[Unity Tutorial] 3Dキャラクターにアニメーションを追加する

:Unityの3Dアセットは色々と良いものがありますがアニメーションがいない時があります、

:その時AdobeのMixamoを使ってAnimationを簡単に入れるのが可能です。

:これはその方法の記録です。

 

[使用したアセット]

 

=====

 

1. Unityのアセットを買ったら、プロジェクトに追加(Import)しましょう。

2. そうしたらプロジェクトに該当アセットが見れるはず、

>> その中でキャラクター(Character)モデルを探しましょう。

>> Synty StudioのアセットならModelsにCharacter.fbxがあります。 

3. まずそれをフォルダーウィンドウで確認してください。

4. さ、確認(.fbx)したなら、今回はAdobeのMixamoサイトに行きましょう。

>> [Mixamo]

>> Mixamoに到着したら、まずAdobeログインしてAnimationsを開けてください。

5. そうすると下記の通り見えます。

>> そこで右のUPLOAD CHARACTERを押して、

>> さっき確認したキャラクターをアップロードしてください。

6. それじゃ右にキャラが見えます。

>> その状況で左のアニメーションを選択するとキャラが動きます。

>> アニメーションが気に入ったなら右のDOWNLOADを押してください。

7. それじゃ下記の通りDOWNLOAD SETTINGS出ます。

>> ここで重要なものはSkinです。

>> 最初にダウンロードするアニメーションはキャラクターSkinと一緒にDOWNLOADしましょう。

>> 2回目のアニメーションからはwith Skinではなくても良いんです。

>> さ、DOWNLOADしましょう。

8. そしてそのダウンロードしたファイルをプロジェクトに入れてください。

9. 入れたファイルを今回はHierarchyに入れます。

10. Synty Studioの場合すべてのキャラが重なっています。

>> 必要ないキャラはOffしましょう。

11. そうするとキャラが見えますが、白く見えます。

>> Materialが別のものなのでこう見えます。

>> キャラに合ったMaterialを入れましょう。

>> Synty Studioの場合アセット名にMaterial01~04などになっています。

12. さ、アニメーションを入れましょう。

>> キャラにアニメーションを動くアニメーター(Animator)を追加してください。

 

13. 次はアニメーターに入れるAnimator Controllerを生成します。

14. 作ったAnimator ControllerはキャラのAnimatorに入れます。

15. まだAnimator Controllerには何もありません。

16. 今回はAnimator Controllerに入れるAnimationを作ります。

17. だがこれも開けても何もありません。

>> さっきMixamoからダウンロードしてプロジェクトに追加したファイルを開けるとmixamo.comと言うアニメーションがあります。

>> これを開けてください。

18. そのアニメーションを全部コピーします。

19. コピーしたら、さっき新しく作ったアニメーションを開けて貼り付けてください。

20. さ、アニメーションは完了です。

>> さっき作ったAnimator Controllerを開けてそのAnimationをドラグして入れます。

21. 参考ですが、このアニメーション(New Animation)のInspectorでLoop Timeをチェックするとループで動きます。

22. さ、実行してみましょう。

23. キャラが動いたら成功です!

 

 

==========

 

블로그 이미지

RIsN

,

[유니티] 캐릭터 3D 모델 에셋에 애니메이션 입히기 with Synty Studio Asset

 

[사용 에셋]

 

=====

 

1. 유니티 에셋을 구매하셨으면, 해당 에셋을 프로젝트에 추가(Import) 합니다

2. 그러면 이제 해당 에셋들이 프로젝트에 추가되어 있을 겁니다.

>> 그 속에서 캐릭터(Character) 모델을 찾습니다.

>> Synty Studio 에셋들이라면 폴더의 Models에 Character에 있습니다. 

3. 그것을 우선 탐색창(윈도우 폴더)에서 확인하세요

4. 자 확인했으면, 이제는 인터넷을 열어서 Adobe의 Mixamo 사이트로 향합니다.

>> [Mixamo로 가기]

>> Mixamo에 도착했으면 Adobe 로그인을 하고 Animations를 열어주세요.

5. 그럼 아래와 같이 보일 겁니다.

>> 거기서 우선 오른쪽의 UPLOAD CHARACTER를 눌러주세요

