【问题标题】:Unity Tilemaps, Single Tile Collision detection for highlighting Tiles/Cells, strange behaviourUnity Tilemaps, Single Tile Collision detection for highlighting Tiles/Cells, 奇怪的行为
【发布时间】:2021-05-27 12:17:39
【问题描述】:

我已经在这个问题上工作了几天,尝试创建一个方形网格,使用 Unity Tilemaps 似乎是最有效的方法,并且已经内置了很多。

我想突出显示玩家周围的图块,以便他看到合法的动作是什么。 为此,我在底图(地面/草)之上使用了第二个 Tilemap。

第二个Tilemap,highlightsMap由不可见的Tiles组成,当Physics.SphereOverlapPhysics2D.CircleAll出现时会变成高亮的Tiles

为了检测每个 Tile,我在一个空对象上添加了一个盒子碰撞器,并在 Tilemap 网格的顶部制作了一个由这些碰撞器组成的网格,这样:

Box Collider at Position (2,4,0) 正好在Tile at Position(2,4,0) 之上

这应该是最直接的处理方式,因为您可以使用Tilemap.SetTile(Vector3Int Pos, Tile tile)更改磁贴

然而问题很奇怪。碰撞器具有正确的位置值,以便能够通过该位置数据准确地引用它们下方的图块。如上所述,我已经仔细检查过,对撞机空的位置与其下方的瓷砖完全相同,不需要转换。

问题是瓷砖没有像预期的那样在玩家周围突出显示,而是玩家旁边的一些被突出显示而其他人没有,我使用的 Physics.OverlapSphere 仅适用于 3D 碰撞器,这就是我添加它们的原因作为一切之上的另一个网格。

不幸的是,使用 Physics2D.CircleAll 并没有检测到图块本身(Sprite 或 Grid)上的任何 2D 对撞机,不确定是否打算这样工作。

碰撞网格脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class scr_ColliderGrid : MonoBehaviour
{
    public GameObject eCollider;
public Tile invisibleTile;
public Tile highlightTile;
public Tilemap highlightMap;
public int xSize, ySize;

private Vector3Int[] gridArray;                 // Tilemaps use Vector3Int BUT only use (X, Y, 0)   !!!!!!!
private int xC = 0, yC = 0, i = 0;

private Tile[] tiles;

private Vector3Int previous;

private void Start()
{
    gridArray = new Vector3Int[xSize * ySize];
    tiles = new Tile[xSize * ySize];

    GenerateCollisionGrid();
}

private void GenerateCollisionGrid()
{
    for (xC = 0; xC < xSize; xC++)
    {
        for (yC = 0; yC < ySize; yC++)
        {
            Vector3Int newPos = new Vector3Int(xC, yC, 0);       // 2, 4, 0
            Vector3Int newColPos = new Vector3Int(xC, yC, 0);    // 2, 4, 0     //This used to be different values, but now they are exactly the same.
            if (invisibleTile != null)
            {
                Tile tile = Instantiate(invisibleTile, newPos, Quaternion.identity, transform);
                tiles[i] = tile;

                GameObject col = Instantiate(eCollider, newColPos, Quaternion.identity, transform);
            }
            gridArray[i] = newPos;
            i++;
        }
    }

    highlightMap.SetTiles(gridArray, tiles);
    highlightMap.SetTile(new Vector3Int(2,4,0), highlightTile);   // 2,4,0  //Test to see if positions are the same. (collider and tiles)
}

玩家高亮合法移动脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class scr_TankMoves : MonoBehaviour
{
public Tile highlightTile;
public TileBase[] highlightTiles;
public Tilemap highlightMap;
public int maxMoveTiles;
public bool highlight;

private Vector3Int previous, previousLeft, previousRight, previousForward, previousAft, previousVect;
private Vector3Int[] previousVectors;

void Start()
{

}
private void Update()
{
    if (Input.GetKeyDown(KeyCode.W))
    {
        transform.localPosition = new Vector3(transform.localPosition.x,
                                         transform.localPosition.y + 1,
                                         transform.localPosition.z );
    }
    if (Input.GetKeyDown(KeyCode.S))
    {
        transform.localPosition = new Vector3(transform.localPosition.x,
                                         transform.localPosition.y - 1,
                                         transform.localPosition.z );
    }
    if (Input.GetKeyDown(KeyCode.D))
    {
        transform.localPosition = new Vector3(transform.localPosition.x + 1,
                                         transform.localPosition.y,
                                         transform.localPosition.z);
    }
    if (Input.GetKeyDown(KeyCode.A))
    {
        transform.localPosition = new Vector3(transform.localPosition.x - 1,
                                         transform.localPosition.y,
                                         transform.localPosition.z);
    }
}
void LateUpdate()
{
    if (highlight)
        HighlightMoves();
    else EraseHighlights();
}
private void HighlightMoves()
{
    previousVectors = new Vector3Int[1];
    highlightMap.SetTiles(previousVectors, null);

    int tMax = 0;
    
    Collider[] legalTiles = Physics.OverlapSphere(transform.position, maxMoveTiles/2);
    previousVectors = new Vector3Int[legalTiles.Length];
    foreach (Collider col in legalTiles)
    {
        //Vector3 conversionVector = new Vector3(col.transform.localPosition.x, col.transform.localPosition.y, col.transform.localPosition.z);
        Vector3Int tileVector = Vector3Int.FloorToInt(col.transform.position);
        previousVectors[tMax] = tileVector;
        tMax++;
    }
    highlightMap.SetTiles(previousVectors, highlightTiles);
}
private void EraseHighlights()
{
    //highlightMap.SetTile(previousForward, null);
    //highlightMap.SetTile(previousAft, null);
    //highlightMap.SetTile(previous, null);
    //highlightMap.SetTile(previousRight, null);
    //highlightMap.SetTile(previousLeft, null);

    highlightMap.SetTiles(previousVectors, null);
}

private void OnDrawGizmos()
{
    Gizmos.DrawWireSphere(transform.position, maxMoveTiles/2);
    }
}


    }

