Astar 알고리즘을 활용해서 타일맵 좌표 이동 시 길 찾기
TestTile 컴포넌트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestTile : MonoBehaviour
{
public TextMesh G;
public TextMesh H;
public TextMesh F;
public float g;
public float h;
public float f = 1f;
public Vector2 vec2;
public bool closeNode;
public bool isBlock;
public TestTile(Vector2 vec2)
{
this.vec2 = vec2;
}
}
길 찾기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
public class TestIsometricAstar : MonoBehaviour
{
public int colCount, rowCount;
public Vector2 startPos;
private GameObject mapTile;
private Coroutine routine;
private int tileWidth = 64;
private int tileHeight = 32;
private int col = 0; //행
private int row = 0; //열
private GameObject my;
private GameObject motherNode;
private GameObject targetNode;
private GameObject selectedTile;
private Dictionary<Vector2, GameObject> dicFIndTile = new Dictionary<Vector2, GameObject>();
private List<GameObject> openList = new List<GameObject>();
private List<GameObject> closeList = new List<GameObject>();
void Start()
{
this.CreateMap();
this.CreateTile();
this.TileCheck();
}
void TileCheck()
{
if (this.routine != null)
{
StopCoroutine(this.routine);
}
this.routine = StartCoroutine(this.TileCheckImpl());
}
IEnumerator TileCheckImpl()
{
while (true)
{
if (Input.GetMouseButtonDown(0))
{
Vector2 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero);
if (hit.collider != null)
{
this.targetNode = hit.collider.gameObject;
this.FIndNextPos();
}
}
yield return null;
}
}
private void FIndNextPos()
{
if (this.routine != null)
{
StopCoroutine(this.routine);
}
this.routine = StartCoroutine(this.FindNextPosImpl());
}
private IEnumerator FindNextPosImpl()
{
while(true)
{
if(this.targetNode == this.motherNode)
{
Debug.Log("도착");
this.TileCheck();
break;
}
this.FindNode();
this.SetFGH();
this.SortingOpenList();
yield return null;
}
}
private void SortingOpenList()
{
IEnumerable<GameObject> sortNode = this.openList.OrderBy(x => x.GetComponent<TestTile>().f);
this.selectedTile = sortNode.FirstOrDefault<GameObject>();
this.NextNode();
}
private void SetActiveFGH(GameObject tile)
{
if (!this.openList.Contains(tile))
{
tile.GetComponent<TestTile>().G.gameObject.SetActive(false);
tile.GetComponent<TestTile>().F.gameObject.SetActive(false);
tile.GetComponent<TestTile>().H.gameObject.SetActive(false);
}
else
{
tile.GetComponent<TestTile>().G.gameObject.SetActive(true);
tile.GetComponent<TestTile>().F.gameObject.SetActive(true);
tile.GetComponent<TestTile>().H.gameObject.SetActive(true);
}
}
private void NextNode()
{
this.motherNode = this.selectedTile;
this.selectedTile.GetComponentInChildren<SpriteRenderer>().color = Color.magenta;
}
private void FindNode()
{
var mother = this.motherNode.GetComponent<TestTile>();
var motherX = mother.vec2.x - 1;
var motherY = mother.vec2.y - 1;
var maxX = mother.vec2.x + 1;
var maxY = mother.vec2.y + 1;
if (motherX < 0)
{
motherX = 0;
}
if (motherY < 0)
{
motherY = 0;
}
for (int x = (int)motherX; x <= maxX; x++)
{
for (int y = (int)motherY; y <= maxY; y++)
{
var data = new Vector2(x, y);
var tile = this.dicFIndTile[data];
if (tile.GetComponent<TestTile>().isBlock != true && !this.closeList.Contains(tile))
{
this.openList.Add(tile);
this.openList.Remove(motherNode);
this.closeList.Add(motherNode);
if (tile != this.motherNode)
{
tile.GetComponent<SpriteRenderer>().color = Color.yellow;
}
}
}
}
}
private void SetFGH()
{
foreach (var tile in this.openList)
{
var tileScript = tile.GetComponent<TestTile>();
this.SetActiveFGH(tile);
var getG = tile.GetComponent<TestTile>().g;
var getH = tile.GetComponent<TestTile>().h;
getG = Mathf.Round(Vector2.Distance(tile.GetComponent<TestTile>().vec2, this.motherNode.GetComponent<TestTile>().vec2) * 10);
var dx = Mathf.Abs(tile.GetComponent<TestTile>().vec2.x - this.targetNode.GetComponent<TestTile>().vec2.x);
var dy = Mathf.Abs(tile.GetComponent<TestTile>().vec2.y - this.targetNode.GetComponent<TestTile>().vec2.y);
getH = (dx + dy) * 10;
tile.GetComponent<TestTile>().f = getG + getH;
tileScript.G.text = getG.ToString();
tileScript.H.text = getH.ToString();
tileScript.F.text = tile.GetComponent<TestTile>().f.ToString();
}
}
#region 맵, 타일 생성
private void CreateTile()
{
if (this.my == null)
{
this.my = Instantiate(Resources.Load<GameObject>("myIsoTile"));
this.CreatedTileMap(my, this.startPos.x, this.startPos.y);
this.motherNode = my;
}
}
private void CreateMap()
{
while (true)
{
this.mapTile = Instantiate(Resources.Load<GameObject>("mapTile"));
this.mapTile.tag = "Tile";
this.mapTile.transform.SetParent(this.transform);
this.CreatedTileMap(this.mapTile, this.col, row);
this.dicFIndTile.Add(new Vector2(col, row), mapTile);
this.col++;
if (this.col >= this.colCount)
{
this.col = 0;
this.row++;
}
if (this.row >= this.rowCount)
{
row = 0;
break;
}
}
}
private void CreatedTileMap(GameObject tileMap, float x, float y)
{
var vec2 = new Vector2(x, y);
if (tileMap.GetComponentInChildren<TextMesh>())
{
tileMap.GetComponentInChildren<TextMesh>().text = string.Format("({0},{1})", x, y);
this.SetActiveFGH(tileMap);
}
tileMap.AddComponent<TestTile>();
tileMap.GetComponent<TestTile>().vec2 = vec2;
var screen = this.MapToScreen(vec2);
var screenPos = Camera.main.ScreenToWorldPoint(new Vector2(screen.x + 512, screen.y + 384 + 100));
tileMap.transform.position = new Vector3(screenPos.x, screenPos.y, 0);
}
public Vector2 MapToScreen(Vector2 mapPos)
{
var screenX = mapPos.x * this.tileWidth - (mapPos.y * this.tileWidth);
var screenY = mapPos.y * -tileHeight - (mapPos.x * tileHeight);
return new Vector2(screenX, screenY);
}
#endregion
}
+이후 해결 : 딕셔너리 키 오류 해결
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
public class TestIsometricAstar : MonoBehaviour
{
public Button btn;
public int colCount, rowCount;
public Vector2 startPos;
private GameObject mapTile;
private Coroutine routine;
private int tileWidth = 64;
private int tileHeight = 32;
private int col = 0; //행
private int row = 0; //열
private GameObject my;
private GameObject motherNode;
private GameObject targetNode;
private GameObject selectedTile;
private Dictionary<Vector2, GameObject> dicFIndTile = new Dictionary<Vector2, GameObject>();
private List<GameObject> openList = new List<GameObject>();
private List<GameObject> closeList = new List<GameObject>();
void Start()
{
this.CreateMap();
this.CreateTile();
this.TileCheck();
this.btn.onClick.AddListener(() =>
{
//this.NextPosition();
this.FIndNextPos();
});
}
void TileCheck()
{
if (this.routine != null)
{
StopCoroutine(this.routine);
}
this.routine = StartCoroutine(this.TileCheckImpl());
}
IEnumerator TileCheckImpl()
{
while (true)
{
if (Input.GetMouseButtonDown(0))
{
Vector2 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero);
if (hit.collider != null)
{
this.targetNode = hit.collider.gameObject;
hit.collider.GetComponent<SpriteRenderer>().color = Color.cyan;
}
}
yield return null;
}
}
private void FIndNextPos()
{
if (this.routine != null)
{
StopCoroutine(this.routine);
}
this.routine = StartCoroutine(this.FindNextPosImpl());
}
private IEnumerator FindNextPosImpl()
{
while (true)
{
if (this.targetNode == this.motherNode)
{
Debug.Log("도착");
this.FindNode();
this.TileCheck();
this.ClearMap();
break;
}
this.FindNode();
this.SetFGH();
this.NextNode();
yield return new WaitForSeconds(0.5f);
}
}
private void ClearMap()
{
Debug.Log("타일초기화");
for (int i = 0; i < colCount; i++)
{
for (int j = 0; j < rowCount; j++)
{
var tiles = new Vector2(j, i);
var tile = dicFIndTile[tiles];
tile.GetComponent<SpriteRenderer>().color = Color.white;
}
}
this.openList.Clear();
this.closeList.Clear();
}
#region OpenList Sorting and SelecterdTile -> MotherNode
private void NextNode()
{
IEnumerable<GameObject> sortNode = this.openList.OrderBy(x => x.GetComponent<TestTile>().f);
this.selectedTile = sortNode.FirstOrDefault<GameObject>();
this.motherNode = this.selectedTile;
//this.selectedTile.GetComponentInChildren<SpriteRenderer>().color = Color.magenta;
}
#endregion
private void FindNode()
{
var mother = this.motherNode.GetComponent<TestTile>().vec2;
var motherX = mother.x - 1;
var motherY = mother.y - 1;
var maxX = mother.x + 1;
var maxY = mother.y + 1;
if (motherX < 0)
{
motherX = 0;
}
if (motherY < 0)
{
motherY = 0;
}
for (int x = (int)motherX; x <= maxX; x++)
{
for (int y = (int)motherY; y <= maxY; y++)
{
var data = new Vector2(x, y);
if (x < this.colCount && y < this.rowCount)
{
var tile = this.dicFIndTile[data];
if (tile.GetComponent<TestTile>().isBlock != true && !this.closeList.Contains(tile))
{
this.openList.Add(tile);
this.openList.Remove(motherNode);
this.closeList.Add(motherNode);
if (tile != this.motherNode)
{
this.my.transform.position = this.motherNode.transform.position;
tile.GetComponent<SpriteRenderer>().color = Color.grey;
}
}
}
}
}
}
#region FGH 활성화 비활성화
private void SetActiveFGH(GameObject tile)
{
if (!this.openList.Contains(tile))
{
tile.GetComponent<TestTile>().G.gameObject.SetActive(false);
tile.GetComponent<TestTile>().F.gameObject.SetActive(false);
tile.GetComponent<TestTile>().H.gameObject.SetActive(false);
}
else
{
tile.GetComponent<TestTile>().G.gameObject.SetActive(true);
tile.GetComponent<TestTile>().F.gameObject.SetActive(true);
tile.GetComponent<TestTile>().H.gameObject.SetActive(true);
}
}
#endregion
#region OpenList에 FGH 값 넣기
private void SetFGH()
{
foreach (var tile in this.openList)
{
//this.SetActiveFGH(tile);
var getG = tile.GetComponent<TestTile>().g;
var getH = tile.GetComponent<TestTile>().h;
getG = Mathf.Round(Vector2.Distance(tile.GetComponent<TestTile>().vec2, this.motherNode.GetComponent<TestTile>().vec2) * 10);
var dx = Mathf.Abs(tile.GetComponent<TestTile>().vec2.x - this.targetNode.GetComponent<TestTile>().vec2.x);
var dy = Mathf.Abs(tile.GetComponent<TestTile>().vec2.y - this.targetNode.GetComponent<TestTile>().vec2.y);
getH = (dx + dy) * 10;
tile.GetComponent<TestTile>().f = getG + getH;
//tileScript.G.text = getG.ToString();
//tileScript.H.text = getH.ToString();
//tileScript.F.text = tile.GetComponent<TestTile>().f.ToString();
}
}
#endregion
#region 맵, 타일 생성
private void CreateTile()
{
if (this.my == null)
{
this.my = Instantiate(Resources.Load<GameObject>("myIsoTile"));
this.CreatedTileMap(my, this.startPos.x, this.startPos.y);
}
}
private void CreateMap()
{
while (true)
{
this.mapTile = Instantiate(Resources.Load<GameObject>("tile"));
this.mapTile.tag = "Tile";
this.mapTile.transform.SetParent(this.transform);
this.CreatedTileMap(this.mapTile, this.col, row);
this.dicFIndTile.Add(new Vector2(col, row), mapTile);
this.col++;
if (this.col >= this.colCount)
{
this.col = 0;
this.row++;
}
if (this.row >= this.rowCount)
{
row = 0;
break;
}
}
}
private void CreatedTileMap(GameObject tileMap, float x, float y)
{
var vec2 = new Vector2(x, y);
if (tileMap.GetComponentInChildren<TextMesh>())
{
tileMap.GetComponentInChildren<TextMesh>().text = string.Format("({0},{1})", x, y);
this.SetActiveFGH(tileMap);
}
if (tileMap == this.my)
{
this.motherNode = tileMap;
}
tileMap.AddComponent<TestTile>();
tileMap.GetComponent<TestTile>().vec2 = vec2;
var screen = this.MapToScreen(vec2);
var screenPos = Camera.main.ScreenToWorldPoint(new Vector2(screen.x + 512, screen.y + 384 + 100));
tileMap.transform.position = new Vector3(screenPos.x, screenPos.y, 0);
}
public Vector2 MapToScreen(Vector2 mapPos)
{
var screenX = mapPos.x * this.tileWidth - (mapPos.y * this.tileWidth);
var screenY = mapPos.y * -tileHeight - (mapPos.x * tileHeight);
return new Vector2(screenX, screenY);
}
#endregion
}