6. 누르면 아래와 같이 파일을 업로드 할 수 있는 창이 나옵니다.

>> 거기에 아까 확인한 캐릭터 모델 파일(Characters.fbx)을 업로드합니다.

7. 그럼 오른쪽 창에 업로드한 캐릭터가 보일 겁니다.

>> 왼쪽에는 이제부터 이 캐릭터에 붙일 애니메이션들이 있습니다.

8. 그 중 이번에 캐릭터에 붙일 애니메이션을 찾아서 눌러주세요.

>> 그럼 오른쪽에 올린 캐릭터가 그 애니메이션을 보여줄 겁니다.

>> 그 애니메이션이 마음에 들면 오른쪽의 DOWNLOAD 버튼을 눌러주세요.

9. 그럼 이렇게 DOWNLOAD SETTINGS가 나옵니다.

>> 여기서 중요한 것은 Skin입니다.

>> 첫번째로 받을 애니메이션은

>> 캐릭터의 베이스(애니메이션을 입힐 수 있는 상태의 캐릭터)가 될 예정이므로 with Skin으로 받습니다.

>> 같은 캐릭터의 2번째 애니메이션부터는 with Skin이 없어도 됩니다.

>> 설정을 확인했으면 DOWNLOAD를 누릅니다.

10. 그럼 다운로드한 파일이 있을 건데, 그것을 이번에 다시 프로젝트에 넣어주세요.

>> 이름은 원하시는 대로, 폴더도 원하시는 대로 바꾸셔도 됩니다.

11. 그렇게 넣은 파일을 이번에는 Hierarchy 창에 넣습니다.

12. Synty Studio의 캐릭터의 경우 모든 캐릭터가 겹쳐 있습니다.

>> 이 중에서 필요없는 캐릭터들은 전부 꺼주시고, 필요한 캐릭터만 남깁니다.

13. 그러면 캐릭터가 Scene에 보입니다.

>> Synty Studio의 경우 Material이 lambert라는 이상한 게 박혀 있어서 하얗게 나옵니다.

14. 그러니 해당 캐릭터의 Inspector에서 Material을 변경해줍니다.

>> Synty Studio의 경우 에셋명에 Material_01 ~ 04 등으로 머티리얼이 되어 있습니다.

15. 자, 그럼 캐릭터가 제대로 색이 입혀져서 보입니다.

16. 이제는 애니메이션을 넣을 차례입니다.

>> 캐릭터의 가장 위의 폴더의 Inspector에서

>> Add Component로 애니메이션을 움직일 애니메이터(Animator)를 추가해줍니다.

 

17. 다음은 애니메이터 컴포넌트에 넣을 Animator Controller를 추가할 차례입니다.

>> Project 창에서 Create → Animator Conroller를 만들어줍니다.

18. 만든 Animator Controller를 우선

>> 캐릭터의 Inspector에 추가한 Animator 컴포넌트의 Controller에 넣어줍니다. 

19. 그리고 만든 Animator Controller를 열어보면?

20. 아직 아무것도 없습니다.

21. 이제는 애니메이션을 추가할 차례입니다.

>> Project 창에서 Create → Animation으로 새 애니메이션(Animation)을 만들어 줍니다.

22. 하지만 이것도 비어 있습니다.

>> 그러니 아까 받은 파일에서 애니메이션을 가져와야 합니다.

>> 프로젝트에 추가한 파일을 열어서 보면 mixamo.com이라는 애니메이션이 있습니다.

>> 이대로 이것을 애니메이터 컨트롤러(Animator Controller)에 추가해도 되지만,

>> 수정 불가(Read Only)에 반복(Loop)도 안되는 상황이라 그대로 쓸 수가 없습니다.

 

>> 우선 mixamo.com이라는 애니메이션을 열어주세요

23. 그럼 아래와 같이 잔뜩 무언가가 있습니다.

24. 이것을 Ctrl+A로 전부 잡아서 복사(Ctrl+C)해줍니다.

25. 이제 새로 만든 애니메이션을 열고

26. 아무것도 없는 이곳에다

27. 복사해줍니다.

>> 이걸로 이제 이 애니메이션은 수정도 가능하고, 반복도 편하게 할 수 있습니다.