如果您在 Unity 中打开一个新的 3D 项目并使用 Tilemap 设置网格,在我的代码示例中称为 highlightMap,您应该能够使用 2 个脚本准确地重新创建它。

一切都以 2D 为导向,这意味着 x+ is Right y+ is forwardz+ is up/dephth(瓷砖地图未使用)。

我拥有的空 Collider 预制件是一个空的 GO,位置为 0,0,0。它还有一个空的GO Child,里面有Box Collider Component,这个child的transform值是0.5,0.5,0.5,这样Collider就在每个tile的顶部居中。

这是在 0 次移动之后,所以只需按下 Play:

这是在向前移动 1 之后,(y+1):

【问题讨论】:

  • 小心:Afaik Tilemap.SetTile(Vector3Int Pos, Tile tile) 参数 Pos 指的是 grid 位置 .. 这可能与实际的 world Vector3 位置不同(transform.position) 对撞机...你检查它们是否真的在正确的地方?
  • 是的,请确保您使用一个物理引擎;) Physics 和 Physics2D 是两个完全独立的引擎,并且没有任何关联!如果你使用 Physics.OverlapSphere 你将需要 3D Colliders ..如果你想要 Physics2D.OverlapCircle 你将需要 2D Collider2Ds (在 2D 中 Z 轴基本上不存在)......你的截图看起来像你宁愿在 3D 中工作
  • 是的,100% 它们在正确的位置,当我在 pos (2,4,0) 突出显示 Tile 然后遍历层次结构中的 emptyCollider 数组时,emptyCollider 在位置 ( 2,4,0) 正好在突出显示的 Tile(2,4,0) 之上,所以它应该是这样,但我认为添加这些 3d 对撞机是一团糟,是否没有内置的方法来检查附近的瓷砖?我只是获取玩家所在的 Tile 并从中添加/减去以突出显示该中心周围的 Tiles,但这也是一团糟并且不允许可扩展性,这就是我想使用 Sphere/Circle 的原因。跨度>

标签: c# unity3d grid collision tile


【解决方案1】:

你也可以只使用GridLayout.WorldToCell,而不是通过对撞机,例如

Tilemap yourTileMap;

// see https://docs.unity3d.com/ScriptReference/Tilemaps.Tilemap-layoutGrid.html
var grid = yourTileMap.layoutGrid;
// The "Grid" implements "GridLayout"
// see https://docs.unity3d.com/ScriptReference/Grid.html
var currentCellPos = grid.WorldToCell(transform.position);
var currentCell = yourTileMap.GetTile(currentCellPos);

然后,如果您需要获取多个图块,我可能会采用“懒惰/愚蠢”的方式,并执行类似的操作,例如

var currentCellPos = grid.WorldToCell(transform.position);
var currentCell = yourTileMap.GetTile(currentCellPos);

var leftCellPos = grid.WorldToCell(transform.position - transform.right * someRange);
var leftCell = yourTileMap.GetTile(leftCellPos);

var rightCellPos = grid.WorldToCell(transform.position + transform.right * someRange);
var rightCell = yourTileMap.GetTile(rightCellPos);

var frontCellPos = grid.WorldToCell(transform.position + transform.forward * someRange);
var frontCell = yourTileMap.GetTile(frontCellPos);

var backCellPos = grid.WorldToCell(transform.position - transform.forward * someRange);
var backCell = yourTileMap.GetTile(backCellPos);

【讨论】:

  • 谢谢雨果,但不幸的是,这并没有得到相邻的瓷砖。同样的问题仍然存在,获取中心图块很容易,但是如果您通过手动减法和加法来推断获得相邻图块,则特别是如果您希望玩家有 30 个左右的合法移动。我真的需要它来处理球体/圆碰撞,以便可以轻松地为不同的类更改半径。
  • 我以前就是这样做的,但是就像我说的那样它根本不可扩展,也许你可以做一个 for 循环来循环,但它仍然会为每个图块和它会很混乱。这种对撞机的方式在可伸缩性和更少的代码方面要好得多,因为一旦它工作,你需要做的就是改变半径!无论如何,我发现出了什么问题!非常感谢你一直帮助我 derHugo 你是老板!
【解决方案2】:
  highlightTiles[tMax] = Instantiate(highlightTile, col.transform.parent.position, Quaternion.identity, transform);

我不知道为什么,但显然我没有将瓦片本身实例化为瓦片数组,所以每次我将它们“放置”在网格上时,它实际上是放置了一个空数组。 现在它固定了!只需要找到一个很好的方法在移动并设置后擦除所有内容!

【讨论】:

    猜你喜欢
    • 2017-07-17
    • 2011-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-11
    • 1970-01-01
    相关资源
    最近更新 更多