首页 > 解决方案 > 如何检测玩家在网格中的位置?

问题描述

我正在开发一个战术角色扮演游戏,我有一个名为 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()
    {

    }
}

我能做些什么来解决这个问题? 项目打印。黑色圆柱体是玩家

标签: c#unity3d

解决方案


  1. 首先我会做类型

    private Tile[] MayVector = new Tile[36];
    public Tile[,] GridMatrix = new Tile[6,6];
    

    所以你可以避免所有昂贵的GetComponent<Tile>电话,只打一次。

  2. 我也宁愿使用OnTriggerEnterwhich 只调用一次,而不是OnTriggerStay每帧调用 which 。OnMouseEnter对于您的第一部分,因为OnMouseOver只设置一次就足够了。

  3. 而不是使用GameObject.FindGameObjectWithTagandGetComponent我也会使用FindObjectOfType甚至更好(如果可能的话)通过 Inspector 引用所有内容!

  4. 此外,您应该GridMatrix已经在Awake. 否则StartofGetPlayerPosition可能会在尝试访问数组的方法之前执行,此时它还没有被填充并且只包含元素 → !StartGridMapnullNullReferenceException

    Awake我对and的一般经验法则Start

    • Awake:做一切不依赖他人的事情,设置自己的引用和值,使用GetComponent等来查找场景引用

    • Start: 做一些取决于你已经设置好的参考的事情。像其他人的访问字段等。

  5. 然后我会让整个事情更受事件驱动,而不是让玩家一直循环通过瓷砖,而是让瓷砖设置玩家的位置:


所以像

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;               
        }
    }
}

推荐阅读