28. 이제 다시 아까 만든 Animator Controller를 열어서

29. 아무것도 없는 이곳에 지금 많은 것을 추가해 넣은 애니메이션(New Animation)을 끌어다 놔줍니다.

30. 그럼 기본적으로 알아서 기본 애니메이션(Default)로 잡힐 겁니다.

31. 참고로 지금 이 애니메이션(New Animation)의 Inspector에서 Loop Time을 체크하면 반복적으로 움직입니다.

32. 자 이제 프로젝트를 실행해 봅시다.

33. 캐릭터가 움직이는 게 보인다면 성공입니다!

 

 

==========

 

블로그 이미지

RIsN

,

《Fade》

【MecanimのAlphaの修正】

// :: Change Skeleton Alpha
this.GetComponent<SkeletonMecanim>().skeleton.A = 0f;
블로그 이미지

RIsN

,

:: プラットフォームゲームテスト

:: 上に上がる時は衝突無視

youtu.be/hzvA86wAHAo

>>改善すべき:UpdateのたびにBoxColliderを探すのは非効率的。

using UnityEngine;

// PlayerController는 플레이어 캐릭터로서 Player 게임 오브젝트를 제어한다.
public class PlayerController : MonoBehaviour {
   public AudioClip deathClip; // 사망시 재생할 오디오 클립
   public float jumpForce = 700f; // 점프 힘

   private int jumpCount = 0; // 누적 점프 횟수
   private bool isGrounded = false; // 바닥에 닿았는지 나타냄
   private bool isDead = false; // 사망 상태

   private Rigidbody2D playerRigidbody; // 사용할 리지드바디 컴포넌트
   private Animator animator; // 사용할 애니메이터 컴포넌트
   private AudioSource playerAudio; // 사용할 오디오 소스 컴포넌트


   private void Start() {
        // 초기화
        // 게임 오브젝트로부터 사용할 컴포넌트들을 가져와 변수에 할당
        this.playerRigidbody = this.GetComponent<Rigidbody2D>();
        this.animator = this.GetComponent<Animator>();
        this.playerAudio = this.GetComponent<AudioSource>();
   }

    private Vector2 playerVelocity;
    private void Update() {
        // 사용자 입력을 감지하고 점프하는 처리
        // 사망 시 처리를 더 이상 진행하지 않고 종료
        if(isDead)
        {
            return;
        }

        this.playerVelocity = playerRigidbody.velocity;
        if(playerRigidbody.velocity.y > 0)
        {
            // :: Find all BoxCollider2D
            foreach (var itm in GameObject.FindObjectsOfType<BoxCollider2D>())
            {
                // :: Player : Ignore it
                Physics2D.IgnoreCollision(playerRigidbody.gameObject.GetComponent<CircleCollider2D>(), itm, true);
            }
        } else if(playerRigidbody.velocity.y < 0)
        {
            foreach(var itm in GameObject.FindObjectsOfType<BoxCollider2D>())
            {
                Physics2D.IgnoreCollision(playerRigidbody.gameObject.GetComponent<CircleCollider2D>(), itm, false);
            }
        }

        // :: When Mouse Right Button Clicking
        if(Input.GetMouseButton(1))
        {
            // :: Igonore all Box Collider
            foreach (var itm in GameObject.FindObjectsOfType<BoxCollider2D>())
            {
                // :: If it's not Start Block
                if(!itm.gameObject.name.Contains("Start"))
                    Physics2D.IgnoreCollision(playerRigidbody.gameObject.GetComponent<CircleCollider2D>(), itm, true);
            }
        }

        if(Input.GetMouseButtonDown(0) && jumpCount < 2)
        {
            // 점프 횟수 증가
            jumpCount++;
            // 점프 직전에 속도를 순간적으로 제로(0, 0)로 변경
            playerRigidbody.velocity = Vector2.zero;
            // 리지드 바디에 위쪽으로 힘 주기
            playerRigidbody.AddForce(new Vector2(0, jumpForce));
            // 오디오 소스 재생
            playerAudio.Play();
        } else if(Input.GetMouseButtonUp(0) && playerRigidbody.velocity.y > 0)
        {
            // 마우스 왼쪽 버튼에서 손을 떼는 순간 && 속도의 y 값이 양수라면(위로 상승 중)
            // 현재 속도를 절반으로 변경
            playerRigidbody.velocity = playerRigidbody.velocity * 0.5f;
        }

        // 애니메이터의 Grounded 파라미터를 isGrounded 값으로 갱신
        animator.SetBool("Grounded", isGrounded);
   }

