如何解决Unity Tilemaps,用于突出显示瓷砖/单元格的单瓷砖碰撞检测,奇怪的行为
我已经在这个问题上工作了几天,试图创建一个方形的瓷砖网格,使用 Unity Tilemaps 似乎是最有效的方法,而且它已经内置了很多。
我想突出显示玩家周围的瓷砖,以便他看到合法的动作是什么。 为此,我在底图(地面/草地)之上使用了第二个 Tilemap。
第二个Tilemap,highlightsMap由不可见的Tiles组成,当出现Physics.SphereOverlap
或Physics2D.CircleAll
时会变成高亮的Tiles
为了检测每个 Tile,我在一个空对象上添加了一个盒子碰撞器,并在 Tilemap 网格的顶部制作了这些碰撞器的网格,以便:
Box Collider at Position (2,4,0)
正好在 Tile at Position(2,0)
这应该是最直接的处理方式,因为您可以使用 Tilemap.SetTile(Vector3Int Pos,Tile tile)
然而这个问题很奇怪。碰撞器具有正确的位置值,以便能够仅通过该位置数据来引用它们正下方的图块。如上所述,我已经仔细检查了这一点,碰撞器空物与它们下方的瓷砖具有完全相同的位置,不需要转换。
问题是瓷砖没有像预期的那样在玩家周围突出显示,而是突出显示了玩家旁边的一些而其他人没有,我使用的 Physics.OverlapSphere 仅适用于 3D Colliders,这就是我添加它们的原因作为一切之上的另一个网格。
不幸的是,使用 Physics2D.CircleAll 并没有检测到瓦片本身(精灵或网格)上的任何 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,0
Vector3Int newColPos = new Vector3Int(xC,0); // 2,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,transform);
}
gridArray[i] = newPos;
i++;
}
}
highlightMap.SetTiles(gridArray,tiles);
highlightMap.SetTile(new Vector3Int(2,0),highlightTile); // 2,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.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 forward
和 z+ is up/dephth
(瓷砖地图未使用)。
我拥有的空对撞机预制件是一个空的 GO,位置为 0,0。它有另一个空的 GO Child,它有 Box Collider Component,这个 child 的变换值是 0.5,0.5,0.5,所以 Collider 在每个 tile 的顶部居中。
解决方法
您也可以像使用 GridLayout.WorldToCell
一样使用 Wikipedia: Binomial coefficient,例如
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);
,
highlightTiles[tMax] = Instantiate(highlightTile,col.transform.parent.position,Quaternion.identity,transform);
我不知道为什么,但显然我没有将瓦片本身实例化为瓦片阵列,所以每次我将它们“放置”在网格上时,它实际上是放置一个空阵列。 现在它固定了!只需要找到一个好的方法来在移动和设置后擦除所有内容!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。