[프로젝트명] S? : Charles VII

[장르] 전략

[목적] 스터디 전용 & 취업 포트폴리오

 

[사용 에셋]

 

 

 

 

일지 #1

<0> 시작
<1> 캐릭터 [0][0][0]에 출력
<2> 캐릭터 [1][-1][0]으로 이동 with DOTween
<3> 캐릭터 [4][-7][3]으로 이동 by AStar Algorithm

<1> 캐릭터 [0][0][0]에 출력

[A. 출력시 캐릭터 <-> 맵 상호간 오브젝트 보유]

// :: Connect Character and Tile
private void ConnectCharacterAndTile(GameObject character, GameObject tile)
{
    character.GetComponent<Battle_Class_Character>().SetTileObject(tile);
    tile.GetComponent<Battle_Class_Tile>().SetCharacterObject(character);
}

<2> 캐릭터 [1][-1][0]으로 이동 with DOTween

// :: Move Position
public void MovePosition(Vector3 position, System.Action action)
{
    // :: Rotation
    this.gameObject.transform.DOLookAt(position, ROTATION_SPEED)
        .onComplete = () =>
        {
            // :: Move
            this.gameObject.transform.DOMove(position, MOVING_SPEED)
            	.onComplete = () => { action?.Invoke(); };
    	};
}

<3> 캐릭터 [4][-7][3]으로 이동 by AStar Algorithm

[AStar Algorithm이란?]

: A* search algorithm

: 간단 설명 : 거리를 추정해서 가까운 곳 위주로 탐색해서 나아가는 알고리즘

: 참고 : よくわかるA*(A-star)アルゴリズム

: 쓰는 이유?

>> 길찾기를 할 때 쓰는 알고리즘으로 기본적인 알고리즘이라는 소문

 

youtu.be/ESLPnJmlvew

 

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

public class Battle_GOFunc_Astar
{
    // :: Constructor
    public Battle_GOFunc_Astar() { }

    // :: Constant
    private int[,] DIRECTION = new int[6, 3] { { -1, 0, 1 }, { 0, -1, 1 }, { 1, -1, 0 }, { 1, 0, -1 }, { 0, 1, -1 }, { -1, 1, 0 } };

    // :: Variable : for Node
    private LinkedList<int[]> paths;
    private List<Astar_Node> listNode;
    private List<Astar_Node> listOpenNode;
    private List<Astar_Node> listClosedNode;

    // :: Initialise
    public void Init(List<Battle_Class_Tile> listTile)
    {
        // :: Use
        this.paths = new LinkedList<int[]>();
        this.listNode = new List<Astar_Node>();
        this.listOpenNode = new List<Astar_Node>();
        this.listClosedNode = new List<Astar_Node>();

        // :: Make Node and put it in list
        this.AddNodeList(listTile);
    }

    // :: Make Node
    private void AddNodeList(List<Battle_Class_Tile> listTile)
    {
        // :: Convert
        foreach (var itm in listTile)
        {
            // :: Make Node
            Astar_Node node = new Astar_Node();
            node.Init(itm.X, itm.Y, itm.Z);

            // :: Add it
            this.listNode.Add(node);
        }
    }