   private void Die() {
        // 사망 처리
        // 애니메이터의 Die 트리거 파라미터를 셋
        animator.SetTrigger("Die");

        // 오디오 소스에 할당된 오디오 클립을 deathClip으로 변경
        playerAudio.clip = deathClip;
        // 사망 효과음 재생
        playerAudio.Play();

        // 속도를 제로(0, 0)으로 변경
        playerRigidbody.velocity = Vector2.zero;
        // 사망 상태를 true로 변경
        isDead = true;
   }

   private void OnTriggerEnter2D(Collider2D other)
    {

        Debug.LogFormat("OnTriggerEnter2D : {0}", other.gameObject.name);

        // 트리거 콜라이더를 가진 장애물과의 충돌을 감지
        if (other.tag == "Dead" && !isDead)
        {
            // 충돌한 상대방의 태그가 Dead이며 아직 사망하지 않았다면 Die() 실행
            Die();
        }
   }

   private void OnCollisionEnter2D(Collision2D collision)
    {

        Debug.LogFormat("OnCollisionEnter2D : {0} / {1}", playerRigidbody.velocity.y, collision.contacts[0].normal.y);

        // 바닥에 닿았음을 감지하는 처리
        // 어떤 콜라이더와 닿았으며, 충돌 표면이 위쪽을 보고 있으면
        if (collision.contacts[0].normal.y > 0.7f)
        {
            // isGrounded를 true로 변경하고, 누적 점프 횟수를 0으로 리셋
            isGrounded = true;
            jumpCount = 0;
        }
   }

   private void OnCollisionExit2D(Collision2D collision) {

        Debug.LogFormat("OnCollisionExit2D : {0}", collision.gameObject.name);

        // 바닥에서 벗어났음을 감지하는 처리
        // 어떤 콜라이더에서 떼어진 경우 isGrounded를 false로 변경
        isGrounded = false;
   }
}
블로그 이미지

RIsN

,

>Durationがよく作動しない感じがする?

>もっと確認が必要。

// :: Fade Out
public System.Action CallBack_FadeOut = null; // Callback delegate
public void FadeOut(Image image)
{
	image.gameObject.SetActive(true); // :: Render TRUE
	DOTween.ToAlpha(
		() => image.color,
		alpha => image.color = alpha,
		0f, // :: Target Value
		1f) // :: Duration
		.SetEase(Ease.InQuad) // :: Set Animation Play Type
		.onComplete = () =>  // :: On Complete
		{
			this.CallBack_FadeOut?.Invoke(); // :: Call Back when this Action isn't null
			this.CallBack_FadeOut = null; // :: Reset Action
            image.gameObject.SetActive(false); // :: Render FALSE
		};
}
// :: Fade In
public System.Action CallBack_FadeIn = null; // Callback delegate
public void FadeIn(Image image)
{
    image.gameObject.SetActive(true); // :: Render TRUE
    DOTween.ToAlpha(
        () => image.color,
        alpha => image.color = alpha,
        1f, // :: Target Value
        1f) // :: Duration
        .SetEase(Ease.InQuad) // :: Set Animation Play Type
        .onComplete = () =>  // :: On Complete
        {
            this.CallBack_FadeIn?.Invoke(); // :: Call Back when this Action isn't null
            this.CallBack_FadeIn = null; // :: Reset Action
            image.gameObject.SetActive(true); // :: Render TRUE
        };
}
블로그 이미지

RIsN

,

Portfolio:事前調律 #1

Unity 2020. 11. 19. 12:38

【チーム】

・システム構造設計

 

【個人:Auto Battle】

・問題になるSingletonのCallbackを外から中に変更。

・タイルマップをコードではなく、基本シーンに入れてスタートするとコードが貰う。

 

【現状況】

・一対多(動かない)の時以外にオートバトルが動かない。

 

youtu.be/SSnDyP6hfcs

 

블로그 이미지

RIsN

