<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jjarseag_kim.log</title>
        <link>https://velog.io/</link>
        <description>비전공자가 개발자로 취업하기 위해</description>
        <lastBuildDate>Fri, 22 Mar 2024 01:12:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jjarseag_kim.log</title>
            <url>https://velog.velcdn.com/images/jjarseag_kim/profile/03f01cec-dac6-48e7-9ee6-008cce2161c5/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jjarseag_kim.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jjarseag_kim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Unity] Google Directions API 사용해서 길찾기 구현-실패]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-Google-Directions-API-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EA%B8%B8%EC%B0%BE%EA%B8%B0-%EA%B5%AC%ED%98%84-%EC%8B%A4%ED%8C%A8</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-Google-Directions-API-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EA%B8%B8%EC%B0%BE%EA%B8%B0-%EA%B5%AC%ED%98%84-%EC%8B%A4%ED%8C%A8</guid>
            <pubDate>Fri, 22 Mar 2024 01:12:32 GMT</pubDate>
            <description><![CDATA[<p>구현 참고 블로그 : <a href="https://techblog.raccoon.ne.jp/archives/2019090403.html">https://techblog.raccoon.ne.jp/archives/2019090403.html</a></p>
<p>이제 구글 Direction API를 써서 static map에 경로 표시를 해보려고 했다.
참고 블로그를 보고 코드작성하고 경로를 불러오기를 하려는데 화면상에서는 전혀 변화가 없었다..</p>
<p>그래서 구글 api상태를 보기로 한다.
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/abd2f6eb-80ef-4f23-8291-40a4e98bdc71/image.png" alt="">
오류율이 100%인건 정상이 아닌거같은데 뭔가 이상하다.</p>
<p>웹url에서도 길찾기가 작동되는지 확인해봤는데 2가지 에러메세지를 받았다.</p>
<p>1) API를 쓸 때 기능을 허용하지 않음으로 발생한 에러 메세지
2) 안드로이드 API키로 제한을 해서 웹브라우저엔 안뜬다는 메세지</p>
<p>첫번째는 api 제한없음으로 해결했으나 두번째는 확인이 좀 어려워졌다. </p>
<p>길찾기를 하게되면 지나가는 경로의 위도와 경도를 url에 배열로 입력이 된다. 그래서 구글 맵 서비스로  가서 원하는 위치의 위도, 경로 배열값을 보자는 심정으로 구글 맵 경로 데이터를 보러 사이트에 들어가서 입력을 했는데
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/e6dbcaa2-4241-4024-8fcc-38437f693bc6/image.png" alt="">
도보 경로를 계산을 못 한다는 메세지를 받았다...(다른 이동수단도 마찬가지이다.)</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/a4d9c4ca-dcee-4d1d-97d8-ae82ee8fa567/image.png" alt="">
참고로 미국은 잘 된다.</p>
<p>구글 api는 사용량에 따라 돈받겠다고 하면서 이런 서비스를 잘도 내논거같다.
<del>메뉴얼에 미국한정이라고 써놓던지</del></p>
<p>그래서 다음 포스트는 네이버 api로 맵 구성을 해보겠다.
이번에 외부 api 받는건은 확실하게 공부하는거같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] Google static map 현재위치 받기]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-Google-static-map-%ED%98%84%EC%9E%AC%EC%9C%84%EC%B9%98-%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-Google-static-map-%ED%98%84%EC%9E%AC%EC%9C%84%EC%B9%98-%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Wed, 20 Mar 2024 03:54:09 GMT</pubDate>
            <description><![CDATA[<p>내 기기의 위치값이 받아지는 것을 확인하고
하단에 구글 스테틱 맵을 불러오는 것을 구현해본다.
참고 : <a href="https://techblog.raccoon.ne.jp/archives/2019090402.html">https://techblog.raccoon.ne.jp/archives/2019090402.html</a>,
<a href="https://www.youtube.com/watch?v=HkmxWhQ3E0U&amp;t=181https://www.youtube.com/watch?v=HkmxWhQ3E0U&amp;t=181">https://www.youtube.com/watch?v=HkmxWhQ3E0U&amp;t=181https://www.youtube.com/watch?v=HkmxWhQ3E0U&amp;t=181</a></p>
<pre><code class="language-C#">using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;


public class GoogleStaticMapRoad : MonoBehaviour
{
    public string apiKey = &quot;&quot;;
    private string url = &quot;&quot;;
    private int mapWidth = 640;
    private int mapHeight = 640;
    public int zoom = 20;
    private Rect rect;
    public enum resolution { low = 1, high = 2 };
    public resolution mapResolution = resolution.low;
    public enum type { roadmap, satellite, gybrid, terrain };
    public type mapType = type.roadmap;

    public TextMeshProUGUI mapLog = new TextMeshProUGUI(); 

    private int frame = 0;
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(getStaticMap());
        rect = gameObject.GetComponent&lt;RawImage&gt;().rectTransform.rect;
        mapWidth = (int)Math.Round(rect.width);
        mapHeight = (int)Math.Round(rect.height);

    }

    // Update is called once per frame
    void Update()
    {
        if(frame &gt;= 100)
        {
            StartCoroutine(getStaticMap());
            frame = 0;
        }
        frame++;
    }


    IEnumerator getStaticMap()
    {
        url = &quot;https://maps.googleapis.com/maps/api/staticmap?&quot; + &quot;&amp;zoom=&quot; + zoom + &quot;&amp;size=&quot; + mapWidth + &quot;x&quot; + mapHeight + &quot;&amp;scale=&quot; + mapResolution + &quot;&amp;maptype=&quot; + mapType + &quot;&amp;key=&quot; + apiKey;

        var query = &quot;&quot;;
        query += &quot;&amp;center=&quot; + UnityWebRequest.UnEscapeURL(string.Format(&quot;{0}, {1}&quot;, Input.location.lastData.latitude, Input.location.lastData.longitude));
        query += &quot;&amp;markers=&quot; + UnityWebRequest.UnEscapeURL(string.Format(&quot;{0}, {1}&quot;, Input.location.lastData.latitude, Input.location.lastData.longitude));


        UnityWebRequest www = UnityWebRequestTexture.GetTexture(url + query);

        yield return www.SendWebRequest();

        if (www.error == null)
        {

            Destroy(GetComponent&lt;RawImage&gt;().texture);
            GetComponent&lt;RawImage&gt;().texture = ((DownloadHandlerTexture)www.downloadHandler).texture;
            mapLog.text = &quot;택스쳐 불러오기 성공&quot;;
        }
        else
        {
            mapLog.text = &quot;error. 맵 불러오기 실패&quot;;
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/19976c14-ddd6-431f-86d7-27591a8301f4/image.png" alt=""></p>
<p>게임뷰에서는 위치값과 맵이 불러와지는 것을 확인했다.</p>
<p>그러나 안드로이드에서는 맵이 안나오는 현상을 보게 됐는데..
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/38a3fde2-63ed-4765-b1b8-7fc99b1d96b7/image.png" alt=""></p>
<p>이유는 Maps api key에 애플리케이션 제한사항 설정을 안드로이드로 안해놨기 때문이었다..</p>
<p>문제사항 참고 블로그 : <a href="https://velog.io/@flunge/Play-Store%EC%97%90%EC%84%9C-%EB%8B%A4%EC%9A%B4-%EB%B0%9B%EC%9D%80-%EB%82%B4%EC%95%B1%EC%9D%98-google-map%EC%9D%B4-%EB%B3%B4%EC%9D%B4%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C">https://velog.io/@flunge/Play-Store%EC%97%90%EC%84%9C-%EB%8B%A4%EC%9A%B4-%EB%B0%9B%EC%9D%80-%EB%82%B4%EC%95%B1%EC%9D%98-google-map%EC%9D%B4-%EB%B3%B4%EC%9D%B4%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C</a></p>
<p>패키지는 유니티 빌드 셋팅에서 확인할 수 있는데 SHA-1은 어떻게 확인하는가 했더니
유니티에서 keystore를 발급받고(<a href="https://docs.unity3d.com/kr/2022.3/Manual/android-keystore-manager.html">유니티메뉴얼</a>) 
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/8cd5a336-95f6-43ce-834a-896b0ab622aa/image.png" alt=""></p>
<p>콘솔창에서 keytool -list -v -keystore keyname.keystore를 입력하라고 하는데 문제는 keytool를 실행할 수 있는 프로그램이 아니라는 메세지가 나왔다.
그리고 이것을 해결하기위해 java를 깔아야 했다..(참고 : <a href="https://m.blog.naver.com/bbbisskk2/222977839105">https://m.blog.naver.com/bbbisskk2/222977839105</a>)
환경변수까지 설정하니 명령어가 제대로 작동하고 SHA-1 값도 얻었다.</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/0bea7a1b-a328-4b58-bc80-d032c0e1da31/image.png" alt="">
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/557b94aa-96d4-47ba-93f6-2d13cc65d139/image.png" alt="">
API 제한 설정을 하고 빌드를 다시 하니 안드로이드에서도 잘 나오는 것을 확인했다.
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/fbb120ee-5a99-483e-803d-89da83e33f78/image.jpg" alt=""></p>
<p>여담이지만 지금 플젝중인 다른 팀은 네이버api를 쓰거나 microsoft Maps SDK를 써서 내 문제는 구글링으로 찾았어야했다..
java까지 깔아야하나 했을 땐 현타아닌 현타가 왔었지만 잘 해결되서 다행이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 안드로이드 기기에서 GPS값 가져오기]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B8%B0%EA%B8%B0%EC%97%90%EC%84%9C-GPS%EA%B0%92-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B8%B0%EA%B8%B0%EC%97%90%EC%84%9C-GPS%EA%B0%92-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Tue, 19 Mar 2024 11:53:02 GMT</pubDate>
            <description><![CDATA[<p>참고 : <a href="https://www.youtube.com/watch?v=tMRoXrR7m6o">Unity Android #GPS #Foundation</a></p>
<p>안드로이드 기기에서 위치정보값을 가져오는것을 테스트하기로 한다.</p>
<pre><code class="language-C#">using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.Android;

public class GpsTest : MonoBehaviour
{

    public TextMeshProUGUI[] data = new TextMeshProUGUI[4];
    public float delay;
    public float maxtime = 5f;

    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(Gps_manger());
    }
    IEnumerator Gps_manger()
    {
        if(!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
        {
            Permission.RequestUserPermission(Permission.FineLocation); 
            while(!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
            {
                yield return null;
            }
        }

        if(Input.location.isEnabledByUser)
        {
            data[3].text = &quot;Gps 장치가 꺼져있음.&quot;;
        }

        Input.location.Start();

        while(Input.location.status == LocationServiceStatus.Initializing &amp;&amp; delay &lt; maxtime) 
        {
            yield return new WaitForSeconds(1.0f);
            delay++;
        }

        if(Input.location.status == LocationServiceStatus.Failed || Input.location.status == LocationServiceStatus.Stopped)
        {
            data[3].text = &quot;위치정보를 가져오는데 실패함.&quot;;
        }

        if ( delay &gt;= maxtime ) 
        {
            data[3].text = &quot;지연시간 초과.&quot;;
        }

        data[0].text = &quot;위도 :&quot; + Input.location.lastData.latitude.ToString();
        data[1].text = &quot;경도 :&quot; + Input.location.lastData.longitude.ToString();
        data[2].text = &quot;고도 :&quot; + Input.location.lastData.altitude.ToString();
        data[3].text = &quot;위치 정보 수신완료&quot;;
        yield return new WaitForSeconds(5.0f);
    }</code></pre>
<p>빌드 후 문제점이 순차적으로 2개가 나왔다.</p>
<ol>
<li>UI에서 위도/경도/고도/로그 텍스트가 표시 안됨.</li>
</ol>
<ul>
<li>erachy-&gt;XR-&gt; UI Canvas로 만들지 않았기 때문이었음.</li>
<li>그리고 TextMeshPro에서 한글을 지원해주는 폰트로 변경함.</li>
</ul>
<ol start="2">
<li>UI에서 위도/경도/고도/로그값이 0으로 뜨고 변경되지 않음.</li>
</ol>
<ul>
<li>이것은 실내라서 GPS값을 못 받아온다는 말도 있었으나 기기에 잡히는 와이파이 문제였던 것 같다. 테스트 기기는 안드로이드 테블릿이었는데 셀룰러로 데이터 연결 후 실외에서 작동시키니 GPS값이 잡혔다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 레벨 디자인]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EB%A0%88%EB%B2%A8-%EB%94%94%EC%9E%90%EC%9D%B8</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EB%A0%88%EB%B2%A8-%EB%94%94%EC%9E%90%EC%9D%B8</guid>
            <pubDate>Sun, 25 Feb 2024 16:58:13 GMT</pubDate>
            <description><![CDATA[<p>레벨디자인.. 플레이어와 몬스터가 싸우는 장소를 만들게 됐다.
일딴 맘에 드는 에셋을 찾는데 시간을 쓰고 본격적으로 만들었다.</p>
<p>우리가 만드는 게임은 첫 시작시 플레이어가 전투 전 대기하는 공간이 있고 밖으로 나오면 바로 전투가 시작된다.</p>
<p>분위기는 던전같은 느낌으로 찾는데 보스가 플레이어와의 거리에 따라 뛰기도 해서 전투 영역은 넓게 구성하고 싶었다.</p>
<p>플레이어는 HP를 회복하는 수단이 공격을 받지 않으면서 시간이 지나면 천천히 회복된다.
그래서 넓은 공간 + 보스의 공격을 잠시나마 막을 수 있는 기둥이 있었으면 했다. </p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/4a64645c-2ca6-4799-a9bc-baf486cd73e6/image.png" alt=""></p>
<p>추후 플레이어와 몬스터 제작이 끝나면 테스트 하면서 맵을 수정해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] UI 디자인과 Scene에 올리기]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-UI-%EB%94%94%EC%9E%90%EC%9D%B8%EA%B3%BC-Scene%EC%97%90-%EC%98%AC%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-UI-%EB%94%94%EC%9E%90%EC%9D%B8%EA%B3%BC-Scene%EC%97%90-%EC%98%AC%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Sun, 25 Feb 2024 16:42:49 GMT</pubDate>
            <description><![CDATA[<p>얼추 몬스터 BT 제작이 마무리 되는 느낌이 되자 같이 몬스터를 구성하는 팀원과 플레이어를 구성하는 팀원들 간의 전투시스템에 대한 회의가 오갔다.</p>
<p>나는 주말동안 UI 디자인을 시작했다.
레퍼런스는 엘든링이다.</p>
<p>디자인에 사용한 프로그램으론 Adobe Photoshop, Illustrator를 썼다. 요즘 시대가 좋아져서 Photoshop에서ai 이미지 생성으로 도움을 받았다.</p>
<p>일딴 처음 애플리케이션 실행시 보일 TitleScene을 만들어 봤다.
TitleScene</p>
<ul>
<li>Title</li>
<li>Press Any Button</li>
<li>Press Any Button의 배경이미지
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/6f3cdf4d-a1dc-4123-987d-221b89c6a2d1/image.png" alt=""></li>
</ul>
<p>그리고 PlayScene에서 필요한 UI를 만들었다.
PlayScene</p>
<ul>
<li>Player : HP, SP, Weapon 1~4</li>
<li>Monster : HP</li>
<li>BattleEnd : Win, Lose, Background
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/20e42114-e620-45c6-8dda-394d1253e21e/image.png" alt=""></li>
</ul>
<p>PlayScene에서 캔버스 구성에서 고민을 했다.</p>
<ol>
<li>플레이어는 보스방에 진입 전 대기상태이다.</li>
<li>대기시에는 비무장상태로 보스에 대한 정보가 안 떠있다.</li>
<li>이후 보스방 진입시 보스에 대한 정보가 뜬다.</li>
<li>보스와 전투를 하다가 플레이어가 지거나 이기게 된다.</li>
</ol>
<p>일딴.. 생각으론 상황마다 유니티에서 캔버스를 활성화 시키면 될꺼같다고 생각했다.</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/90a449ab-ddc4-4f2e-80fa-5683a98dce61/image.png" alt="">
Canvas Scaler에서
화면 비율을 1920X1080으로 셋팅했다.
불러온 이미지들은 Texture Type을 Sprite로 변경해주고 캔버스에 이미지를 추가한다. </p>
<p>보통 이미지를 작업하면 도형은 일러스트로 제작하고 텍스쳐가 많이 들어가는 이미지는 포토샵으로 제작했었는데 이번에 UI작업하면서 일러스트에 신규로 업뎃한 치수 기능을 잘 썻다.
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/61abdbe8-edea-40d2-96cc-b626eb459ca9/image.png" alt=""></p>
<p>포토샵은 option키 누르면 치수가 자동으로 떠서 편했다.
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/500da7c8-4434-4f3e-87d9-66bb6d47eed8/image.png" alt="">
그리고 캔버스에 이미지 넣으면서 UI기획 피드백받았던것이 생각났다. 각 UI 픽셀 치수 알아놔야한다는 것이었는데 Rect transform에 정확한 치수를 넣어서 제작하기 때문인가 싶었다.
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/c0e79148-84c7-437a-8952-73b5355a46d4/image.png" alt=""></p>
<p>제작하다 어려운점이라면 Slider였는데 만들다가 ctrl+z를 누르면 fild가 제멋대로 없어지거나 크기가 변해서 애를 먹었다. 
후에 팀원에게 물어보니 유니티 버그라 프로그램을 껏다 켜보라는 조언을 받았다. 해결이 됐는지 확인해보기엔 슬라이더에 너무 많은 시간을 써서 뒤로가기를 안 누르면서 제작했다.</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/178bc6cc-b1f6-4e88-98bd-bfb318baaadd/image.gif" alt=""></p>
<p>이후에 보스와 플레이어 전투시스템이 완성되면  스크립트랑 연결하여 피가 줄어드는것과 무기 스왑 상태가 잘 보이는지 확인해야겠다.</p>
<p>에필로그.</p>
<p>다른 팀원이 엘든링을 보면 플레이어의 HP의 총량이 늘어나기도 하는데 이건 어떤식으로 제작하는 걸까? 라는 질문을 하길래 나는 HP바 프레임이 첫부분과 끝 부분에 장식이 들어가 있는데 이게 어떻게 늘어나는건지...? 생각하다 Sprite 에 이미지 분할기능이 있다는게 생각났다. 그리고 팀원도 HP용량이 늘어나는 방법을 찾았다.</p>
<p>스크립트 부분은 유투브에 유니티로 엘든링과 다크소울을 만드시는 분 영상을 많이 참고하고 있다. 감사하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 몬스터 BT NavMeshAgent]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-NavMeshAgent</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-NavMeshAgent</guid>
            <pubDate>Fri, 23 Feb 2024 00:48:38 GMT</pubDate>
            <description><![CDATA[<p>이제 몬스터가 플레이어를 따라가게 만들기 위해 NavMesh를 적용해본다.</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/90c79570-c479-4119-a04f-50037d7c431f/image.png" alt=""></p>
<p>패키지메니져에서 AI Navigation을 설치해준다.</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/0bdc3e28-f0ca-49f0-8052-7fa0c82c2125/image.png" alt="">
바닥면엔 NavMeshSurface 컴포넌트를 추가해 NavMeshAgent 컴포넌트를 적용한 몬스터가 잘 돌아다니도록 만들어준다. 
그리고 MinoController.cs에 &quot;_agent.SetDestination(_player.position);&quot;를 추가해준다.</p>
<ul>
<li>공격범위 안으로 들어오면 몬스터는 멈추고 AttackActionNode를 실행한다.</li>
<li>공격범위는 콜라이더에서 OnTriggerEnter를 쓰고 몬스터가 적정 범위내에서 멈추도록 NavMeshAgent 컴포넌트에서 Stopping Distanced에서 값을 넣어줬다.</li>
</ul>
<p>플레이어 포지션을 감지해 자동으로 따라가게 만들려고하는데 문제가 발생했다.
그리고 해결의 결론부터 말하자면 이렇다.</p>
<p><strong>1. 공격하는 애니메이션 플레이 도중에 플레이어가 움직이면 공격하는 애니메이션이 재생되는 상태에서 플레이어를 따라다닌다.</strong></p>
<ul>
<li>&quot;BossTrack&quot; 상태일 때만 플레이어를 추적한다.</li>
<li>액션 노드 bool 속성의 클래스 isStoped을 추가해서 공격범위 밖으로 플레이어가 나갈 때 &quot;BossTrack&quot;을 한다.</li>
</ul>
<p><strong>2. Backstep 후 Stomp모션을 하는데 Backstep은 velocity를 적용해 뒤로 점프하는 모션을 넣었는데 점프는 하지만 플레이어한테 향하기 때문에 거리가 안 벌어진다.</strong></p>
<ul>
<li>동일하게 isStoped의 값을 주는 액션 노드를 추가해서 애니메이션 시작 전 노드가 실행되게 했다.</li>
</ul>
<p>처음엔 일딴 메소드 부터 만들었다.</p>
<p>조건은 특정상황에서 애니메이션이 재생될 때 isStoped의 반환이 true / false 인지에 따라 NavMeshAgent의 추척을 멈추거나 다시 재생하는 거였다.</p>
<pre><code class="language-csherp">public void TrackRangeInPlayer()
    {
        if (_attackRange == false &amp;&amp; _isIdle == true)
        {
            if (_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime &gt;= 1f)
            {
                _agent.enabled = true;
                _agent.SetDestination(_player.position);
            }
            if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName(&quot;BossTrack&quot;))
            {
                _agent.enabled = true;
                _agent.SetDestination(_player.position);
            }
        }
        else if (_attackRange == true &amp;&amp; _isIdle == true)
        {
            if (_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime &gt;= 1f)
            {
                transform.LookAt(_player.position);
            }

        }

        if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName(&quot;BossJumpAttack&quot;))
        {
            _agent.isStopped = true;
            if (_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime &gt;= 1f)
            {
                _agent.isStopped = false;
            }
        }

        if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName(&quot;BossBackstep&quot;))
        {
            _agent.isStopped = true;

        }
        if (_bossAnimator.GetCurrentAnimatorStateInfo(0).IsName(&quot;BossStompAttack&quot;))
        {
            _agent.isStopped = true;

            if(_bossAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime &gt;= 1f)
            {
                _agent.isStopped = false;
            }
        }

    }</code></pre>
<p>그리고 팀장님이 이 어지러운 코드를 노드에 넣어서 구동시켜보자고 한다.</p>
<p><strong>3. 플레이어가 몬스터의 뒤로 향해도 플레이어가 공격범위 내에 있으면 플레이어를 보지 않고도 공격모션을 한다.</strong></p>
<ul>
<li>월드좌표계를 사용해서 이런 상황이 발생했다.</li>
<li>벡터 내적을 사용하여 몬스터 기준으로 &quot;BackStep&quot;을 수행할 범위를 지정한다.</li>
</ul>
<p>이 세가지 문제를 정리한 코드는 아래와 같다.</p>
<pre><code class="language-csherp">    private void Awake()
    {
        _bossAnimator = GetComponent&lt;Animator&gt;();
        _rigidbody = GetComponent&lt;Rigidbody&gt;();
        _agent = GetComponent&lt;NavMeshAgent&gt;();

        _treeA = new BehaviorTreeBuilder(gameObject)
            .Selector()
                .Sequence()
                    .Condition(&quot;attackRange&quot;, () =&gt; _dist &lt;= _attackRange)
                    .Selector()
                        .Sequence()
                            .Condition(&quot;backPOS&quot;, () =&gt; _isForward == true)
                            .Do(() =&gt;
                            {
                                _agent.isStopped = true;
                                return TaskStatus.Success;
                            })
                            .StateAction(&quot;BossBackstep&quot;, ProcessBackstep)
                            .StateAction(&quot;BossStompAttack&quot;)
                        .End()
                        .Sequence()
                            .Condition(&quot;keepDEF&quot;, () =&gt; _keepDEF == true)
                            .StateAction(&quot;BossKickAttack&quot;)
                        .End()
                        .SelectorRandom()
                            .StateAction(&quot;BossATK1&quot;)
                            .StateAction(&quot;BossATK2&quot;)
                            .StateAction(&quot;BossATK3&quot;)
                            .StateAction(&quot;BossATK4&quot;)
                        .End()
                    .End()
                .End()
                .Sequence()
                    .Condition(&quot;out7SEC&quot;, () =&gt; _dist &gt;= _minJump &amp;&amp; _dist &lt; _maxJump)
                    .Do(() =&gt;
                    {
                        _agent.isStopped = true;
                        return TaskStatus.Success;
                    })
                    .StateAction(&quot;BossJumpAttack&quot;, ProcessJumpAttack)
                .End()
                .RepeatUntilSuccess()
                    .Do(&quot;BossTrack&quot;, () =&gt;
                    {
                        _bossAnimator.Play(&quot;BossTrack&quot;);
                        _agent.isStopped = false;
                        _agent.SetDestination(_player.position);
                        if (_dist &gt; 5f)
                        {
                            return TaskStatus.Success;
                        }
                        else
                        {
                            _agent.isStopped = true;
                            return TaskStatus.Failure;
                        }
                    })
                .End()
            .End()
            .Build();
    }

        // 플레이어와의 위치 계산
    private void _checkDistance()
    {
        _dist = Vector3.Distance(_player.position, transform.position);
        Vector3 directionToPlayer = (_player.position - transform.position).normalized;
        Vector3 bossForward = transform.forward;

        float dotProduct = Vector3.Dot(directionToPlayer, bossForward);

        if (dotProduct &gt; 0.5f)
        {
            _isForward = false;
        }
        else
        {
            _isForward = true;
        }
    }</code></pre>
<p>Behavior Tree의 조건식과 구현에 더 익숙해진거같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 몬스터 Rigidbody 적용 후 공중에 뜨는 현상]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-Rigidbody-%EC%A0%81%EC%9A%A9-%ED%9B%84-%EA%B3%B5%EC%A4%91%EC%97%90-%EB%9C%A8%EB%8A%94-%ED%98%84%EC%83%81</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-Rigidbody-%EC%A0%81%EC%9A%A9-%ED%9B%84-%EA%B3%B5%EC%A4%91%EC%97%90-%EB%9C%A8%EB%8A%94-%ED%98%84%EC%83%81</guid>
            <pubDate>Wed, 21 Feb 2024 02:16:34 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/a83452a1-12bc-475a-828e-8d21d18ea32f/image.gif" alt=""></p>
<p>인스펙터 창에서 애니메이터를 비활성화 하니 공중부양을 하지않았다. 애니메이션 때문에 뜨는것같다.</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/dc9b2d4b-de00-4a4c-9c2d-7c19beaa4fa1/image.png" alt=""></p>
<p>먼저 애니메이션 타입을 Generic -&gt; Humanoid로 변경 </p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/9ec2ebe5-0523-4acf-a8c9-891726a9d5e8/image.png" alt=""></p>
<p>애니메이션에 Root Tranform Rotation Position(Y)에 Bake Into Pose를 체크. (혹은 Based Upon 옵션에서 Feet로 변경)</p>
<p>더 이상 공중에 뜨진 않는데 다른 애니메이션 연동 동작시 몬스터의 방향이 바뀌는 현상이 있었다.
이것은 Root Tranform Rotation Rotation 에 Bake Into Pose를 체크하여 해결했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 몬스터 BT 노드 수정]]></title>
            <link>https://velog.io/@jjarseag_kim/%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EB%85%B8%EB%93%9C-%EC%88%98%EC%A0%95</link>
            <guid>https://velog.io/@jjarseag_kim/%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EB%85%B8%EB%93%9C-%EC%88%98%EC%A0%95</guid>
            <pubDate>Wed, 21 Feb 2024 01:02:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/53498eb3-6266-441d-92de-6c23e54ca4f7/image.png" alt=""></p>
<h4 id="오늘-까지의-이슈사항">오늘 까지의 이슈사항</h4>
<p>1) 구현을 하다보니 RandomSelector에서 attack 노드가 애니메이션이 끝나기도 전에 변경이돼서 몬스터의 공격구현이 제대로 안 된다.</p>
<p> -&gt; 애니메이션의 끝에서 다음 attack모션이 나오게 해야함.</p>
<pre><code>public class CustomAction : ActionBase
{
    int _animationHash;
    private bool _isEnter;
    Animator _animator;

    public CustomAction(string stateName)
    {
        _animationHash = Animator.StringToHash(stateName);
    }
    protected override void OnInit()
    {
        _animator = Owner.GetComponent&lt;Animator&gt;();
    }


    protected override TaskStatus OnUpdate()
    {
        if (_isEnter == false)
        {
             _isEnter = true;
              _animator.Play(_animationHash, 0, 0);

             return TaskStatus.Continue;
           }

        if (_animator.GetCurrentAnimatorStateInfo(0).normalizedTime &gt;= 1f)
           {
            return true;
            _isEnter = false;

            return TaskStatus.Success;
        }

        return TaskStatus.Continue;
    }
}</code></pre><p>GetCurrentAnimatorStateInfo(0) - 현재 재생되는 애니메이션 state
.normalizedTime - 0부터 1까지 시간 정규화</p>
<p>애니메이션 동작 완료시 1f를 주면 해당 state는 Success를 반환해서 완료 상태가된다. 이것으로 애니메이션이 완료되기 전 다음 동작으로 넘어가는 것을 방지한다.</p>
<p>2) 새로 짠 BT구조에서 RandomSelector는 하위노드의 개수회수로만 렌덤으로 돌고 동작이 안 되었다.</p>
<p>노드는 애니메이션 구현이 주가 되므로 Nodestate의 정의가 필요했다.</p>
<ul>
<li>Continue : 애니메이션 재생 동안</li>
<li>Successe : 애니메이션 재생 완료</li>
<li>Failure : 조건 불충족</li>
</ul>
<p>그리고 이미 동작한 노드에서 Successe를 반환하니 다시 재생되지 않았다. 노드를 체크는 하는데 해당 노드 상태가 Successe가 되니 다시 재생을 하지 않았다.</p>
<p>-&gt; 상단 코드의 _animator.Play(_animationHash, 0, 0); 로 다시 초기화로 해결했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 몬스터 BT - Fluid BT로 제작하기]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-Fluid-BT%EB%A1%9C-%EC%A0%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-Fluid-BT%EB%A1%9C-%EC%A0%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 20 Feb 2024 01:30:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://github.com/ashblue/fluid-behavior-tree?tab=readme-ov-file">https://github.com/ashblue/fluid-behavior-tree?tab=readme-ov-file</a></p>
</blockquote>
<p>커스텀 에셋을 이용해서 노드생성을 해보기로 했다.</p>
<pre><code>using UnityEngine;
using CleverCrow.Fluid.BTs.Tasks;
using CleverCrow.Fluid.BTs.Trees;

public class BossAi : MonoBehaviour
{
    [SerializeField] private BehaviorTree _treeA;
    [SerializeField] private bool _isNotPlayerInBossroom = true;
    [SerializeField] private float _bossHP = 10f;
    [SerializeField] private bool _parrying = false;
    [SerializeField] private bool _attackRange = false;
    [SerializeField] private bool _turnBack = false;
    [SerializeField] private bool _playerDefense = false;
    [SerializeField] private bool _outRange = false;

    Animator _bossAnimator;

    private void Awake()
    {
        _bossAnimator = GetComponent&lt;Animator&gt;();

        _treeA = new BehaviorTreeBuilder(gameObject)
            .Selector()
                .Sequence(&quot;2 Sequence&quot;)
                        .Condition(&quot;Player isn&#39;t in Bossroom&quot;, IsPlayerInBossroom)
                        .Do(&quot;BossIdle&quot;, BossIdle)
                    .End()
                .Selector()
                    .Sequence(&quot;1 Sequence&quot;)
                        .Condition(&quot;BossHp &lt;= 0&quot;, () =&gt; _bossHP &lt;= 0)
                        .Do(&quot;Bossdie&quot;, BossDie)
                    .End()
                    .Sequence(&quot;1 Sequence&quot;)
                        .Condition(&quot;Player Parrying&quot;, IsPlayerParrying)
                        .Do(&quot;BossHit&quot;, BossHit)
                    .End()
                .Selector()
                    .Sequence(&quot;1 Sequence&quot;)
                        .Condition(&quot;Player is in Attack Range&quot;, IsPlayerInAttackRange)
                        .Selector()
                            .Sequence(&quot;1 Sequence&quot;)
                                .Condition(&quot;Player located back&quot;, IsPlayerLocatedBack)
                              .Do(&quot;BackJump&quot;, BossBackstep)
                              .Do(&quot;Stump Attack&quot;, BossStompAttack)
                            .End()
                            .Sequence(&quot;1 Sequence&quot;)
                                .Condition(&quot;Player keep defense more than 5sec&quot;, IsPlayerKeepDefense)
                              .Do(&quot;Kick Attck&quot;, BossKickAttack)
                            .End()
                            .SelectorRandom(&quot;Random Attack&quot;)
                                .Do(&quot;Attck1&quot;, BossNormalAttack1)
                                .Do(&quot;Attck2&quot;, BossNormalAttack2)
                                .Do(&quot;Attck3&quot;, BossComboAttack1)
                                .Do(&quot;Attck4&quot;, BossComboAttack2)
                            .End()
                        .End()
                    .End()
                    .Sequence(&quot;1 Sequence&quot;)
                        .Condition(&quot;Player out of attack range&quot;, IsPlayerOutOfRange)
                        .Do(&quot;Jump Attack&quot;, BossJumpAttack)
                    .End()
                    //.Sequence(&quot;1 Sequence&quot;)
                    //  .Condition(&quot;Boss Track Player&quot;, () =&gt; _trackRange)
                    .Do(&quot;Boss Track&quot;, BossTrack)
                    //.End()
                .End()
            .End()
            .Build();
    }

    private void Update()
    {
        // Update our tree every frame
        _treeA.Tick();
    }

    private bool IsPlayerInBossroom()
    {
        if (_isNotPlayerInBossroom == true) {  return true; }
        else { return false; }
    }

    private bool IsPlayerParrying()
    {
        if (_parrying == true) { return true; }
        else { return false; }
    }

    private bool IsPlayerInAttackRange()
    {
        if (_attackRange == true) { return true; }
        else { return false; }
    }

    private bool IsPlayerLocatedBack()
    {
        if (_turnBack == true) 
        {
            _turnBack = false;
            return true; 
        }
        else { return false; }
    }

    private bool IsPlayerKeepDefense()
    {
        if (_playerDefense == true) { return true; }
        else { return false; }
    }
    private bool IsPlayerOutOfRange()
    {
        if (_outRange == true) { return true; }
        else { return false; }
    }


    private TaskStatus BossIdle()
    {
        _bossAnimator.Play(&quot;BossIdle&quot;);
        return TaskStatus.Success;
    }
    private TaskStatus BossDie()
    {
        _bossAnimator.Play(&quot;BossDie&quot;);
        return TaskStatus.Success;
    }

    private TaskStatus BossHit()
    {
        _bossAnimator.Play(&quot;BossHit&quot;);
        return TaskStatus.Success;
    }

    private TaskStatus BossTrack()
    {
        _bossAnimator.Play(&quot;BossTrack&quot;);
        return TaskStatus.Success;
    }

    private TaskStatus BossKickAttack() 
    {
        _bossAnimator.Play(&quot;BossKickAttack&quot;);
        return TaskStatus.Success;
    }

    private TaskStatus BossBackstep()
    {
        _bossAnimator.Play(&quot;BossBackstep&quot;);
        return TaskStatus.Success;
    }

    private TaskStatus BossStompAttack()
    {
        _bossAnimator.Play(&quot;BossStompAttack&quot;);
        return TaskStatus.Success;
    }

    private TaskStatus BossNormalAttack1()
    {
        _bossAnimator.Play(&quot;BossNormalAttack1&quot;);
        return TaskStatus.Success;
    }
    private TaskStatus BossNormalAttack2()
    {
        _bossAnimator.Play(&quot;BossNormalAttack2&quot;);
        return TaskStatus.Success;
    }
    private TaskStatus BossComboAttack1()
    {
        _bossAnimator.Play(&quot;BossCombo1&quot;);
        return TaskStatus.Success;
    }
    private TaskStatus BossComboAttack2()
    {
        _bossAnimator.Play(&quot;BossCombo2&quot;);
        return TaskStatus.Success;
    }
    private TaskStatus BossJumpAttack()
    {
        _bossAnimator.Play(&quot;BossJumpAttack&quot;);
        return TaskStatus.Success;
    }
}</code></pre><p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/00262791-bc20-472c-b7eb-15994b9f9e74/image.gif" alt=""></p>
<p>몬스터 ai에 대한 빌드와 메소드를 만들었으나 역시나 조건생성에 대한 제약사항이 많아 커스텀 노드를 생성하기로 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 몬스터 BT 노드 데이터]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EB%85%B8%EB%93%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EB%85%B8%EB%93%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Tue, 20 Feb 2024 00:55:05 GMT</pubDate>
            <description><![CDATA[<p>어째서..</p>
<p>몬스터 BT는 DFS(Depth First Search : 깊이우선탐색) 구조로 구성되었다.
현재 기본동작으로 idle, die, track, attack을 구현했지만 이후 플레이어의 패링시 피격(hit)이라던지 attack 모션도 조건문으로 구성되는 다양한 공격들이 존재한다.</p>
<pre><code>INode SettingBT()
{
    return new SelectorNode
    (
        new List&lt;INode&gt;()
        {
            new SequenceNode
            (
                new List&lt;INode&gt;()
                {
                    new ConditionNode(() =&gt; bossHP ==0, BossDie),
                    new ActionNode(BossTrack),
                    new ActionNode(BossNornalAttack1),
                }
            ),
            new ActionNode(BossIdle)
        }
    );
}</code></pre><p>조건에 조건으로 계속 들어간다면 이 구조는 한눈에 보기가 어려워진다.
그러면 어떻게 하죠..?
노드를 데이터구조화 시키면 어떨까?
노드를 세팅하고 각 노드에 데이터를 빌드하는 구조라면 앞으로 더 추가될 조건들과 구성 노드를 구성하기 편리할거같다(?)</p>
<p><strong>그렇다면 각 노드를 어떻게 데이터화 시킬것인가?</strong></p>
<p>JSON으로 관리시..</p>
<blockquote>
<p><strong>Json 
&quot;키&quot; : {&quot;값&quot;}</strong>
&quot;selector&quot;: { &quot;type&quot;, &quot;child&quot; }
&quot;sequence&quot;: { &quot;child&quot; }
&quot;if&quot;: { &quot;trueCondition&quot;, &quot;action&quot;}</p>
</blockquote>
<p>attack의 &quot;selector&quot;는 무작위로 구현된다는 조건이 있어 &quot;selector&quot;는 타입이 존재해야한다.</p>
<blockquote>
<p>SelectorType</p>
</blockquote>
<ul>
<li>Normal(0) =&gt; 우선 순위 높은 것부터 차례대로 노드를 평가한다.</li>
<li>Random(1) =&gt; 자식 중에서 무작위로 선택하여 평가한다.</li>
</ul>
<pre><code>{
    &quot;selector&quot; : {
        &quot;type&quot;: 0,
        &quot;child&quot;: {
            &quot;selector&quot;: {
                &quot;type&quot;: 0,
                &quot;child&quot;: {
                    &quot;if&quot;: {
                        &quot;trueCondition&quot;: &quot;IsEmptyHp&quot;,
                        &quot;action&quot;: &quot;BossDie&quot;
                        .
                        .
                        .
                    },
                    &quot;BossAttack&quot;,
                    &quot;BossTrack&quot;
                }
            }
        } 

    }
}</code></pre><p>보기 어렵다.</p>
<p>다른 프로젝트의 BT는 어떤식으로 데이터구성을 했을까?
배열에 모든 노드를 저장해서 쓴다.</p>
<p>디자인 패턴
빌더 패턴</p>
<ul>
<li>객체를 단계별로 생성할 수 있다.
빌더패턴 적용시 파라미터를 메소드 체인징으로 구성</li>
</ul>
<pre><code>enum MinotaurosActionType
{
    Die,
    Attack,
    Track,
    Idle
}

[Die][Attack][Track][Idle]

class MinotaurosBuilder : Builder
{
    void Init()
    {
        MinotaurosActionType을 기반으로 배열에 액션 노드를 저장한다.
    }

    override void Build()
    {
        selector5 = Selector.
                    AddChild((GetNode((int)MinotaurosActionType.NormalAttack1))).
                    AddChild((GetNode((int)MinotaurosActionType.NormalAttack2))).
                    AddChild((GetNode((int)MinotaurosActionType.ComboAttack1))).
                    AddChild((GetNode((int)MinotaurosActionType.ComboAttack2))).
                    SetType(SelectorType.Random).
                    Build();

        sequence = Sequence.
                    AddChild((GetNode((int)MinotaurosActionType.Backstep))).
                    AddChild((GetNode((int)MinotaurosActionType.Stumping))).
                    Build();

        selector4 = Selector.
                    AddChild(sequence.SetCondition(player is behind me)).
                    AddChild(GetNode((int)MinotaurosActionType.KickAttack).SetCondition(player is defense)).
                    AddChild(selector5).
                    Build();

        selector3 = ControlNodeBuilder.
                    AddChild(selctor4.SetCondition(player is in attack range)).
                    AddChild(GetNode((int)MinotaurosActionType.JumpAttack).SetCondition(player is out of attack range)).
                    Build&lt;Selector&gt;();

        selector2 = Selector.
                    AddChild(GetNode(Die).SetCondition(hp == 0)).
                    AddChild(GetNode(Hit).SetCondition(방어 상태)).
                    AddChild(selector3).
                    AddChild(GetNode(BossTrack).SetCondition(공격 범위 내에 없다면)).
                    Build();

        selector1 = Selector.
                    AddChild(selector2.SetCondition(보스룸 진입)).
                    AddCConhild(GetNode(Idle)).
                    Build();

        Root.child = selector1;           
    }
}</code></pre><p>노드 생성 - 노드 빌드 - 데이터 입력으로 빌드한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 몬스터 BT 상태구현(Track 과 Attack)]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EC%83%81%ED%83%9C%EA%B5%AC%ED%98%84Track-%EA%B3%BC-Attack</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EC%83%81%ED%83%9C%EA%B5%AC%ED%98%84Track-%EA%B3%BC-Attack</guid>
            <pubDate>Thu, 15 Feb 2024 15:16:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/a3adeee2-426e-476f-9b54-72442f0be863/image.png" alt=""></p>
<p>어제는 기본 action으로 idel과 die만 구성했다.</p>
<pre><code class="language-c#">    INode SettingBT()
    {
        return new SelectorNode
        (
            new List&lt;INode&gt;()
            {
                new SelectorNode
                (

                new ConditionNode(() =&gt; bossHP &lt;= 0, new ActionNode(BossDie)),
                new ActionNode(BossIdle)
            }

        );
    }</code></pre>
<p>ConditionNode 클래스 추가</p>
<pre><code>public sealed class ConditionNode : INode
{
    INode _onSuccessNode;
    Func&lt;bool&gt; _condition;

    public ConditionNode(Func&lt;bool&gt; condition, INode onSuccessNode)
    {
        _condition = condition;
        _onSuccessNode = onSuccessNode;
    }

    public INode.ENodeState Evaluate()
    {
        if (_condition.Invoke())
        {
            return _onSuccessNode.Evaluate();
        }
        else
        {
            return INode.ENodeState.Failure;
        }
    }
}</code></pre><p>그리고 오늘.. 
BossHit은 패링시스템이라 먼저 보스의 공격 액션을 구성하고 타이핑을 해봐야할거같아서 BossTrack을 먼저 구현했다.
BossTrack은 플레이어가 몬스터의 인식 영역으로 들어오면 walk 애니메이션이 재생된다.
그런데 이걸 ConditionNode에 조건으로 어떻게 넣을것인지 고민해야했다.
참고한 블로그에서는 ENodeState의 action에 조건까지 작성해서 반환해줬는데 난감해졌다.
일딴 참고해서 BossTrack을 구성했다.
그리고 잘 생각해보니 BossAttack의 범위에 들어오면 BossTrack보다 먼저 재생되어야하니 트리구조 변경을 했다.
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/58e1226e-72ee-4a07-9319-5f07a1ffbce5/image.png" alt="">
<img src="https://velog.velcdn.com/images/jjarseag_kim/post/d6901934-9688-4688-9cd3-c9de3b21e17d/image.gif" alt=""></p>
<p>애니메이션이 부드럽게 재생되지 않는다.
이거는 추후 애니메이션 블랜딩트리로 구성해보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[02.14 Git 커밋 오류]]></title>
            <link>https://velog.io/@jjarseag_kim/02.14-Git-%EC%BB%A4%EB%B0%8B-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@jjarseag_kim/02.14-Git-%EC%BB%A4%EB%B0%8B-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Wed, 14 Feb 2024 14:07:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/0b9cdad1-3d9b-49d6-a5bd-24fd70136e62/image.png" alt=""></p>
<p>error: The following untracked working tree files would be overwritten by merge:...
Please move or remove them before you merge.</p>
<p>라는 메세지가 뜨는데 구글링 결과 해당 파일을 merge전에 지우라는 창이 떴다.
뭔가 Unity의 Materlal이 충돌이 나는건가.. 삭제 후 커밋해보자..</p>
<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/74f96db6-df0e-4d44-9e05-db48d7044607/image.png" alt="">
 다행히 성공했다^^!</p>
<p> 교훈: 구글링을 열심히 하자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 몬스터 BT 작성]]></title>
            <link>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EC%9E%91%EC%84%B1</link>
            <guid>https://velog.io/@jjarseag_kim/Unity-%EB%AA%AC%EC%8A%A4%ED%84%B0-BT-%EC%9E%91%EC%84%B1</guid>
            <pubDate>Wed, 14 Feb 2024 02:18:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jjarseag_kim/post/3a75802f-6abb-4a5a-9d3e-709dc8d1cf94/image.png" alt="">
몬스터 Behavior Tree로 AI제작을 하기 앞서 행동 트리를 구조화했다.</p>
<p>처음 접하는 구조다보니 일단 간략하게 &#39;idle&#39; 과 &#39;die&#39; 액션 노드를 구현하는데 집중하기로함.</p>
<p>참고 : <a href="https://leekangw.github.io/posts/91/">https://leekangw.github.io/posts/91/</a></p>
]]></description>
        </item>
    </channel>
</rss>