본문 바로가기

IT/유니티

2016/8/24 개발노트

어떤 객체의 자식들만 검색하고 싶을때

transform.FindChild("")

를 이용하면 된다.


괜히 모든 객체를 검색하지 않아도 찾을수 있다.


그리고 오늘은 오랜만에 세바스찬의 길찾기 알고리즘을 만들어 보았다.

역시 세바스찬의 강의는 들을때마다 항상 감동인거 같다.


A*알고리즘이라고 불리우는데 알고리즘의 문외한으로써 처음 봤을때 참 멋지다고 느꼈다.

알고리즘의 원리는 아주 간단하다. 

위의 그리과 같이 seeker의 위치와 target의 위치를 이용해서 가장 짧은 길을 찾는것이다.

의외로 단순한것이지만, 이것을 코드로 구현해내는것은 쉽지 않을것이다.


코드는 다음과 같다.



Grid2D.cs

using UnityEngine;

using System.Collections;

using System.Collections.Generic;


public class Grid2D : MonoBehaviour {


public bool onlyDisplayPathGizmo;

public LayerMask unwalkableMask;

public Vector2 gridWorldSize;

public float nodeRadius;

Node[,] grid;


float nodeDiameter;

int gridSizeX;

int gridSizeY;


void Start(){

nodeDiameter = nodeRadius * 2;

gridSizeX = Mathf.RoundToInt (gridWorldSize.x / nodeDiameter);

gridSizeY = Mathf.RoundToInt (gridWorldSize.y / nodeDiameter);

CreateGrid ();

}


public int MaxSize{

get{

return gridSizeX * gridSizeY;

}

}




void CreateGrid(){

grid = new Node[gridSizeX, gridSizeY];

Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x / 2 - Vector3.up * gridWorldSize.y / 2;



for (int x = 0; x < gridSizeX; x++) {

for (int y = 0; y < gridSizeY; y++) {

Vector3 worldPoint = worldBottomLeft + Vector3.right * (x * nodeDiameter + nodeRadius) + Vector3.up * (y * nodeDiameter + nodeRadius);

bool walkable = !(Physics.CheckSphere (worldPoint, nodeRadius, unwalkableMask));

grid [x, y] = new Node (walkable, worldPoint, x, y);

}

}


}


public List<Node> GetNeighbours(Node node){

List<Node> neighbours = new List<Node> ();


for (int x = -1; x <= 1; x++) {

for (int y = -1; y <= 1; y++) {

if (x == 0 && y == 0) {

continue;

}

int checkX = node.gridX + x;

int checkY = node.gridY + y;


if (checkX >= 0 && checkX < gridSizeX && checkY >= 0 && checkY < gridSizeY) {

neighbours.Add (grid[checkX, checkY]);

}

}

}


return neighbours;


}



public Node NodeFromWorldPoint(Vector3 worldPosition){

float percentX = (worldPosition.x + gridWorldSize.x / 2) / gridWorldSize.x;

float percentY = (worldPosition.y + gridWorldSize.y / 2) / gridWorldSize.y;

percentX = Mathf.Clamp01 (percentX);

percentY = Mathf.Clamp01 (percentY);


int x = Mathf.RoundToInt((gridSizeX - 1) * percentX);

int y = Mathf.RoundToInt((gridSizeY - 1) * percentY);


return grid [x, y];

}


public List<Node> path;

void OnDrawGizmos(){

Gizmos.DrawWireCube (transform.position, new Vector3 (gridWorldSize.x, gridWorldSize.y, 0f));


if (onlyDisplayPathGizmo) {

if (path != null) {

foreach (Node n in path) {

Gizmos.color = Color.black;

Gizmos.DrawCube (n.worldPosition, Vector3.one * (nodeDiameter * .9f));

}

}

} else {

if (grid != null) {


foreach (Node n in grid) {

Gizmos.color = (n.walkable) ? Color.white : Color.red;

if (path != null) {

if (path.Contains (n))

Gizmos.color = Color.black;

}

Gizmos.DrawCube (n.worldPosition, Vector3.one * (nodeDiameter * .9f));

}

}

}

}


}

 


위의 파일은 그리드에 관한 것이고, 다음은 길찾기에 관한 소스이다.


using UnityEngine;

using System.Collections;

using System.Collections.Generic;

using System.Diagnostics;


public class Pathfinding3D : MonoBehaviour {


public Transform seeker, target;


Grid3D grid;


void Awake(){

grid = GetComponent<Grid3D> ();


}


void Update(){

if(Input.GetKeyDown(KeyCode.Space)){

FindPath (seeker.position, target.position);

}

}



void FindPath(Vector3 startPos, Vector3 targetPos){


Stopwatch sw = new Stopwatch ();

sw.Start ();


Node startNode = grid.NodeFromWorldPoint (startPos);

Node targetNode = grid.NodeFromWorldPoint (targetPos);


Heap<Node> openSet = new Heap<Node> (grid.MaxSize);

HashSet<Node> closedSet = new HashSet<Node> ();

openSet.Add (startNode);


while(openSet.Count > 0){

Node currentNode = openSet.RemoveFirst();


closedSet.Add (currentNode);


if (currentNode == targetNode) {

sw.Stop ();

print ("Path found: " + sw.ElapsedMilliseconds + " ms");


RetracePath (startNode, targetNode);

return;

}


foreach (Node neighbour in grid.GetNeighbours(currentNode)) {

if (!neighbour.walkable || closedSet.Contains (neighbour)) {

continue;

}


int newMovementCostToNeighour = currentNode.gCost + GetDistance (currentNode, neighbour);

if (newMovementCostToNeighour < neighbour.gCost || !openSet.Contains (neighbour)) {

neighbour.gCost = newMovementCostToNeighour;

neighbour.hCost = GetDistance (neighbour, targetNode);

neighbour.parent = currentNode;


if (!openSet.Contains (neighbour)) {

openSet.Add (neighbour);

openSet.UpdateItem (neighbour);

}

}

}

}

}


void RetracePath(Node startNode, Node endNode){

List<Node> path = new List<Node> ();

Node currentNode = endNode;


while(currentNode != startNode){

path.Add (currentNode);

currentNode = currentNode.parent;

}

path.Reverse ();


grid.path = path;

}


int GetDistance(Node nodeA, Node nodeB){

int dstX = Mathf.Abs (nodeA.gridX - nodeB.gridX);

int dstY = Mathf.Abs (nodeA.gridY - nodeB.gridY);

if (dstX > dstY)

return 14 * dstY + 10 * (dstX - dstY);

return 14 * dstX + 10 * (dstY - dstX);

}


}


참고로 세바스찬은 기본 리스트구조로 작성했지만, 리스트 구조로일때에는 퍼포먼스가 너무 좋지 않아서

heap트리 구조로 바꾸어서 다시 작성하였다!


눈물나게 멋지다...

최적화 된거 보면 한 5배는 빨리진다는!!