,

Portfolio:事前検証 #1

Unity 2020. 11. 17. 17:36

レファレンスゲーム:TFT

 

チームファイト タクティクス | リーグ・オブ・レジェンドのオートバトルゲームモード - チー

チームファイト タクティクス(TFT)は、自分で作ったチームで他の7人を相手に戦うラウンド制のストラテジーゲームです。目標はひとつ──最後の一人になるまで戦い抜くこと。

teamfighttactics.leagueoflegends.com

【現段階】

事前検証:チームプロジェクトのスタート前にどこまで作れるのかを検証中

 

【今回のテスト】

オートバトル:ゲームが始まって配置が終わったら勝手に戦うオートバトルモードの実証

youtu.be/crZgdh8fvII

【改善】

大あり、整理中。

블로그 이미지

RIsN

,

::キー入力(上下左右)で動く

youtu.be/5nzEYsQzBNw

>>改善すべき:もっと自然な数値(数式)設定?

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine;

public class HeroA : MonoBehaviour
{
    // :: Variables : Easy to Use
    private CharacterController controller;
    private Animation anim;
    // :: Enums : Animation Key
    private enum eAnimation
    {
        [Description("idle@loop")]
        IDLE,
        [Description("run@loop")]
        RUN,
        [Description("walk@loop")]
        WALK
    }


    // Start is called before the first frame update
    void Start()
    {
        // :: Easy to use for : CharacterController
        controller = this.gameObject.GetComponent<CharacterController>();
        // :: Easy to use for : Animation
        anim = this.gameObject.GetComponent<Animation>();
    }

    // :: for use enum Description
    private string GetEnumDescription(eAnimation value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());
        var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        var descriptionString = attributes.Select(ele => ele.Description).FirstOrDefault();

        if(descriptionString != null)
        {
            return descriptionString;
        }
        return value.ToString();
    }

    // Update is called once per frame
    void Update()
    {
        // :: 1.1 Input key (up, down) check
        float forwardInput = Input.GetAxisRaw("Vertical");

        // :: 2 Rotate with key (left, right) check
        float rotateInput = Input.GetAxisRaw("Horizontal");
        this.transform.Rotate(Vector3.up * rotateInput * 0.5f);

        // :: 3# Change Animation
        // ::: Run
        if (forwardInput != 0)
        {
            anim.Play(this.GetEnumDescription(eAnimation.RUN));

            // :: 1.2 Move forward and Backward
            this.controller.Move(this.transform.forward * forwardInput * Time.deltaTime);
        }
        // ::: Just Rotate
        else if(rotateInput != 0){
            anim.Play(this.GetEnumDescription(eAnimation.WALK));
        }
        // ::: No Move
        else
        {
            anim.Play(this.GetEnumDescription(eAnimation.IDLE));
        }


        //this.transform.Rotate(0, 1, 0);
        //controller.Move(this.transform.forward * 0.01f);
    }
}
블로그 이미지

RIsN

,

::武器を変える。

youtu.be/SlFWpbw6N3Q

>>改善すべき:武器変更を他のスクリプトに入れて使う?

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

