c# - 如何检测玩家在网格中的位置?
问题描述
我正在开发一个战术角色扮演游戏,我有一个名为 Map 的空游戏对象,它由 36 个图块组成。
Map 对象附加了以下脚本:
using System.Collections.Generic;
using UnityEngine;
public class GridMap : MonoBehaviour
{
private GameObject[] MyVector = new GameObject[36];
public GameObject[,] GridMatrix = new GameObject[6,6];
public GameObject Map;
// Start is called before the first frame update
void Awake()
{
for (int i = 0; i < 36; i++)
{
try
{
MyVector[i] = Map.transform.GetChild(i).gameObject;
}
catch
{
print("Something is wrong!");
}
}
}
void Start()
{
int Counter = 0;
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
GridMatrix[i, j] = MyVector[Counter];
Counter;
}
}
}
// Update is called once per frame
void Update()
{
}
}
每个图块都有这个脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tile : MonoBehaviour
{
public bool Current = false;
public bool Selectable = false;
public bool MouseOver = false;
public bool Clicked = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void OnMouseOver()
{
MouseOver = true;
if (Clicked == false)
{
GetComponent<Renderer>().material.color = Color.red;
}
if (Input.GetMouseButton(0))
{
Clicked = true;
GetComponent<Renderer>().material.color = Color.green;
}
}
void OnMouseExit()
{
MouseOver = false;
Clicked = false;
GetComponent<Renderer>().material.color = Color.white;
}
private void OnTriggerStay(Collider other)
{
if (other.tag == "Player")
{
GetComponent<Renderer>().material.color = Color.magenta;
Current = true;
}
}
private void OnTriggerExit(Collider other)
{
GetComponent<Renderer>().material.color = Color.white;
Current = false;
}
}
我想检测玩家在哪个图块中,例如:如果玩家在矩阵中位置为 0,0 的图块中,我想在控制台中显示如下内容:“玩家在位置 0,0'。
我已经创建了这个脚本并将其附加到播放器,以便在地图(网格)中获取它的位置,但是它不起作用:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GetPlayerPosition : MonoBehaviour
{
private GameObject mapgrid;
GridMap map;
float tileX, tileY;
// Start is called before the first frame update
void Start()
{
mapgrid = GameObject.FindGameObjectWithTag("map");
map = mapgrid.GetComponent<GridMap>();
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
Tile tile = map.GridMatrix[i, j].GetComponent<Tile>();
if (tile.Current)
{
tileX = i;
tileY = j;
Debug.Log(tileX);
Debug.Log(tileY);
}
}
}
}
// Update is called once per frame
void Update()
{
}
}
我能做些什么来解决这个问题? 项目打印。黑色圆柱体是玩家
解决方案
首先我会做类型
private Tile[] MayVector = new Tile[36]; public Tile[,] GridMatrix = new Tile[6,6];
所以你可以避免所有昂贵的
GetComponent<Tile>
电话,只打一次。我也宁愿使用
OnTriggerEnter
which 只调用一次,而不是OnTriggerStay
每帧调用 which 。OnMouseEnter
对于您的第一部分,因为OnMouseOver
只设置一次就足够了。而不是使用
GameObject.FindGameObjectWithTag
andGetComponent
我也会使用FindObjectOfType
甚至更好(如果可能的话)通过 Inspector 引用所有内容!此外,您应该
GridMatrix
已经在Awake
. 否则Start
ofGetPlayerPosition
可能会在尝试访问数组的方法之前执行,此时它还没有被填充并且只包含元素 → !Start
GridMap
null
NullReferenceException
Awake
我对and的一般经验法则Start
:Awake
:做一切不依赖他人的事情,设置自己的引用和值,使用GetComponent
等来查找场景引用Start
: 做一些取决于你已经设置好的参考的事情。像其他人的访问字段等。
然后我会让整个事情更受事件驱动,而不是让玩家一直循环通过瓷砖,而是让瓷砖设置玩家的位置:
所以像
public class Tile : MonoBehaviour
{
// maybe not needed anymore
public bool Current = false;
public bool Selectable = false;
public bool MouseOver = false;
public bool Clicked = false;
// Will be assigned by the GridMap script
// so every tile will know it's own position
// you could also assign this via the Inspector
// but maybe a lot overhead for 36 tiles ;)
public Vector2Int TilePosition;
// The player will register itself here
// you could even already reference this via the Inspector
// select all tiles and simply drag the player object here
public GetPlayerPosition getPlayerPosition;
// would even be better to already reference this via the Inspector
// but maybe a bit overhead for 36 tiles
// except this is a prefab (what it probably should be ;) )
[SerializeField] private Renderer renderer;
private void Awake ()
{
// do this only once
if(!renderer) renderer = GetComponent<Renderer>();
}
private void OnMouseEnter()
{
// do this only once .. no need to do it every frame
MouseOver = true;
renderer.material.color = Color.red;
}
private void OnMouseOver()
{
// skip further API calls if already Clicked
if(Clicked) return;
if (Input.GetMouseButton(0))
{
Clicked = true;
renderer.material.color = Color.green;
}
}
private void OnMouseExit()
{
MouseOver = false;
Clicked = false;
renderer.material.color = Color.white;
}
private void OnTriggerEnter(Collider other)
{
// do all these only once .. no need to do it every frame
if(other.tag != "Player") return;
renderer.material.color = Color.magenta;
// Tell the player it's current position
playerPosition.GridPosition = TilePosition;
// probably not needed anymore
Current = true;
}
private void OnTriggerExit(Collider other)
{
if(other.tag != "Player") return;
renderer.material.color = Color.white;
// probably not needed anymore
Current = false;
}
}
然后在GridMap中告诉Tile
他们的位置
public class GridMap : MonoBehaviour
{
// just for clean coding reasons
public const int GRID_WIDTH = 6;
public const int GRID_HEIGHT = 6;
public Transform Map;
public Tile[,] GridMatrix = new Tile[GRID_WIDTH, GRID_HEIGHT];
private Tile[] MyVector = new Tile[GRID_WIDTH * GRID_HEIGHT];
// I would move it all to Awake
// 1. it is possible so why wait until Start
// 2. Makes also sure that the Start method of GetPlayerPosition
// will definitely be executed after all this
// => makes sure the array will actually be filled already!
private void Awake()
{
for (int i = 0; i < GRID_WIDTH * GRID_HEIGHT; i++)
{
try
{
MyVector[i] = Map.GetChild(i).GetComponent<Tile>();
}
catch
{
print("Something is wrong!");
return;
}
}
for (var x = 0; x < GRID_WIDTH; x++)
{
for (var y = 0; y < GRID_HEIGHT; y++)
{
// This depends of course on how you arranged the tiles in the hierarchy
GridMatrix[x, y] = MyVector[x * GRID_WIDTH + y];
GridMatrix[x, y].TilePosition = new Vector2Int (x, y);
}
}
}
}
现在剩下的就是玩家必须注册到 Tiles:
public class GetPlayerPosition : MonoBehaviour
{
// would be better if you already reference this via the Inspector
[SerilaizeField] private GridMap map;
// Will be assigned when entering a Tile
// since you want integer values I would also store them as such
// and Vector2Int is already made for storing a value pair x and y ;)
public Vector2Int GridPosition;
private void Awake()
{
if(!map) map = FindObjectOfType<GridMap>();
}
private void Start()
{
// if you don't have to know the indices within the loop
// you can iterate a multidimensional array simply via foreach
// because internally it is actually saved and treated as flat array
foreach (var tile in map.GridMatrix)
{
tile.playerPosition = this;
}
}
}
推荐阅读
- powershell - 如何检查数组日期是否处于特定模式
- c# - orA-01843: 批量上传 excel 时月份无效
- c# - 显示自最近的数据库 DateTime 以来经过的时间,并每隔一秒增加一次
- haskell - 在haskell中将一个元素从一个列表移动到另一个列表
- python - 首先根据年份降序和月份升序对 pandas DataFrame 列进行排序
- arrays - 如何在快速附加数组后使表格视图从 Firebase Firestore 加载数据
- javascript - 这里映射 api:行为在 Microsoft Web 浏览器 ActiveX 中没有反应
- google-optimize - Google Optimize 重定向测试重新加载页面
- node.js - Mongoose nodejs:find() 和 populate() 调用
- docker - 如何检查停止的 docker 容器文件