直接上地图加载和如何滚动的代码:
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Unity.VisualScripting;
using UnityEngine;
using ZzjbSrc;
public class TownMap : MonoBehaviour
{
public float moveSpeed = 3;
public Player[] player;
Texture2D texMap;
Sprite[] spritesmap;
string alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
public static string[,] mapdataRoad;
string[,] mapUpdate;
int row = 0;
int col = 0;
//设置字典,保存每个卡片的精灵和ID,而每个ID对应唯一卡片坐标
public Dictionary<int, Sprite> dSprite = new Dictionary<int, Sprite>();
//------------地图滚动---------------
int STEP = 0;//移动步数
public string mapName = "laduo";
[HideInInspector]
public string dir = "";//移动方向
string currentStr = "DOWN";//默认方向
Vector2 myCoordinate;//探路坐标
[HideInInspector]
public bool isKeyUp = true;
[HideInInspector]
public bool isMoving = false;
int keyboard = 0;
int tox = 12;
int toy = -8;
int mapMoveX = 0;
int mapMoveY = 0;
Vector2 centerPos;
public Vector2 mapCenterPos;
Vector2 mapNextPos;
//----------UI处理------------
[HideInInspector]
public bool UIOpen = false;
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(transform);
item.SetActive(false);
}
tox = 8;
toy = -7;
centerPos = new Vector2(8,-7);
myCoordinate = centerPos;
//StartCoroutine(WaitMapdata());
InitMapData("laduo", new MapConfig().laduo, 17, 26);
}
public void InitMapData(string _mapName, string mapData, int mapRow, int mappCol)
{
mapName = _mapName;
string strAll = mapData;
row = mapRow;
col = mappCol;
texMap = Resources.Load<Texture2D>("sprite/mm1_locate_general");
//拆分图片获取编号,从左上角编号零开始根据像素拆分
GetMapSprite();
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;
dSprite.Clear();
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;
}
}
LoadMapData();
}
public void LoadMapData()
{
//player.GetComponent<PlayerController>().mapName = mapName;
int _x = (int)mapCenterPos.x;
int _y = (int)mapCenterPos.y;
int xCount = 8 - _x;
int yCount = -7 - _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 = transform.GetChild(childID);
card.position = new Vector2(x + xCount, y + yCount);
card.GetComponent<SpriteRenderer>().sprite = dSprite[SpriteID];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
else
{
childID++;
Transform card = transform.GetChild(childID);
card.position = new Vector2(x + xCount, y + yCount);
card.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
}
else
{
childID++;
Transform card = transform.GetChild(childID);
card.position = new Vector2(x + xCount, y + yCount);
card.GetComponent<SpriteRenderer>().sprite = spritesmap[0];
card.GetComponent<SpriteRenderer>().size = new Vector2(1, 1);
card.gameObject.SetActive(true);
}
}
}
}
void GetMapSprite()
{
int index = -1;
int row = texMap.height / 32;
int col = texMap.width / 32;
spritesmap = new Sprite[row * col];
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
index++;
Sprite sp = Sprite.Create(texMap, new Rect(j * 32, (row - 1 - i) * 32, 32, 32), new Vector2(0.5f, 0.5f));
spritesmap[index] = sp;
int dex = index / 10;
string str = alphabet[dex].ToString();
str = str + "" + (index % 10).ToString();
spritesmap[index].name = str;
}
}
}
//移动后更新卡片
public void ShowMapSprite(int posX, int posY)
{
if (mapName == "word")
{
return;
}
for (int i = 0; i < transform.childCount; i++)
{
if (dir == "UP")
{
if (transform.GetChild(i).position.y == posY - 9)
{
Transform trans = transform.GetChild(i);
trans.position = new Vector2(trans.position.x, trans.position.y + 16);
NextBoardCard(trans);
}
}
else if (dir == "DOWN")
{
if (transform.GetChild(i).position.y == posY + 9)
{
Transform trans = transform.GetChild(i);
trans.position = new Vector2(trans.position.x, trans.position.y - 16);
NextBoardCard(trans);
}
}
else if (dir == "LEFT")
{
if (transform.GetChild(i).position.x == posX + 9)
{
Transform trans = transform.GetChild(i);
trans.position = new Vector2(trans.position.x - 17, trans.position.y);
NextBoardCard(trans);
}
}
else if (dir == "RIGHT")
{
if (transform.GetChild(i).position.x == posX - 10)
{
Transform trans = transform.GetChild(i);
trans.position = new Vector2(trans.position.x + 17, trans.position.y);
NextBoardCard(trans);
}
}
}
}
void NextBoardCard(Transform trans)
{
int xCount = 8 - (int)mapCenterPos.x;//中心点X轴偏移总单位
int yCount = -7 - (int)mapCenterPos.y;//中心点Y轴偏移总单位
int x = (int)trans.position.x - xCount;//映射这个卡片在地图数组中的X坐标
int y = (int)trans.position.y - yCount;//映射这个卡片在地图数组中的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];
}
}
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))
{
isKeyUp = true;
keyboard = 2;//同时有两个按键则返回
}
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";
}
for (int i = 0; i < player.Length; i++)
{
player[i].dir = dir;
}
}
//多个方向同时按键则急停
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;
}
if (myCoordinate == centerPos)
{
if (isMoving)
{
int moveX = (int)transform.position.x;
int moveY = (int)transform.position.y;
transform.position = Vector2.zero;
for (int i = 0; i < transform.childCount; i++)
{
transform.GetChild(i).position = new Vector2(transform.GetChild(i).position.x + moveX, transform.GetChild(i).position.y + moveY);
}
}
isMoving = false;
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 (keyboard < 2)
{
Debug.Log("方向不同");
currentStr = dir;
}
}
//不可移动判断
if (isKeyUp)
{
return;
}
if (tox < 0 || toy > 0) return;//防止移出边界
if (tox > 254 || toy < -254) return;//防止移出边界
//加载移动路径,其他为障碍物
//if (NotMove())
{
//return;
}
for (int i = 0; i < player.Length; i++)
{
player[i].SetNextPos();//人物开始移动
}
STEP++;
ShowMapSprite(tox, toy);
mapMoveX = tox - (int)myCoordinate.x;
mapMoveY = toy - (int)myCoordinate.y;
myCoordinate = new Vector2(tox, toy);
mapCenterPos = new Vector2(mapCenterPos.x + mapMoveX, mapCenterPos.y + mapMoveY);
mapNextPos = new Vector2(transform.position.x - mapMoveX, transform.position.y - mapMoveY);
}
else
{
isMoving = true;
myCoordinate = Vector2.MoveTowards(myCoordinate, centerPos, moveSpeed * Time.deltaTime);
transform.position = Vector2.MoveTowards(transform.position, mapNextPos, moveSpeed * Time.deltaTime);
}
}
//增加移动路径
private bool NotMove()
{
int _tox = (int)tox;
int _toy = (int)toy;
if (mapName == "laduo")
{
if (tox < 26 && toy < 17)
{
if (mapdataRoad[_tox, _toy] == "00") return false;
if (mapdataRoad[_tox, _toy] == "01") return false;
if (mapdataRoad[_tox, _toy] == "15") return false;
if (mapdataRoad[_tox, _toy] == "30") return false;
if (mapdataRoad[_tox, _toy] == "33") return false;
if (mapdataRoad[_tox, _toy] == "36") return false;
if (mapdataRoad[_tox, _toy] == "41") return false;
if (mapdataRoad[_tox, _toy] == "43") return false;
if (mapdataRoad[_tox, _toy] == "53") return false;
if (mapdataRoad[_tox, _toy] == "62") return false;
if (mapdataRoad[_tox, _toy] == "75") return false;
if (mapdataRoad[_tox, _toy] == "82") return false;
if (mapdataRoad[_tox, _toy] == "83") return false;
}
else
{
return true;
}
}
else if (mapName == "word")
{
if (mapdataRoad[_tox, _toy] == "01") return false;
if (mapdataRoad[_tox, _toy] == "08") return false;
if (mapdataRoad[_tox, _toy] == "09") return false;
if (mapdataRoad[_tox, _toy] == "12") return false;
if (mapdataRoad[_tox, _toy] == "13") return false;
if (mapdataRoad[_tox, _toy] == "27") return false;
if (mapdataRoad[_tox, _toy] == "28") return false;
if (mapdataRoad[_tox, _toy] == "39") return false;
if (mapdataRoad[_tox, _toy] == "44") return false;
if (mapdataRoad[_tox, _toy] == "45") return false;
if (mapdataRoad[_tox, _toy] == "50") return false;
if (mapdataRoad[_tox, _toy] == "51") return false;
if (mapdataRoad[_tox, _toy] == "52") return false;
if (mapdataRoad[_tox, _toy] == "53") return false;
if (mapdataRoad[_tox, _toy] == "60") return false;
if (mapdataRoad[_tox, _toy] == "65") return false;
if (mapdataRoad[_tox, _toy] == "66") return false;
if (mapdataRoad[_tox, _toy] == "68") return false;
if (mapdataRoad[_tox, _toy] == "69") return false;
if (mapdataRoad[_tox, _toy] == "70") return false;
if (mapdataRoad[_tox, _toy] == "71") return false;
if (mapdataRoad[_tox, _toy] == "b7") return false;
if (mapdataRoad[_tox, _toy] == "b8") return false;
if (mapdataRoad[_tox, _toy] == "b9") return false;
if (mapdataRoad[_tox, _toy] == "c0") return false;
if (mapdataRoad[_tox, _toy] == "c1") return false;
if (mapdataRoad[_tox, _toy] == "c2") return false;
if (mapdataRoad[_tox, _toy] == "c3") return false;
if (mapdataRoad[_tox, _toy] == "c4") return false;
if (mapdataRoad[_tox, _toy] == "c7") return false;
if (mapdataRoad[_tox, _toy] == "d2") return false;
if (mapdataRoad[_tox, _toy] == "d5") return false;
if (mapdataRoad[_tox, _toy] == "e0") return false;
if (mapdataRoad[_tox, _toy] == "e1") return false;
if (mapdataRoad[_tox, _toy] == "e2") return false;
if (mapdataRoad[_tox, _toy] == "e3") return false;
if (mapdataRoad[_tox, _toy] == "e9") return false;
if (mapdataRoad[_tox, _toy] == "f0") return false;
}
return true;
}
}
这个模式是摄像机和中心玩家固定不动,移动的是背景达到视觉欺骗实现。
代码分析:
GetMapSprite();//拆分图片获取编号,从左上角编号零开始根据像素拆分下图:
用工具分析拆分图片如下做了记号:
多出来的两列是刻意PS来保留的为了保持是10列的整数。对于没有背景底色的卡片自己动手PS了底色,之前是复制了一个背景色块在底部有同样效果,这样会多消耗一个卡片资源没有采用此方法。
GetMapSprite()函数得到了每一个编号的卡片保存到内存中。
开始加载拉多地图InitMapData(“laduo”, new MapConfig().laduo, 17, 26);
地图名字"laduo",地图数据new MapConfig().laduo,地图行17和列26。
new MapConfig().laduo的二维地图数组封装在dll里面保点密不想直接在工程太显眼。
ShowMapSprite(tox, toy):地图每移动一步需要更新让一个距离中心的一个单位的探路坐标myCoordinate向中心点移动。
dSprite.Add(cardCount, s);这个方法获取二维数组每一个编号精灵,每次移动我们获取屏幕玩家中心点在数组的第几行第几列即:
mapCenterPos = new Vector2(mapCenterPos.x + mapMoveX, mapCenterPos.y + mapMoveY);
地图滚动思路是有一个所有卡片父节点A,按下方向键A移动一步,移动一步完了之后重置A的世界坐标为(0,0):见
transform.position = Vector2.zero;
然后重新偏移映射所有卡片:
for (int i = 0; i < transform.childCount; i++)
{
transform.GetChild(i).position = new Vector2(transform.GetChild(i).position.x + moveX, transform.GetChild(i).position.y + moveY);
}
防止它们的世界坐标移动后发生变化。
NextBoardCard(Transform trans)方法是更新背景卡片的核心代码,
我这里故意让超出屏幕的地图右边多了一列和下边多了一行,
只要红色屏幕区域在整个正方形区域里面滚动啥也不用处理,如果此时红色区域向上或者向左,我们在移动之前马上把黑色区域移动到要去的地方,比如向左移动一格,右边那一列马上向左移动16个格子(也许是17吧,此时屏幕也在移动所以距离要偏移一个单位)到屏幕的左边即可,其他同理。移动之后马上给这些移动的卡片精灵赋值到二维数组对应卡片。我把超出二维数组的卡片赋值了一个沙漠卡片背景:
trans.GetComponent().sprite = spritesmap[0];
没有超出边界的给了这个赋值:
card.GetComponent().sprite = dSprite[SpriteID];
看这个二维数组:
这个红圈里面的17编号在二维数组对应的ID是一个26列+2个列,它的ID是28,我们得到了它的精灵了:card.GetComponent().sprite = dSprite[28];
卡片的ID在移动后这样获取的:
int xCount = 8 - (int)mapCenterPos.x;//中心点X轴偏移总单位
int yCount = -7 - (int)mapCenterPos.y;//中心点Y轴偏移总单位
int x = (int)trans.position.x - xCount;//映射这个卡片在地图数组中的X坐标
int y = (int)trans.position.y - yCount;//映射这个卡片在地图数组中的X坐标
int SpriteID = (x + (col * -y));
思路比较复杂,要多次来反向映射,这代码自己也许过一段时间就看不懂了,也不指望大家能看懂,这个不重要,重要的是可以直接拿来用。
思路是一样的:切割精灵图和加载二维数组,必须多留一行和一列,超出边界直接移动另一边的卡片即可。
NotMove();增加移动路径,固定编号可以前进,比如草是mapdataRoad[_tox, _toy] == “00”,玩家可以停在这个卡片上,其他卡片则禁止。
屏幕分辨率1024X896,这个是一个卡片是32X32得来得,在NES里面截个图刚好是这个大小,NES里一张高清背景的大小是1024X896,所以我这里就设置了这个分辨率。
屏幕左上角坐标是(0,-0.5f),这个-0.5f是NES里面的重装机兵屏幕最上方(和下方)的卡片是显示半个;
屏幕右下角坐标是(15,-13.5f);
屏幕中心点通常第一个玩家的坐标是(8,-7);
摄像机坐标是(7.5f,-7,10);
这个是地图滚动运行结果:
重装机兵屏幕中心地图滚动
下一章给出玩家队列在屏幕中心的一个跟随。