public class TestApp3 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        // :: Initialise Buttons
        Button btnNoHand = GameObject.Find("Button_NoHand").GetComponent<Button>();   
        Button btnAxe = GameObject.Find("Button_Axe").GetComponent<Button>();   
        Button btnSpear = GameObject.Find("Button_Spear").GetComponent<Button>();

        // :: Create Character and Instantiate
        GameObject unit = Object.Instantiate(Resources.Load<GameObject>("ch_03_01"));

        // :: Initialise dummyRHand
        Transform dummyRHand = null;

        // :: Find DummyRHand and Remember it
        Transform[] unitChildren = unit.transform.GetComponentsInChildren<Transform>();
        foreach(var child in unitChildren)
        {
            if(child.name == "DummyRHand")
            {
                dummyRHand = child;
                break;
            }
        }

        // :: Initialise currentWeaponName
        string currentWeaponName = null;

        // :: Add Event Listener : Button No Hand
        btnNoHand.onClick.AddListener(() =>
        {
            // :: When you have weapon
            if(currentWeaponName != null)
            {
                // :: Find current weapon by name
                Transform currentWeapon = dummyRHand.transform.Find(currentWeaponName);

                // :: Release parent
                currentWeapon.transform.SetParent(null);

                // :: Destroy current weapon
                Object.Destroy(currentWeapon.gameObject);

                // :: Set Current Weapon Name : null
                currentWeaponName = null;
            }
        });

        // :: Add Event Listener : Button Axe
        btnAxe.onClick.AddListener(() =>
        {
            // :: When you don't have weapon
            if (currentWeaponName == null)
            {
                // :: Resources Load and Set Parent
                var currentWeapon = Object.Instantiate<GameObject>(Resources.Load<GameObject>("Axe_3"));
                currentWeapon.transform.SetParent(dummyRHand, false);

                // :: Set Current Weapon Name
                currentWeaponName = currentWeapon.name;
            }
        });

        // :: Add Event Listener : Button Spear
        btnSpear.onClick.AddListener(() =>
        {
            // :: When you don't have weapon
            if (currentWeaponName == null)
            {
                // :: Resources Load and Set Parent
                var currentWeapon = Object.Instantiate<GameObject>(Resources.Load<GameObject>("Spear_7"));
                currentWeapon.transform.SetParent(dummyRHand);
                currentWeapon.transform.localPosition = Vector3.zero;
                currentWeapon.transform.localRotation = Quaternion.Euler(Vector3.zero);

                // :: Set Current Weapon Name
                currentWeaponName = currentWeapon.name;
            }
        });

    }
}
블로그 이미지

RIsN

,

::目標たちを辿って宝箱を開けてみよう。

:2020-10-29

youtu.be/taYBOVJ_OHg

>>改善すべき:関数を分けるより、一つのログを作ってそれに従うようにしよう。

>>疑問点:キュー(Queue)を使ってみたが方向性が違った感じがする。

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

public class App : MonoBehaviour
{
    // Start is called before the first frame update
    Queue<GameObject> targets;
    GameObject beforeTarget;
    GameObject chestClosed;
    GameObject chestOpened;
    GameObject lastTarget;
    void Start()
    {
        // :: Load Targets with name
        targets = new Queue<GameObject>();
        for(int i = 1; i <= 3; i++)
        {
            targets.Enqueue(GameObject.Find("Target" + i));

            if(i == 3)
            {
                lastTarget = GameObject.Find("Target" + i);
            }
        }
        Debug.Log(targets.Count);

        // :: Initialise Chest
        GameObject chestPrefabA = Resources.Load<GameObject>("chest_close");
        GameObject chectPrefabB = Resources.Load<GameObject>("chest_open");

        // :: Render Chest Closed and Set Position
        chestClosed = Object.Instantiate<GameObject>(chestPrefabA);
        chestClosed.transform.position = lastTarget.transform.position + new Vector3(0, 0f, 0.5f);
        chestClosed.transform.eulerAngles = new Vector3(-90, -90, 0);

        // :: Render Chest Open and Set Position and Set Active false;
        chestOpened = Object.Instantiate<GameObject>(chectPrefabB);
        chestOpened.transform.position = chestClosed.transform.position;
        chestOpened.transform.eulerAngles = chestClosed.transform.eulerAngles;
        chestOpened.SetActive(false);

        // :: Initialise beforeTarget
        beforeTarget = new GameObject();

        // :: Button Load and Attach OnClick Function : Create
        Button BUTTON_create = GameObject.Find("Button_Create").GetComponent<Button>();
        BUTTON_create.onClick.AddListener(() => OnClick());

        // :: Button Load and Attach OnClick Function : GoAndTurn
        Button BUTTON_goAndTurn = GameObject.Find("Button_GoAndTurn").GetComponent<Button>();
        BUTTON_goAndTurn.onClick.AddListener(() => { RunToTarget(); });
    }

    // :: Run to Target
    void RunToTarget(float speed = 0.5f)
    {   
        // :: If you have remain targets
        if(targets.Count > 0)
        {
            // :: Go Target
            beforeTarget = targets.Peek();
            unit.GetComponent<Unit>().RunToTarget(targets.Dequeue(), speed);
        }
    }
    // :: Go Target
    void WalkToTarget(float speed = 0.5f)
    {
        // :: If you have remain targets
        if (targets.Count > 0)
        {
            // :: Go Target
            beforeTarget = targets.Peek();
            unit.GetComponent<Unit>().WalkToTarget(targets.Dequeue(), speed);
        }
    }

