场景布局:
基础精灵拆分代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardSprites : MonoBehaviour
{
[HideInInspector]
public Sprite[] sprites;
Texture2D texture2D;
string alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
//string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public void GetMapSprite(string path)
{
texture2D = Resources.Load<Texture2D>(path);
int index = -1;
int row = texture2D.height / 32;
int col = texture2D.width / 32;
sprites = new Sprite[row * col];
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
index++;
Sprite sp = Sprite.Create(texture2D, new Rect(j * 32, (row - 1 - i) * 32, 32, 32), new Vector2(0.5f, 0.5f));
sprites[index] = sp;
int dex = index / col;
string str = alphabet[dex].ToString();
str = str + "" + (index % col).ToString();
sprites[index].name = str;
}
}
}
}
镇子的加载:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
public class TownMap : MonoBehaviour
{
public Transform maps;
//[HideInInspector]
public Sprite[] spritesmap;
[HideInInspector]
public string[,] mapdataRoad;
string[,] mapUpdate;
public TextAsset textAssets;
public string imgPath;
public int row = 0;
public int col = 0;
//设置字典,保存每个卡片的精灵和ID,而每个ID对应唯一卡片坐标
public Dictionary<int, Sprite> dSprite = new Dictionary<int, Sprite>();
void Start()
{
for (int i = 0; i < 272; i++)
{
GameObject item = new GameObject("card");
item.AddComponent<SpriteRenderer>();
item.GetComponent<SpriteRenderer>().drawMode = SpriteDrawMode.Sliced;
item.transform.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
item.transform.SetParent(maps);
item.SetActive(false);
}
}
public void ChangeMap(int posX, int posY)
{
StartCoroutine(WaitMap(posX, posY));
}
IEnumerator WaitMap(int posX, int posY)
{
GetComponent<CardSprites>().GetMapSprite(imgPath);
yield return new WaitUntil(() => GetComponent<CardSprites>().sprites != null);
string str = textAssets.ToString();
InitMapData(str, row, col);
yield return new WaitUntil(() => dSprite != null);
LoadMapData(posX, posY);
}
public void InitMapData(string mapData, int mapRow, int mappCol)
{
dSprite.Clear();
string strAll = mapData;
row = mapRow;
col = mappCol;
spritesmap = transform.GetComponent<CardSprites>().sprites;
string result = strAll.ToString().Replace("\n", "");
string str2 = Regex.Replace(result, @"\s", "");
//拆分地图数据给二维数组
string[] parts = str2.Split(',');
string[,] mapArray = new string[col, row];
mapdataRoad = new string[col, row];//其他脚本引用使用
for (int i = 0; i < col; i++)
{
for (int j = 0; j < row; j++)
{
mapArray[i, j] = parts[i + (col * j)];
}
}
mapUpdate = mapArray;
string[,] map = mapUpdate;
int cardCount = -1;
for (int y = 0; y < row; y++)
{
for (int x = 0; x < col; x++)
{
cardCount++;
string tileType = map[x, y];
//给精灵图片赋值
foreach (Sprite s in spritesmap)
{
if (s.name == tileType)
{
dSprite.Add(cardCount, s);
break;
}
}
mapdataRoad[x, y] = tileType;
}
}
}
public void LoadMapData(int _x, int _y)
{
int childID = -1;
for (int x = _x - 8; x <= _x + 8; x++)
{
for (int y = _y - 8; y < _y + 8; y++)
{
if (x >= 0 && x < col)
{
if (y <= 0 && y > -row)
{
int SpriteID = (x + (col * -y));
childID++;
Transform card = maps.GetChild(childID);
card.position = new Vector2(x, y);
card.GetComponent<SpriteRenderer>().sprite = dSprite[SpriteID];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
else
{
childID++;
Transform card = maps.GetChild(childID);
card.position = new Vector2(x, y);
card.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
}
else
{
childID++;
Transform card = maps.GetChild(childID);
card.position = new Vector2(x, y);
card.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
}
}
}
//移动后更新卡片
public void ShowMapSprite(int posX, int posY)
{
string dir = KeyBoardMove.dir;
for (int i = 0; i < maps.transform.childCount; i++)
{
if (dir == "UP")
{
if (maps.transform.GetChild(i).position.y == posY - 9)
{
Transform trans = maps.GetChild(i);
trans.position = new Vector2(trans.position.x, trans.position.y + 16);
NextBoardCard(trans);
}
}
else if (dir == "DOWN")
{
if (maps.transform.GetChild(i).position.y == posY + 9)
{
Transform trans = maps.GetChild(i);
trans.position = new Vector2(trans.position.x, trans.position.y - 16);
NextBoardCard(trans);
}
}
else if (dir == "LEFT")
{
if (maps.GetChild(i).position.x == posX + 9)
{
Transform trans = maps.transform.GetChild(i);
trans.position = new Vector2(trans.position.x - 17, trans.position.y);
NextBoardCard(trans);
}
}
else if (dir == "RIGHT")
{
if (maps.GetChild(i).position.x == posX - 10)
{
Transform trans = maps.GetChild(i);
trans.position = new Vector2(trans.position.x + 17, trans.position.y);
NextBoardCard(trans);
}
}
}
}
void NextBoardCard(Transform trans)
{
int x = (int)trans.position.x;//映射这个卡片在地图数组中的X坐标
int y = (int)trans.position.y;//映射这个卡片在地图数组中的X坐标
int SpriteID = (x + (col * -y));
if (x >= 0 && x < col)
{
if (y <= 0 && y > -row)
{
Transform card = trans;
card.GetComponent<SpriteRenderer>().sprite = dSprite[SpriteID];
}
else
{
trans.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
}
}
else
{
trans.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
}
}
}
世界地图的加载:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
public class WordMap : MonoBehaviour
{
public Transform maps;
//[HideInInspector]
public Sprite[] spritesmap;
[HideInInspector]
public string[,] mapdataRoad;
string[,] mapUpdate;
public TextAsset[] textAssets;
public string imgPath;
public int row = 0;
public int col = 0;
//设置字典,保存每个卡片的精灵和ID,而每个ID对应唯一卡片坐标
public Dictionary<int, Sprite> dSprite = new Dictionary<int, Sprite>();
//组装世界地图
static List<string> mapTxts = new List<string>();
string map_word;
public void ChangeMap(int posX, int posY)
{
StartCoroutine(WaitMap(posX, posY));
}
IEnumerator WaitMap(int posX, int posY)
{
GetComponent<CardSprites>().GetMapSprite(imgPath);
yield return new WaitUntil(() => GetComponent<CardSprites>().sprites != null);
MakeAllStr();
yield return new WaitUntil(() => map_word != string.Empty);
InitMapData(map_word, row, col);
LoadMapData(posX, posY);
}
private void MakeAllStr()
{
for (int i = 0; i < textAssets.Length; i++)
{
mapTxts.Add(textAssets[i].ToString());
}
for (int i = 0; i < mapTxts.Count; i++)
{
map_word += mapTxts[i];
}
}
public void InitMapData(string mapData, int mapRow, int mappCol)
{
dSprite.Clear();
string strAll = mapData;
row = mapRow;
col = mappCol;
spritesmap = transform.GetComponent<CardSprites>().sprites;
string result = strAll.ToString().Replace("\n", "");
string str2 = Regex.Replace(result, @"\s", "");
//拆分地图数据给二维数组
string[] parts = str2.Split(',');
string[,] mapArray = new string[col, row];
mapdataRoad = new string[col, row];//其他脚本引用使用
for (int i = 0; i < col; i++)
{
for (int j = 0; j < row; j++)
{
mapArray[i, j] = parts[i + (col * j)];
}
}
mapUpdate = mapArray;
string[,] map = mapUpdate;
int cardCount = -1;
for (int y = 0; y < row; y++)
{
for (int x = 0; x < col; x++)
{
cardCount++;
string tileType = map[x, y];
//给精灵图片赋值
foreach (Sprite s in spritesmap)
{
if (s.name == tileType)
{
dSprite.Add(cardCount, s);
break;
}
}
mapdataRoad[x, y] = tileType;
}
}
}
public void LoadMapData(int _x, int _y)
{
int childID = -1;
for (int x = _x - 8; x <= _x + 8; x++)
{
for (int y = _y - 8; y < _y + 8; y++)
{
if (x >= 0 && x < col)
{
if (y <= 0 && y > -row)
{
int SpriteID = (x + (col * -y));
childID++;
Transform card = maps.GetChild(childID);
card.position = new Vector2(x, y);
card.GetComponent<SpriteRenderer>().sprite = dSprite[SpriteID];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
else
{
childID++;
Transform card = maps.GetChild(childID);
card.position = new Vector2(x, y);
//card.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
}
else
{
childID++;
Transform card = maps.GetChild(childID);
card.position = new Vector2(x, y);
//card.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
}
}
}
//移动后更新卡片
public void ShowMapSprite(int posX, int posY)
{
string dir = KeyBoardMove.dir;
for (int i = 0; i < maps.transform.childCount; i++)
{
if (dir == "UP")
{
if (maps.transform.GetChild(i).position.y == posY - 9)
{
Transform trans = maps.GetChild(i);
trans.position = new Vector2(trans.position.x, trans.position.y + 16);
NextBoardCard(trans);
}
}
else if (dir == "DOWN")
{
if (maps.transform.GetChild(i).position.y == posY + 9)
{
Transform trans = maps.GetChild(i);
trans.position = new Vector2(trans.position.x, trans.position.y - 16);
NextBoardCard(trans);
}
}
else if (dir == "LEFT")
{
if (maps.GetChild(i).position.x == posX + 9)
{
Transform trans = maps.transform.GetChild(i);
trans.position = new Vector2(trans.position.x - 17, trans.position.y);
NextBoardCard(trans);
}
}
else if (dir == "RIGHT")
{
if (maps.GetChild(i).position.x == posX - 10)
{
Transform trans = maps.GetChild(i);
trans.position = new Vector2(trans.position.x + 17, trans.position.y);
NextBoardCard(trans);
}
}
}
}
void NextBoardCard(Transform trans)
{
int x = (int)trans.position.x;//映射这个卡片在地图数组中的X坐标
int y = (int)trans.position.y;//映射这个卡片在地图数组中的X坐标
int SpriteID = (x + (col * -y));
if (x >= 0 && x < col)
{
if (y <= 0 && y > -row)
{
Transform card = trans;
card.GetComponent<SpriteRenderer>().sprite = dSprite[SpriteID];
}
else
{
trans.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
}
}
else
{
trans.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
}
}
}
世界地图和镇子的加载貌似是一样的为什么还要分开来,方便为后面的世界地图动态水做准备。
玩家队列布局:
Items的Y轴为什么是0.125f,因为玩家卡片不是和背景重合的,它比背景高这么多,仔细观察会发现NES里面玩家脑袋是要超出卡片0.125个单位的
玩家也使用了精灵拆分工具加载了对应图集,
PSprites代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PSprites : MonoBehaviour
{
SpriteRenderer sRenderer;
Sprite[] sprites;
[HideInInspector]
public float animTimer = 0;//动画帧
public float speed = 3;//这个速度和followObj的速度必须保持一致
public string imgPath;
void Start()
{
sRenderer = GetComponent<SpriteRenderer>();
StartCoroutine(WaitSprites());
}
IEnumerator WaitSprites()
{
GetComponent<CardSprites>().GetMapSprite(imgPath);
yield return new WaitUntil(() => GetComponent<CardSprites>().sprites != null);
// 加载精灵图集
sprites = GetComponent<CardSprites>().sprites;
sRenderer.sprite = sprites[0];
sRenderer.size = Vector2.one;
}
//移动一步的动画
public void PlayerAnim(string dir, int stage)
{
//stage为0,各方向第一张精灵图,非移动状态
//stage为1,各方向第二张精灵图,移动状态
switch (dir)
{
case "UP":
sRenderer.sprite = sprites[12 + stage];
break;
case "LEFT":
sRenderer.sprite = sprites[4 + stage];
break;
case "RIGHT":
sRenderer.sprite = sprites[8 + stage];
break;
case "DOWN":
sRenderer.sprite = sprites[0 + stage];
break;
}
}
}
首位玩家的移动:
using System.Collections;
using UnityEngine;
public class Player : MonoBehaviour
{
public string inMapName = "";//当前地图名字
public int playerID = 1;//玩家在队列的编号,头部玩家编号是1
public TownMap townMap;
public WordMap wordMap;
[HideInInspector]
public int currentStep = 0;
[HideInInspector]
public int player2WSAD = -1;
public string dir = "";//移动方向
public bool isMoving = false;
Vector3 myCoordinate;//探路动画坐标
int STEP = 0;//移动步数
string currentStr = "DOWN";//默认方向
public int tox = 0;
public int toy = 0;
bool isMapChangeing = false;
void Start()
{
transform.localPosition = new Vector2(tox, toy);
myCoordinate = transform.localPosition;
InitGame();
}
void InitGame()
{
for (int i = 0; i < transform.parent.childCount; i++)
{
transform.parent.GetChild(i).localPosition = new Vector2(tox, toy);
if (i>0)
{
transform.parent.GetChild(i).GetComponent<FollowMe>().InitPos(tox, toy);
}
}
inMapName = "laduo";
townMap.ChangeMap(tox, toy);//动态加载地图生成玩家位置
}
void Update()
{
if (myCoordinate == transform.localPosition)
{
if (isMoving)
{
//TODO 到了目标点执行任务
MyNextTask();
}
isMoving = false;
KeyBoardMove.isMoving = false;
dir = KeyBoardMove.dir;
switch (dir)
{
case "UP":
tox = (int)myCoordinate.x;
toy = (int)myCoordinate.y + 1;
break;
case "LEFT":
tox = (int)myCoordinate.x - 1;
toy = (int)myCoordinate.y;
break;
case "RIGHT":
tox = (int)myCoordinate.x + 1;
toy = (int)myCoordinate.y;
break;
case "DOWN":
tox = (int)myCoordinate.x;
toy = (int)myCoordinate.y - 1;
break;
}
if (currentStr != dir)
{
if (KeyBoardMove.keyboard < 2)
{
transform.GetComponent<PSprites>().PlayerAnim(dir,0);//多个障碍可以原地转向
currentStr = dir;
}
}
//不可移动判断
if (KeyBoardMove.isKeyUp)
{
return;
}
//加载移动路径,其他为障碍物
//if (NotMove())
//{
// return;//注销取消障碍
//}
GetComponent<PSprites>().animTimer = 0;
myCoordinate = new Vector2(tox, toy);
STEP++;
if (inMapName == "word")
{
wordMap.ShowMapSprite(tox, toy);
}
else
{
townMap.ShowMapSprite(tox, toy);
}
}
else
{
if (isMapChangeing)
{
return;
}
isMoving = true;
KeyBoardMove.isMoving = true;
transform.localPosition = Vector2.MoveTowards(transform.localPosition, myCoordinate, GetComponent<PSprites>().speed * Time.deltaTime);
//处理玩家移动动画
if (GetComponent<PSprites>().animTimer < GetComponent<PSprites>().speed)
{
GetComponent<PSprites>().animTimer += GetComponent<PSprites>().speed * Time.deltaTime;
if (GetComponent<PSprites>().animTimer < 0.5f)
{
transform.GetComponent<PSprites>().PlayerAnim(dir,1);
}
else
{
transform.GetComponent<PSprites>().PlayerAnim(dir, 0);
}
}
}
}
private void MyNextTask()
{
int boardX = (int)myCoordinate.x;
int boardY = (int)myCoordinate.y;//第四象限
if (inMapName == "laduo")
{
if (boardX <= 0 || boardY >= 0 || boardX >= 25 || boardY <= -16)
{
//切换到世界地图
StopAllCoroutines();
StartCoroutine(MapChange("word", 36, -79));
}
else
{
//TODO
//到屋子里面去
}
}
else if (inMapName == "word")
{
//进入村庄
StopAllCoroutines();
if (boardX == 36 && boardY == -79)
{
StartCoroutine(MapChange("laduo", 14, -15));
}
}
}
IEnumerator MapChange(string name, int x,int y)
{
isMapChangeing = true;
transform.localPosition = new Vector2(x, y);
myCoordinate = new Vector2(x, y);
tox = x;
toy = y;
for (int i = 1; i < transform.parent.childCount; i++)
{
transform.parent.GetChild(i).GetComponent<FollowMe>().InitPos(tox, toy);
transform.parent.GetChild(i).GetComponent<FollowMe>().isMapChangeing = true;
}
inMapName = name;
if (name=="word")
{
wordMap.ChangeMap(x, y);//动态加载地图生成玩家位置
}
else
{
townMap.ChangeMap(x, y);//动态加载地图生成玩家位置
}
yield return new WaitForSeconds(1);
isMapChangeing = false;
for (int i = 1; i < transform.parent.childCount; i++)
{
transform.parent.GetChild(i).GetComponent<FollowMe>().isMapChangeing = false;
}
}
//增加移动路径
private bool NotMove()
{
int _toy = Mathf.Abs(toy);
if (_toy > 254)
{
return true;//防止移出边界
}
if (tox < 0 || toy > 0)
{
return true;//防止移出边界
}
if (tox > 254 || toy < -254)
{
return true;//防止移出边界
}
if (inMapName == "word")
{
Debug.Log(tox + "~~~" + _toy);
if (wordMap.mapdataRoad[tox, _toy] == "01") return false;
if (wordMap.mapdataRoad[tox, _toy] == "08") return false;
if (wordMap.mapdataRoad[tox, _toy] == "09") return false;
if (wordMap.mapdataRoad[tox, _toy] == "12") return false;
if (wordMap.mapdataRoad[tox, _toy] == "13") return false;
if (wordMap.mapdataRoad[tox, _toy] == "27") return false;
if (wordMap.mapdataRoad[tox, _toy] == "28") return false;
if (wordMap.mapdataRoad[tox, _toy] == "39") return false;
if (wordMap.mapdataRoad[tox, _toy] == "44") return false;
if (wordMap.mapdataRoad[tox, _toy] == "45") return false;
if (wordMap.mapdataRoad[tox, _toy] == "50") return false;
if (wordMap.mapdataRoad[tox, _toy] == "51") return false;
if (wordMap.mapdataRoad[tox, _toy] == "52") return false;
if (wordMap.mapdataRoad[tox, _toy] == "53") return false;
if (wordMap.mapdataRoad[tox, _toy] == "60") return false;
if (wordMap.mapdataRoad[tox, _toy] == "65") return false;
if (wordMap.mapdataRoad[tox, _toy] == "66") return false;
if (wordMap.mapdataRoad[tox, _toy] == "68") return false;
if (wordMap.mapdataRoad[tox, _toy] == "69") return false;
if (wordMap.mapdataRoad[tox, _toy] == "70") return false;
if (wordMap.mapdataRoad[tox, _toy] == "71") return false;
if (wordMap.mapdataRoad[tox, _toy] == "b7") return false;
if (wordMap.mapdataRoad[tox, _toy] == "b8") return false;
if (wordMap.mapdataRoad[tox, _toy] == "b9") return false;
if (wordMap.mapdataRoad[tox, _toy] == "c0") return false;
if (wordMap.mapdataRoad[tox, _toy] == "c1") return false;
if (wordMap.mapdataRoad[tox, _toy] == "c2") return false;
if (wordMap.mapdataRoad[tox, _toy] == "c3") return false;
if (wordMap.mapdataRoad[tox, _toy] == "c4") return false;
if (wordMap.mapdataRoad[tox, _toy] == "c7") return false;
if (wordMap.mapdataRoad[tox, _toy] == "d2") return false;
if (wordMap.mapdataRoad[tox, _toy] == "d5") return false;
if (wordMap.mapdataRoad[tox, _toy] == "e0") return false;
if (wordMap.mapdataRoad[tox, _toy] == "e1") return false;
if (wordMap.mapdataRoad[tox, _toy] == "e2") return false;
if (wordMap.mapdataRoad[tox, _toy] == "e3") return false;
if (wordMap.mapdataRoad[tox, _toy] == "e9") return false;
if (wordMap.mapdataRoad[tox, _toy] == "f0") return false;
}
else
{
if (inMapName == "laduo")
{
if (tox < 26 && _toy < 17)
{
if (townMap.mapdataRoad[tox, _toy] == "00") return false;
if (townMap.mapdataRoad[tox, _toy] == "01") return false;
if (townMap.mapdataRoad[tox, _toy] == "15") return false;
if (townMap.mapdataRoad[tox, _toy] == "30") return false;
if (townMap.mapdataRoad[tox, _toy] == "32") return false;
if (townMap.mapdataRoad[tox, _toy] == "33") return false;
if (townMap.mapdataRoad[tox, _toy] == "36") return false;
if (townMap.mapdataRoad[tox, _toy] == "41") return false;
if (townMap.mapdataRoad[tox, _toy] == "43") return false;
if (townMap.mapdataRoad[tox, _toy] == "53") return false;
if (townMap.mapdataRoad[tox, _toy] == "62") return false;
if (townMap.mapdataRoad[tox, _toy] == "75") return false;
if (townMap.mapdataRoad[tox, _toy] == "82") return false;
if (townMap.mapdataRoad[tox, _toy] == "83") return false;
}
else
{
return true;
}
}
}
return true;
}
}
队列跟随部分:
Tank01跟随的是Player3,Player3跟随的是Player2,Player2跟随的是Player1,
它们都在PSprites填了对应精灵图集路径ImgPath。
KeyBoardMove代码,随便一个按键方向工具:
using UnityEngine;
public class KeyBoardMove : MonoBehaviour
{
public static string dir = "";//移动方向
public static bool isKeyUp = true;
public static bool isMoving = false;
public static int keyboard = 0;
void Update()
{
if (Input.GetKeyUp(KeyCode.S) || Input.GetKeyUp(KeyCode.A) || Input.GetKeyUp(KeyCode.D) || Input.GetKeyUp(KeyCode.W))
{
keyboard = 0;
isKeyUp = true;
}
if (Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.S)
|| Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.A)
|| Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.D)
|| Input.GetKey(KeyCode.S) && Input.GetKey(KeyCode.A)
|| Input.GetKey(KeyCode.S) && Input.GetKey(KeyCode.D)
|| Input.GetKey(KeyCode.A) && Input.GetKey(KeyCode.D))
{
keyboard = 2;//同时有两个按键则返回
isKeyUp = true;
}
if (!isMoving)
{
if (Input.GetKey(KeyCode.W))
{
isKeyUp = false;
dir = "UP";
}
else if (Input.GetKey(KeyCode.S))
{
isKeyUp = false;
dir = "DOWN";
}
else if (Input.GetKey(KeyCode.A))
{
isKeyUp = false;
dir = "LEFT";
}
else if (Input.GetKey(KeyCode.D))
{
isKeyUp = false;
dir = "RIGHT";
}
}
//多个方向同时按键则急停
switch (dir)
{
case "UP":
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D) || Input.GetKeyUp(KeyCode.W))
{
isKeyUp = true;
}
break;
case "LEFT":
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.D) || Input.GetKeyUp(KeyCode.A))
{
isKeyUp = true;
}
break;
case "RIGHT":
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.W) || Input.GetKeyUp(KeyCode.D))
{
isKeyUp = true;
}
break;
case "DOWN":
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D) || Input.GetKeyUp(KeyCode.S))
{
isKeyUp = true;
}
break;
}
}
}
人物跟随代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowMe : MonoBehaviour
{
public Transform followObj;//跟随目标
Vector3 movePoint;//下一个移动点
Vector3 prevPos;//玩家移动插值帧
Vector3 prevSelfPos;//自身移动插值帧
string dir = "DOWN";//默认移动方向
string lateDir = "";//移动后朝向
[HideInInspector]
public bool startFollow = false;//第一次如果是重合防止移动
[HideInInspector]
public bool isImgLoading = false;
[HideInInspector]
public bool isMapChangeing = false;
public void InitPos(int x,int y)
{
transform.localPosition = new Vector2(x, y);
prevPos = new Vector2(x, y);
movePoint = new Vector2(x, y);
startFollow = false;
isImgLoading = true;
}
private void Update()
{
if (isImgLoading && !isMapChangeing)
{
Follow();
}
}
void Follow()
{
if (Vector3.Distance(transform.localPosition, followObj.localPosition) >= 1)
{
startFollow = true;
}
if (followObj && startFollow)
{
transform.localPosition = Vector3.MoveTowards(transform.localPosition, movePoint, GetComponent<PSprites>().speed * Time.deltaTime);
float moveX = followObj.localPosition.x - prevPos.x;
float moveY = followObj.localPosition.y - prevPos.y;
float mX = transform.localPosition.x - prevSelfPos.x;
float mY = transform.localPosition.y - prevSelfPos.y;
if (mX > 0)
{
dir = "RIGHT";
}
if (mX < 0)
{
dir = "LEFT";
}
if (mY > 0)
{
dir = "UP";
}
if (mY < 0)
{
dir = "DOWN";
}
//跟随原理,unity玩家队列跟随
if (Vector3.Distance(movePoint, transform.localPosition) == 0)
{
PlayerLateAnim(0);
if (Vector3.Distance(transform.localPosition, followObj.localPosition) != 1)
{
GetComponent<PSprites>().animTimer = 0;
movePoint = new Vector3(Mathf.RoundToInt(prevPos.x), Mathf.RoundToInt(prevPos.y), 0);
}
if (Vector3.Distance(transform.localPosition, followObj.localPosition) > 1)
{
if (transform.localPosition.x == followObj.localPosition.x && moveX == 0)
{
if (transform.localPosition.y < followObj.localPosition.y)
{
transform.localPosition = new Vector3(followObj.localPosition.x, followObj.localPosition.y - 1, 0);
}
else if (transform.position.y > followObj.position.y)
{
transform.localPosition = new Vector3(followObj.localPosition.x, followObj.localPosition.y + 1, 0);
}
}
if (transform.localPosition.y == followObj.localPosition.y && moveY == 0)
{
if (transform.localPosition.x < followObj.localPosition.x)
{
transform.localPosition = new Vector3(followObj.localPosition.x - 1, followObj.localPosition.y, 0);
}
else if (transform.localPosition.x > followObj.localPosition.x)
{
transform.localPosition = new Vector3(followObj.localPosition.x + 1, followObj.localPosition.y, 0);
}
}
}
prevPos = followObj.localPosition;
prevSelfPos = transform.localPosition;
}
else
{
//处理玩家移动动画
if (GetComponent<PSprites>().animTimer < GetComponent<PSprites>().speed)
{
GetComponent<PSprites>().animTimer += GetComponent<PSprites>().speed * Time.deltaTime;
if (GetComponent<PSprites>().animTimer < 0.5f)
{
transform.GetComponent<PSprites>().PlayerAnim(dir, 1);
}
else
{
transform.GetComponent<PSprites>().PlayerAnim(dir, 0);
}
}
}
}
}
//移动完毕后需要朝向跟随的玩家
public void PlayerLateAnim(int stage)
{
int fX = Mathf.RoundToInt(followObj.localPosition.x);
int fY = Mathf.RoundToInt(followObj.localPosition.y);
int selfX = Mathf.RoundToInt(transform.localPosition.x);
int selfY = Mathf.RoundToInt(transform.localPosition.y);
if (selfX == fX)
{
if (selfY < fY)
{
lateDir = "UP";
}
else if (selfY > fY)
{
lateDir = "DOWN";
}
}
else if (selfY == fY)
{
if (selfX < fX)
{
lateDir = "RIGHT";
}
else if (selfX > fX)
{
lateDir = "LEFT";
}
}
dir = lateDir;
transform.GetComponent<PSprites>().PlayerAnim(dir, stage);
}
}
PlayerLateAnim()方法//移动完毕后需要朝向跟随的玩家
每移动完一步之后的朝向需要更改。
地图数据:
拉多地图的二维数组:
不使用JSON数组,太长了拥挤,直接用字符串:
精灵编号查看工具图片:
精灵编号查看工具GetImgIndex代码:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class GetImgIndex : MonoBehaviour
{
public SpriteRenderer sr;
public string TexName = "";
Texture2D tex;
int texSize = 32;//图片的一个格子分辨率,有的默认是16需要注意
int nullRow = 0;//去掉无用行
int nullCol = 0;//去掉无用列
int downCount = 0;//图片底部多出来的空白区域分辨率
int rightCound = 0;//图片右部多出来的空白区域分辨率
//[HideInInspector]
public Sprite[] _sprites;
void Start()
{
if (TexName == "word")
{
nullRow = 0;
nullCol = 0;
downCount = 0;
texSize = 32;
tex = Resources.Load<Texture2D>("sprite/mm1_world_mt");
}
else if (TexName == "town")
{
nullRow = 0;
nullCol = 0;
downCount = 0;
texSize = 32;
tex = Resources.Load<Texture2D>("sprite/mm1_locate_general");
}
int index = -1;
int row = tex.height / texSize - nullRow;
int col = tex.width / texSize - nullCol;
_sprites = new Sprite[row * col];
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
index++;
Sprite sp = Sprite.Create(tex, new Rect(j * texSize + rightCound, (row - 1 - i) * texSize + downCount, texSize, texSize), new Vector2(0.5f, 0.5f));
_sprites[index] = sp;
_sprites[index].name = index.ToString();
}
}
GetImgID();
}
/// <summary>
/// 获取精灵下标
/// </summary>
void GetImgID()
{
int index = -1;
int row = tex.height / texSize - nullRow;
int col = tex.width / texSize - nullCol;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
index++;
GameObject go = Instantiate(sr.gameObject);
go.transform.position = new Vector2(j, row - 1 - i);
go.transform.GetComponent<SpriteRenderer>().sprite = _sprites[index];
go.transform.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
go.SetActive(true);
go.transform.GetChild(0).GetComponent<TextMeshPro>().text = index.ToString();
go.name = "card" + index;
}
}
}
/// <summary>
/// 获取图片的一部分
/// </summary>
/// <param name="tex">图片</param>
/// <param name="size">要将图片分为n*n的图块的集合,如3*3</param>
/// <param name="id">目标图块的Id(左下角是(0,0),右上角是(n-1,n-1))</param>
/// <returns></returns>
//private Sprite GetSprite(Texture2D tex, Vector2Int size, Vector2Int id)
//{
// var x = (float)tex.width / size.x;
// var y = (float)tex.height / size.y;
// var sprite = Sprite.Create(tex, new Rect(id.x * x, id.y * y, x, y), new Vector2(0.5f, 0.5f));
// return sprite;
//}
}
下面是世界地图的编辑工具,世界地图是5*5总共25张:
世界地图编辑的场景布局:
WordCard工具代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
public class WordCard : MonoBehaviour
{
public Transform maps;
[HideInInspector]
public Sprite[] spritesmap;
[HideInInspector]
public string[,] mapdataRoad;
string[,] mapUpdate;
public string imgPath;
public int row = 0;
public int col = 0;
public TextAsset[] textAssets;
//设置字典,保存每个卡片的精灵和ID,而每个ID对应唯一卡片坐标
public Dictionary<int, Sprite> dSprite = new Dictionary<int, Sprite>();
//------------地图滚动---------------
[HideInInspector]
public float animTimer = 0;//动画帧
//----------UI处理------------
[HideInInspector]
public bool UIOpen = false;
bool isLoad = false;
void Start()
{
for (int i = 0; i < row * col; i++)
{
GameObject item = new GameObject("card");
item.AddComponent<SpriteRenderer>();
item.GetComponent<SpriteRenderer>().drawMode = SpriteDrawMode.Sliced;
item.transform.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
item.transform.SetParent(maps);
item.SetActive(false);
}
ChangeMap();
}
public void ChangeMap()
{
StartCoroutine(WaitMap());
}
IEnumerator WaitMap()
{
GetComponent<CardSprites>().GetMapSprite(imgPath);
yield return new WaitUntil(() => GetComponent<CardSprites>().sprites != null);
string str = textAssets[0].ToString();
InitMapData(str, row, col);
LoadMapData();
}
public void InitMapData(string mapData, int mapRow, int mappCol)
{
dSprite.Clear();
string strAll = mapData;
row = mapRow;
col = mappCol;
spritesmap = transform.GetComponent<CardSprites>().sprites;
string result = strAll.ToString().Replace("\n", "");
string str2 = Regex.Replace(result, @"\s", "");
//拆分地图数据给二维数组
string[] parts = str2.Split(',');
string[,] mapArray = new string[col, row];
mapdataRoad = new string[col, row];//其他脚本引用使用
for (int i = 0; i < col; i++)
{
for (int j = 0; j < row; j++)
{
mapArray[i, j] = parts[i + (col * j)];
}
}
mapUpdate = mapArray;
string[,] map = mapUpdate;
int cardCount = -1;
for (int y = 0; y < row; y++)
{
for (int x = 0; x < col; x++)
{
cardCount++;
string tileType = map[x, y];
//给精灵图片赋值
foreach (Sprite s in spritesmap)
{
if (s.name == tileType)
{
dSprite.Add(cardCount, s);
break;
}
}
mapdataRoad[x, y] = tileType;
}
}
}
public void LoadMapData()
{
int childID = -1;
for (int x = 0; x < col; x++)
{
for (int y = 0; y < row; y++)
{
int SpriteID = (x + (col * y));
childID++;
Transform card = maps.GetChild(childID);
card.position = new Vector2(x, -y);
card.GetComponent<SpriteRenderer>().sprite = dSprite[SpriteID];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
}
isLoad = true;
}
private void Update()
{
if (isLoad)
{
if (Input.GetKeyDown(KeyCode.K))
{
ChangeMap();
}
}
}
}
如果你修改了TXT地图文本,按下K建刷新一下地图,这样就不用频繁运行窗口了。
如何使用:
每个块有对应编号:
世界地图总共是255行和255列,分成五个块就是每个块51行和51列
我这里测试了加载word_01:
运行后如下:
在正式游戏窗口里面我加载了word_01和word_0两个组合体:
word_01在word_00上面,它两加起来有102行,还是只有51列。
在WordMap代码里的对应方法:
定义一个全局字符串
string map_word;
拼装两个文本:
等待字符串不为空即可加载地图了。
玩家的出生情况:
他出生在了拉多的12,-8坐标的位置
切换了地图:
正式的运行效果:
运行效果
玩家现在是超人,需要开启障碍路径:
根据自己文本的卡片ID添加可移动路径。
我这里添加的是可移动路径,不是添加的障碍,因为障碍的ID太多了比较麻烦:
路径一添加,超人立马变凡人。
源码:磨刀不误砍柴工