    // :: GET Path
    public LinkedList<int[]> GetPath(Battle_Class_Tile baseTile, Battle_Class_Tile targetTile)
    {
        // :: Set Base and Target Node
        Astar_Node baseNode = this.GetNode(baseTile.X, baseTile.Y, baseTile.Z);
        Astar_Node targetNode = this.GetNode(targetTile.X, targetTile.Y, targetTile.Z);

        // :: Find Min Path
        this.GetPathImplement(baseNode, targetNode);

        // :: Return
        return this.paths;
    }
    // :: Get Path Implement
    private void GetPathImplement(Astar_Node baseNode, Astar_Node targetNode)
    {
        // :: EXIT : Found Target
        if (baseNode == targetNode)
            return;

        // :: Status Open
        foreach (var itm in this.CheckDirectionNode(baseNode))
        {
            var openNode = itm.Open(targetNode);
            if(openNode != null)
                this.listOpenNode.Add(openNode);
        }

        // :: Find Min
        var minNode = this.FindMinCost();

        // :: Check Distance : And Turn Back When it's 2+
        this.TurnBackPath(baseNode, minNode);

        // :: Add Path
        this.paths.AddLast(new int[3] { minNode.x, minNode.y, minNode.z });

        // :: Close Min and Add Closed Node
        this.listClosedNode.Add(minNode.Closed());
        this.listOpenNode.Remove(minNode);

        // :: Return Self : When reach target Node
        this.GetPathImplement(minNode, targetNode);
    }
    // :: Turn Back Path : When distance is 2+
    private void TurnBackPath(Astar_Node baseNode, Astar_Node minNode)
    {
        // :: Check Distance and Same Distance
        var distance = this.CheckDistance(baseNode, minNode);
        var lastPathSameDistance = baseNode.estimateCost == minNode.estimateCost;

        if (distance == false)
        {
            // :: Close Base Node
            this.listClosedNode.Add(baseNode.Closed());
            this.listOpenNode.Remove(baseNode);

            // :: Delete Last Path
            this.paths.RemoveLast();

            // :: Return Path
            Astar_Node newBaseNode = this.GetNode(this.paths.Last.Value[0], this.paths.Last.Value[1], this.paths.Last.Value[2]);

            // :: Return Self : When distance <= 2 or Same Distance
            distance = this.CheckDistance(newBaseNode, minNode);
            lastPathSameDistance = newBaseNode.estimateCost == minNode.estimateCost;
            if (distance == false)
            {
                this.TurnBackPath(newBaseNode, minNode);
            }
            // :: or Same Distance
            else if (lastPathSameDistance)
            {
                // :: Close Base Node
                this.listClosedNode.Add(newBaseNode.Closed());
                this.listOpenNode.Remove(newBaseNode);

                // :: Delete Last Path
                this.paths.RemoveLast();

                Debug.LogFormat("마지막이 거리와 같을 때 {0},{1},{2} / {3},{4},{5}", newBaseNode.x, newBaseNode.y, newBaseNode.z, minNode.x, minNode.y, minNode.z);
            }
        }
        // :: or Same Distance
        else if (lastPathSameDistance)
        {
            // :: Close Base Node
            this.listClosedNode.Add(baseNode.Closed());
            this.listOpenNode.Remove(baseNode);

            // :: Delete Last Path
            this.paths.RemoveLast();

            Debug.LogFormat("마지막이 거리와 같을 때 {0},{1},{2} / {3},{4},{5}", baseNode.x, baseNode.y, baseNode.z, minNode.x, minNode.y, minNode.z);
        }
    }
    // :: Find Min Distance
    private Astar_Node FindMinCost()
    {
        // :: First Set
        Astar_Node minNode = this.listOpenNode[0];

        // :: Find
        foreach(var itm in this.listOpenNode)
        {
            if (minNode.estimateCost > itm.estimateCost)
                minNode = itm;
        }

        // :: Return
        return minNode;
    }
    // :: CheckDistance
    private bool CheckDistance(Astar_Node baseNode, Astar_Node targetNode)
    {
        var distance = Mathf.Abs(baseNode.x - targetNode.x) + Mathf.Abs(baseNode.y - targetNode.y) + Mathf.Abs(baseNode.z - targetNode.z);
        return distance <= 2;
    }

    // :: Check Direction and Return which isn't null
    private List<Astar_Node> CheckDirectionNode(Astar_Node baseNode)
    {
        // :: List Direction Node
        List<Astar_Node> listDirectionNode = new List<Astar_Node>();

        for(var i = 0; i < DIRECTION.GetLength(0); i++)
        {
            // :: Check Direction with Const
            Astar_Node node = this.GetNode(baseNode.x + DIRECTION[i,0], baseNode.y + DIRECTION[i, 1], baseNode.z + DIRECTION[i, 2]);
            if(node != null 
                && this.listOpenNode.Contains(node) == false
                && this.listClosedNode.Contains(node) == false)
            {
                listDirectionNode.Add(node);
            }
        }

        // :: Return
        return listDirectionNode;
    }
    // :: Get Node
    private Astar_Node GetNode(int x, int y, int z)
    {
        return this.listNode.Find(ele => ele.x == x && ele.y == y && ele.z == z);
    }

    // :: Node Status Enum
    private enum eNodeStatus
    {
        NONE,
        OPEN,
        CLOSED
    }
    // :: Node
    private class Astar_Node
    {
        // :: Constructor
        public Astar_Node() { }

        // :: XYZ
        public int x;
        public int y;
        public int z;

        // :: Status
        public eNodeStatus status;

        // :: Cost
        public int realCost;
        public int heuristicCost;
        public int estimateCost;

        // :: Initialise
        public void Init(int x, int y, int z)
        {
            // :: XYZ
            this.x = x;
            this.y = y;
            this.z = z;

            // :: Status
            this.status = eNodeStatus.NONE;

            // :: Cost
            this.realCost = 0;
            this.heuristicCost = 0;
            this.estimateCost = this.realCost + this.heuristicCost;
        }
        // :: Chagne Status
        public Astar_Node Open(Astar_Node targetNode)
        {
            // :: Don't Open this when Closed
            if (this.status == eNodeStatus.CLOSED)
                return null;

            // :: Open
            this.status = eNodeStatus.OPEN;

            // :: Calculate Cost
            this.realCost += 1;
            this.heuristicCost = Mathf.Abs(this.x - targetNode.x) + Mathf.Abs(this.y - targetNode.y) + Mathf.Abs(this.z - targetNode.z);
            this.estimateCost = this.realCost + this.heuristicCost;

            // :: Return this
            return this;
        }
        public Astar_Node Closed()
        {
            // :: Close
            this.status = eNodeStatus.CLOSED;

            // :: Return this
            return this;
        }
    }
}

[개선점]

>> 엄청 많음, 장애물일 때의 시험 등 여러가지 필요

>> 고려중

블로그 이미지

RIsN

,