    // :: Go Complete
    void GoComplete()
    {
        // :: Target Remains
        if(targets.Count > 0)
        {
            // :: When arrived target1
            if (targets.Peek().name.Contains("2"))
            {
                // :: Look at target forward
                unit.GetComponent<Unit>().LookTarget(beforeTarget.transform.forward);
                unit.GetComponent<Unit>().WaitNow();
            }
            else
            {
                // :: Walk To Target
                this.WalkToTarget();
            }
        } else
        {
            Debug.Log("You've got treasure!");
            chestClosed.SetActive(false);
            chestOpened.SetActive(true);
            
        }
    }

    // :: Wait Complete
    void WaitComplete()
    {
        Debug.Log("wait is Over!");

        // :: Walk To Target
        this.WalkToTarget();
    }

    // :: OnClick Function
    // ::: for Variables
    GameObject unit; // :: Unit
    void OnClick()
    {
        // :: Unit Resources Load & Instantiate
        unit = Instantiate<GameObject>(Resources.Load<GameObject>("ch_03_01"));
        unit.AddComponent<Unit>().goComplete = () => { GoComplete(); };
        unit.GetComponent<Unit>().waitComplete = () => { WaitComplete(); };
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Unit : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // :: Look at direction of Target
    public void LookTarget(Vector3 direction)
    {
        this.gameObject.transform.LookAt(direction);
    }

    // :: Go and Turn with Target
    // ::: for Variables

    // :: Run To Target Function
    // ::: for Variables
    bool isGo = false;
    string animationName_Run = "run@loop";
    string animationName_Idle = "idle@loop";
    GameObject target; // ::: target
    float speed = 0.5f; // ::: Move Speed
    public void RunToTarget(GameObject target, float speed = 0.5f)
    {
        // :: Remember Target
        this.target = target;

        // :: Remember Speed
        this.speed = speed;

        // :: Look at Target
        this.LookTarget(this.target.transform.position);

        // :: Animation Run
        this.gameObject.GetComponent<Animation>().Play(animationName_Run);

        // :: Start Update
        isGo = true;
    }

    // :: Walk To Target Function
    string animationName_Walk = "walk@loop";
    public void WalkToTarget(GameObject target, float speed = 0.05f)
    {
        // :: Remember Target
        this.target = target;

        // :: Remember Speed
        this.speed = speed;

        // :: Look at Target
        this.LookTarget(this.target.transform.position);

        // :: Animation Run
        this.gameObject.GetComponent<Animation>().Play(animationName_Walk);

        // :: Start Update
        isGo = true;
    }

    // :: Wait Function
    float waitTime;
    bool isWait = false;
    float elapsedTime = 0f;
    public CheckComplete waitComplete = () => { };
    public void WaitNow(float waitTime = 3f)
    {
        // :: Remember Wait Time
        this.waitTime = waitTime;

        // :: Start Update
        isWait = true;
    }

    // Update is called once per frame
    // ::: for Variables
    public delegate void CheckComplete();
    public CheckComplete goComplete = () => { };
    void Update()
    {
        // :: Start Go
        if(isGo)
        {
            // :: Go Toward
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, this.target.transform.position, speed * Time.deltaTime);

            // :: Check Distance
            float distance = Vector3.Distance(this.gameObject.transform.position, this.target.transform.position);

            // :: When near the Cube
            if(distance <= 0.2f)
            {
                // :: Stop Go
                isGo = false;

                // :: Change Animation
                this.gameObject.GetComponent<Animation>().Play(animationName_Idle);

                // :: Send Move Complete
                this.goComplete();
            }
        }
        // :: Start Wait
        else if(isWait)
        {
            // :: Check Elapsed Time
            elapsedTime += Time.deltaTime;
            Debug.Log(elapsedTime);

            // :: When Wait is Over
            if(elapsedTime >= waitTime)
            {
                // :: Stop Wait
                isWait = false;

                // :: Send Wait Complete
                this.waitComplete();
            }
        }
    }
}
블로그 이미지

RIsN

,