前言
在梦日记及其相关派生中,总会用到循环的地图,这些循环的地图都有一个能让玩家能一直走下去不会停止的功能,今天,我们就要实现它。
方法
首先我们就要实现移动的功能,再在这个移动功能的基础上进行循环地图的效果。
要循环地图,有以下几点:
第一,你得准备两张地图,一张是物理上的地图,一张是抽象上的地图。
物理意义上的地图准备好了,抽象的地图在你的键盘揉搓一下,也是可以做好的。主要设置的是地图长和宽及内容,要循环就要获取地图的左右边界x值与上下边界y值,求格子边长要用到地图宽度。
设置地图的内容,就只用空格填充就行。
为防止你被莫名其妙的来到了地图的另一边,并且你毫无意义的像橡皮掉下去一样找不到你自己了,就还要有一点改动——给地图周围添加其他与之一样的地图(等你添加完了地图,玩梦日记及其派生的玩家想:这就是东还是西北方?)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class map : MonoBehaviour
{
public static int x = 10;
public static int y = 10;
public int a = 100;
public int b = 100;
public GameObject o;
public static int minX = -50;
public static int minY = -50;
public static int maxX = 50;
public static int maxY = 50;
public static int heightX {
get
{
return Mathf.Abs(maxX - minX);
}
}
public static int widthY
{
get
{
return Mathf.Abs(maxY - minY);
}
}
public static char[,] wmap;
void Awake()
{
wmap = new char[x, y];
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
wmap[i, j] = ' ';
}
}
}
private void Start()
{
for (int i = 0; i < 9; i++)
{
if (4 == i)
{
continue;
}
Instantiate(o, new Vector3(transform.position.x + (i % 3 - 1) * a, 0, transform.position.z + (i / 3 - 1) * b), transform.rotation);
}
}
void Update()
{
}
}
倒数第一,你得准备一个玩家,还有一个抽象化的玩家;
抽象化的玩家,我们把它压得像烧饼一样由又香又扁,就是很简单的一个指针在移动,拆分一下也就是x和y两个轴移动,通过这两个轴,可以在抽象出来的地图上引出无数条明亮的思路。
其中,在抽象化的玩家上,唯一难的是你得将地图上的x轴转为游戏上的x轴,并把地图上的y轴转为游戏上的z轴。解决问题时,我们要先知道已知的信息。
从该问题可知,抽象出来的地图的原点就是在最左上角。然后通过这些抽象地图上的数据,我们可以得出在地图上物体在游戏中的实际位置,就是。
在这个式子中,及求出了一个格子的长与宽,两个分别表示一个格子长与宽的一半,由于与表示为在长方形里的数对的话,就恰好在长方形中间。因此,这两个分数就成了关键的一点。
之后通过x轴和y轴分别乘以及,就得出了地图上的物体在游戏中的实际位置。(在刚才这个例子中,如果你将地块拆分成了像屎一样的形状,那么这个式子不成立,可能连你自己都要笑死)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class move : MonoBehaviour
{
enum wasd
{
n,
w,
a,
s,
d
};
private bool isEnd = true;
public float moveTime = 0.01f;
public float waitTime = 0.2f;
public int x = 5;
public int y = 5;
private float high = 5;
wasd getwasd()
{
if (Input.GetKey("w") && ' ' == map.wmap[x, y - 1 >= 0 ? y - 1 : map.y - 1])
{
map.wmap[x, y--] = ' ';
if (y < 0)
{
transform.position = new Vector3(transform.position.x, high, map.minY - map.widthY / map.y / 2.0f);
y = map.y - 1;
}
map.wmap[x, y] = 'I';
return wasd.w;
}
else if (Input.GetKey("a") && ' ' == map.wmap[x - 1 >= 0 ? x - 1 : map.x - 1, y])
{
map.wmap[x--, y] = ' ';
if (x < 0)
{
transform.position = new Vector3(map.maxX + map.heightX / map.x / 2.0f, high, transform.position.z);
x = map.x - 1;
}
map.wmap[x, y] = 'I';
return wasd.a;
}
else if (Input.GetKey("s") && ' ' == map.wmap[x, (y + 1) % map.y])
{
map.wmap[x, y++] = ' ';
if (y >= map.y)
{
transform.position = new Vector3(transform.position.x, high, map.maxY + map.widthY / map.y / 2.0f);
}
y %= map.y;
map.wmap[x, y] = 'I';
return wasd.s;
}
else if (Input.GetKey("d") && ' ' == map.wmap[(x + 1) % map.x, y])
{
map.wmap[x++, y] = ' ';
if (x >= map.x)
{
transform.position = new Vector3(map.minX - map.heightX / map.x / 2.0f, high, transform.position.z);
}
x %= map.x;
map.wmap[x, y] = 'I';
return wasd.d;
}
else
{
return wasd.n;
}
}
IEnumerator pmove()
{
//初始
isEnd = false;
wasd i = getwasd();
for (int j = 0; j < 100; j++)
{
switch (i)//移动
{
case wasd.w:
transform.position += new Vector3(0, 0, map.widthY / map.y / 100.0f);
yield return new WaitForSeconds(moveTime);
break;
case wasd.a:
transform.position += new Vector3(-map.heightX / map.x / 100.0f, 0, 0);
yield return new WaitForSeconds(moveTime);
break;
case wasd.s:
transform.position += new Vector3(0, 0, -map.widthY / map.y / 100.0f);
yield return new WaitForSeconds(moveTime);
break;
case wasd.d:
transform.position += new Vector3(map.heightX / map.x / 100.0f, 0, 0);
yield return new WaitForSeconds(moveTime);//移动间隔时间
break;
default://wasd.n时无
goto nowait;
}
}
yield return new WaitForSeconds(waitTime);//移动等待时间
nowait:
isEnd = true;
yield return null;
}
void Start()
{
//根据地图z轴进行高度计算
high = transform.position.y;
//根据地图xy轴进行位置计算
transform.position = new Vector3(map.minX + map.heightX / map.x * (0.5f + x), high, map.maxY - map.widthY / map.y * (0.5f + y));
map.wmap[x, y] = 'I';
StartCoroutine(pmove());
}
void Update()
{
if (isEnd)
{
StartCoroutine(pmove());
}
}
}
做到这了,你可以添加一些NPC,那么,实质的我们不讲,简单到没什么讲的,主要就是讲抽象方面的。(抽象)。
抽象方面,NPC这个任务主要就是随便的乱动,但移动的逻辑还是跟玩家一样。因此,我们就复制玩家移动的逻辑,之后将用按键判断的条件改为用随机值判断的条件,就可以。
光是完成这一点还不够,还要给NPC在周围复制8个,让移动的位置在其它地图上与物体刚才地图上的相对位置一样。我们想想,想到可以给NPC移动地图的长或宽步。这样NPC就会觉得:这回是鬼打墙吗,有点意思。
移动之后,我们就要让这些克隆体的父级设为刚才复制这些克隆体的对象,那如何在全局位置不变的情况下能够在克隆的同时,设置克隆物体的父级呢?答案就是只需要一个克隆物体函数Instantiate的重载版本:
这个重载版本,重点就在要重看的那个bool类参数上,如果该参为真,那么物体的全局位置不变;反之,物体的全局位置就变,相当于。
之后,就可以写了。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class npcMove : MonoBehaviour
{
enum wasd
{
w,
a,
s,
d,
n
};
private bool isEnd = true;
public float moveTime = 0.01f;
public float waitTime = 0.2f;
public int x = 5;
public int y = 5;
private float high = 5;
public GameObject clone;
wasd getwasd()
{
wasd w = (wasd)Random.Range(0, 4);
if (wasd.w == w && ' ' == map.wmap[x, y - 1 >= 0 ? y - 1 : map.y - 1])
{
map.wmap[x, y--] = ' ';
if (y < 0)
{
transform.position = new Vector3(transform.position.x, high, map.minY - map.widthY / map.y / 2.0f);
y = map.y - 1;
}
map.wmap[x, y] = 'N';
return wasd.w;
}
else if (wasd.a == w && ' ' == map.wmap[x - 1 >= 0 ? x - 1 : map.x - 1, y])
{
map.wmap[x--, y] = ' ';
if (x < 0)
{
transform.position = new Vector3(map.maxX + map.heightX / map.x / 2.0f, high, transform.position.z);
x = map.x - 1;
}
map.wmap[x, y] = 'N';
return wasd.a;
}
else if (wasd.s == w && ' ' == map.wmap[x, (y + 1) % map.y])
{
map.wmap[x, y++] = ' ';
if (y >= map.y)
{
transform.position = new Vector3(transform.position.x, high, map.maxY + map.widthY / map.y / 2.0f);
}
y %= map.y;
map.wmap[x, y] = 'N';
return wasd.s;
}
else if (wasd.d == w && ' ' == map.wmap[(x + 1) % map.x, y])
{
map.wmap[x++, y] = ' ';
if (x >= map.x)
{
transform.position = new Vector3(map.minX - map.heightX / map.x / 2.0f, high, transform.position.z);
}
x %= map.x;
map.wmap[x, y] = 'N';
return wasd.d;
}
else
{
return wasd.n;
}
}
IEnumerator pmove()
{
//初始
isEnd = false;
wasd i = getwasd();
for (int j = 0; j < 100; j++)
{
switch (i)//移动
{
case wasd.w:
transform.rotation = Quaternion.Euler(-90, 0, 180);
transform.position += new Vector3(0, 0, map.widthY / map.y / 100.0f);
yield return new WaitForSeconds(moveTime);
break;
case wasd.a:
transform.rotation = Quaternion.Euler(-90, 0, 90);
transform.position += new Vector3(-map.heightX / map.x / 100.0f, 0, 0);
yield return new WaitForSeconds(moveTime);
break;
case wasd.s:
transform.rotation = Quaternion.Euler(-90, 0, 0);
transform.position += new Vector3(0, 0, -map.widthY / map.y / 100.0f);
yield return new WaitForSeconds(moveTime);
break;
case wasd.d:
transform.rotation = Quaternion.Euler(-90, 0, -90);
transform.position += new Vector3(map.heightX / map.x / 100.0f, 0, 0);
yield return new WaitForSeconds(moveTime);//移动间隔时间
break;
default://wasd.n时无
goto nowait;
}
}
yield return new WaitForSeconds(waitTime);//移动等待时间
nowait:
isEnd = true;
yield return null;
}
void Start()
{
//根据地图z轴进行高度计算
high = transform.position.y;
//根据地图xy轴进行位置计算
transform.position = new Vector3(map.minX + map.heightX / map.x * (0.5f + x), high, map.maxY - map.widthY / map.y * (0.5f + y));
map.wmap[x, y] = 'N';
Vector3[] clones = { new Vector3(-map.heightX, 0, map.widthY), new Vector3(0, 0, map.widthY), new Vector3(map.heightX, 0, map.widthY), new Vector3(-map.heightX, 0, 0), new Vector3(map.heightX, 0, 0), new Vector3(-map.heightX, 0, -map.widthY), new Vector3(0, 0, -map.widthY), new Vector3(map.heightX, 0, -map.widthY) };
foreach (Vector3 v in clones){
clone.transform.localPosition = v + transform.position;
clone.transform.rotation = transform.rotation;
Instantiate(clone, transform, true);
}
}
void Update()
{
if (isEnd)
{
StartCoroutine(pmove());
}
}
}
最后,你也可以添加一些墙,墙的逻辑比NPC更加简单,不用关于移动的功能了,但是,关于克隆的,也是不可少的。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class wall : MonoBehaviour
{
public int x = 0;
public int y = 0;
public bool ChangeTransform = true;
public GameObject wallPrefab;
void Start()
{
if (ChangeTransform)
{
//根据地图xyz轴进行transform计算
transform.position = new Vector3(map.minX + map.heightX / map.x * (0.5f + x), transform.position.y, map.maxY - map.widthY / map.y * (0.5f + y));
}
map.wmap[x, y] = 'X';
if (null != wallPrefab)
{
Vector3[] clones = { new Vector3(-map.heightX, 0, map.widthY), new Vector3(0, 0, map.widthY), new Vector3(map.heightX, 0, map.widthY), new Vector3(-map.heightX, 0, 0), new Vector3(map.heightX, 0, 0), new Vector3(-map.heightX, 0, -map.widthY), new Vector3(0, 0, -map.widthY), new Vector3(map.heightX, 0, -map.widthY) };
foreach (Vector3 v in clones)
{
wallPrefab.transform.localPosition = v + transform.position;
wallPrefab.transform.rotation = transform.rotation;
Instantiate(wallPrefab, transform, true);
}
}
}
void Update()
{
}
}
而且,你也可以生成墙。只需用一个Vector2数组即可。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class summonWall : MonoBehaviour
{
public Vector2[] wallXY;
public Vector2 xyOffSet = Vector2.zero;//偏移量,可以创建出比1*1还大的墙,也可以对生成出来的墙进行整体移动
public bool ChangeTransform = true;
public wall wallPreFab;
void Start() {
foreach (Vector2 xy in wallXY)
{
if (null == wallPreFab)
{
GameObject emptyWall = new GameObject("EmptyWall");
wallPreFab = emptyWall.AddComponent<wall>();
}
wallPreFab.x = (int)xy.x + (int)xyOffSet.x;
wallPreFab.y = (int)xy.y + (int)xyOffSet.y;
wallPreFab.ChangeTransform = ChangeTransform;
Instantiate(wallPreFab);
}
}
void Update()
{
}
}
之后,你可以测试一下你的代码里还有什么“打不死的小强”——bug。或是你最近你想去试一下这个游戏怎样,也可以先试着玩一玩,再调整调整。这是我最终调整的游戏。
后言
在梦日记及其派生中,可以通过门来传送,下篇博客就把这个功能给实现好。