EasyCastleUNITY

간단 RPG 본문

유니티 기초

간단 RPG

EasyCastleT 2023. 8. 11. 18:20

GameScene

HeroGenerator

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


public class HeroGenerator : MonoBehaviour
{
    [SerializeField]
    private GameObject heroPrefab;
    // Start is called before the first frame update
    void Start()
    {

    }

    public HeroController Generate(Vector3 initPosition)
    {
        GameObject prefab = this.heroPrefab;
        //프리팹 인스턴스를 생성
        GameObject go = Instantiate(prefab);
        //위치 설정
        go.transform.position = initPosition;

        return go.GetComponent<HeroController>();
    }
}

ItemGenerator

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

public class ItemGenerator : MonoBehaviour
{
    [SerializeField]
    private List<GameObject> prefabList; //동적배열 (사용전 반드시 인스턴스화)
                                         // Start is called before the first frame update
    void Start()
    {
        foreach (GameObject prefab in prefabList)
        {
            Debug.LogFormat("Item prefab :{0}", prefab.name);
        }
    }
    /// <summary>
    /// 아이템 생성
    /// </summary>
    /// <param name="itemType">생성하려고 하는 아이템의 타입</param>
    /// <param name="initPosition">생성한 아이템의 초기 월드 좌표</param>
    /// <returns></returns>
    public ItemController Generate(GameEnums.eItemType itemType, Vector3 initPosition)
    {
        Debug.LogFormat("itemType :{0}", itemType);

        int index = (int)itemType;

        Debug.LogFormat("index: {0}", index);
        GameObject prefab = this.prefabList[index];
        Debug.LogFormat("Item prefab: {0}", this.prefabList[index].name);
        //프리팹 인스턴스 생성
        GameObject go = Instantiate(prefab);
        go.transform.position = initPosition;

        return go.GetComponent<ItemController>();

    }

}

MonsterGenerator

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;


public class MonsterGenerator : MonoBehaviour
{
    //[SerializeField] private GameObject turtlePrefab;
    //[SerializeField] private GameObject slimePrefab;
    [SerializeField]
    private List<GameObject> prefabList; //동적배열 (사용전 반드시 인스턴스화)
                                         // Start is called before the first frame update
    void Start()
    {
        foreach (GameObject prefab in prefabList)
        {
            Debug.LogFormat("Monster prefab : {0}", prefab.name);
        }
    }

    /// <summary>
    /// 몬스터 생성
    /// </summary>
    /// <param name="monsterType">생성 하려고 하는 몬스터의 타입</param>
    /// <param name="initPosition">생성된 몬스터의 초기 월드좌표</param>
    public MonsterController Generate(GameEnums.eMonsterType monsterType, Vector3 initPosition)
    {
        Debug.LogFormat("monsterType :{0}", monsterType);
        //몬스터 타입에 따라 어떤 프리팹으로 프리팹 복사본(인스턴스)를 생성할지 결정 해야 함

        int index = (int)monsterType;

        Debug.LogFormat("index: {0}", index);

        GameObject prefab = this.prefabList[index];
        Debug.LogFormat("Monsterprefab: {0}", this.prefabList[index].name);
        //프리팹 인스턴스를 생성
        GameObject go = Instantiate(prefab);
        //MonsterController가 부착되어 있지 않다면 
        //if (go.GetComponent<MonsterController>() == null)
        //{
        //    //동적으로 컴포넌트를 부착 할수 있음
        //    MonsterController controller = go.AddComponent<MonsterController>();
        //}
        //위치 설정
        go.transform.position = initPosition;

        return go.GetComponent<MonsterController>();


    }
}

MonsterController

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

public class MonsterController : MonoBehaviour
{
    [SerializeField]
    private GameEnums.eItemType type;
    [SerializeField]
    private float radius = 1.0f; //몬스터의 사거리 
    [SerializeField]
    private int hp = 10;

    private Animator anim; //몬스터 애니메이터
    private GameEnums.eMonsterState monsterState; //enum 
    private float hitTime = 0.01f; //애니메이션 실행 시간 

    public System.Action onHit; //몬스터가 hit되면 메서드를 넘기는 대리자 
    public System.Action<GameEnums.eItemType> onDie; //몬스터가 죽으면 메서드를 넘기는 대리자

    public float Radius //사거리 속성
    {
        get { return this.radius; }    
    }

    public int Hp //hp 속성 
    {
        get { return this.hp; }
    }
    // Start is called before the first frame update
    void Awake()
    {
       this.anim=GetComponent<Animator>();
    }

    public void Die()
    {
        StartCoroutine(this.CoDie());
    }

    private IEnumerator CoDie()
    {
        Debug.Log("!");
        this.PlayAnimation(GameEnums.eMonsterState.Die);
        yield return new WaitForSeconds(2.0f);
        this.onDie(this.type);
    }
    
    public void HitDamage(int damage)
    {
        //피격애니메이션을 1회 실행
        //this.PlayAnimation(eMonsterState.GetHit);
        if(this.hp>0)
        this.PlayAnimation(GameEnums.eMonsterState.GetHit);
        this.hp -= damage;
        this.onHit();
        if (this.hp <= 0)
        {
            this.hp = 0;
            this.anim.SetInteger("MonsterState",(int)GameEnums.eMonsterState.Die);
            this.Die();

        }
    }

    private void PlayAnimation(GameEnums.eMonsterState monsterState)
    {
        //중복막기
        if (this.monsterState != monsterState)
        {
            Debug.LogFormat("Monster: {0} -> {1}", this.monsterState, monsterState);
            this.monsterState = monsterState;
            this.anim.SetInteger("MonsterState", (int)monsterState);

            switch (monsterState)
            {
                case GameEnums.eMonsterState.GetHit:

                    this.StartCoroutine(this.WaitForCompleteGetHitAnimation());
                    break;
            }
        }
        else
        {
            Debug.LogFormat("{0}은 현재와 동일한 상태입니다.", monsterState);
        }
    }

    //코루틴
    private IEnumerator WaitForCompleteGetHitAnimation()
    {
        yield return null;

        Debug.Log("피격 애니메이션이 끝날때까지 기다림");
        AnimatorStateInfo animStateInfo = this.anim.GetCurrentAnimatorStateInfo(0);
        //실제 애니메이션 이름으로 찾아야 한다
        bool isGetHit = animStateInfo.IsName("GetHit");
        Debug.LogFormat("isGetHit: {0}", isGetHit);
        if (isGetHit)
        {
            Debug.LogFormat("Monster's animStateInfo.length: {0}", animStateInfo.length);
        }
        else
        {
            Debug.LogFormat("GetHit State가 아닙니다");
        }
        yield return new WaitForSeconds(this.hitTime);
        Debug.Log("<color=red>Hit!!!!!</color>");

        yield return new WaitForSeconds(animStateInfo.length - this.hitTime);
        
        this.PlayAnimation(GameEnums.eMonsterState.Idle);

    }

    //이벤트 함수 
    private void OnDrawGizmos()
    {
        GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, 1, 40);
    }
}

HeroController

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

//Game
public class HeroController : MonoBehaviour
{
    //이동 구현을 코루틴으로 함
    private Vector3 targetPosition; //타겟이 되는 위치 
    private Coroutine moveRoutine;  //전에 실행하던 코루틴을 저장하는 변수
    private Animator anim; //영웅의 애니메이터 
    [SerializeField]
    private float radius = 1.0f; //영웅의 사거리 

    private MonsterController monsterTarget; //target이 된 몬스터의 MonsterController 컴포넌트
    private ItemController itemTarget; //target이 된 아이템의 ItemController 컴포넌트

    public System.Action<MonsterController> onMoveComplete; //이동이 끝나면 호출하는 대리자 
    private GameEnums.eHeroState state; //영웅 애니메이션 상태 
    private List<ItemController> getItems; //영웅이 획득한 아이템들
    private float impactTime = 0.399f; //실제로 영웅의 애니메이션이 공격하는 시간 

    private int hp = 150; //영웅의 Hp
    private int damage = 1;
    private int armor = 1;
    private int index = 0; 

    public int Hp
    {
        get { return hp; }
    }

    public int Damage
    {
        get { return this.damage; }
    }

    public int Armor
    {
        get { return this.armor; }
    }

    public float Radius //사거리 속성
    {
        get
        {
            return this.radius;
        }
    }
    // Start is called before the first frame update
    void Start()
    {
        this.getItems = new List<ItemController>();
        this.anim = this.GetComponent<Animator>();
        Debug.LogFormat("<color=lime>영웅의 체력: {0}</color>", this.hp);
    }
    //목적지가 몬스터가 아닐 때 하는 움직임
    public void Move(Vector3 targetPosition)
    {
        //타겟을 지움
        this.monsterTarget = null;
        this.itemTarget = null;

        //이동할 목표 지점을 저장
        this.targetPosition = targetPosition;
        Debug.Log("Move");

        //이동 애니메이션 실행
        this.PlayAnimation(GameEnums.eHeroState.Run);

        if (this.moveRoutine != null)
        {
            //이미 코루틴이 실행중이다 -> 중지
            this.StopCoroutine(this.moveRoutine);
        }
        this.moveRoutine = StartCoroutine(this.CoMove());

    }
    //목적지가 몬스터이면 하는 움직임 
    public void Move(MonsterController target)
    {
        this.monsterTarget = target;
        this.targetPosition = this.monsterTarget.gameObject.transform.position;
        this.PlayAnimation(GameEnums.eHeroState.Run);
        if (this.moveRoutine != null)
        {
            //이미 코루틴이 실행중이다 -> 중지 
            this.StopCoroutine(this.moveRoutine);
        }
        this.moveRoutine = this.StartCoroutine(this.CoMove());
    }
    //목적지가 아이템이면 하는 움직임 
    public void Move(ItemController target)
    {
        this.itemTarget = target;
        this.targetPosition = this.itemTarget.gameObject.transform.position;
        this.anim.SetInteger("State", 1);
        if (this.moveRoutine != null)
        {
            //이미 코루틴이 실행중이다 -> 중지 
            this.StopCoroutine(this.moveRoutine);
        }
        this.moveRoutine = this.StartCoroutine(this.CoMove());
    }
    private IEnumerator CoMove()
    {
        while (true)
        //무한 반복이 되어 유니티가 멈출 수도 있음
        //그러므로 yield return 필수
        {
            Vector3 pos = new Vector3(targetPosition.x, 0, targetPosition.z);
            //방향을 바라봄
            this.transform.LookAt(pos);
            //이미 바라봤으니깐 정면으로 이동 (relateTo: Self/지역좌표)
            //방향 * 속도 * 시간 
            this.transform.Translate(Vector3.forward * 2f * Time.deltaTime);
            //목표지점과 나 사이의 거리를 계산, 즉 1프레임 마다 거리를 계산 
            float distance = Vector3.Distance(this.transform.position, this.targetPosition);
            //타겟이 있을 경우
            if (this.monsterTarget != null)
            {
                if (distance <= (1f + 1f))
                {
                    break;
                }
            }
            else if (this.itemTarget != null)
            {
                if (distance <= 0.5f)
                {
                    Debug.Log("<color=lime>item log</color>");
                    break;
                }
            }
            else
            {
                if (distance <= 0.1f)
                {
                    //도착
                    break;
                }
            }
            yield return null; //다음 프레임 시작
        }
        Debug.Log("<color=yellow>도착!</color>");
        this.PlayAnimation(GameEnums.eHeroState.Idle);
        //대리자 호출
        this.onMoveComplete(this.monsterTarget);
    }
    public void Attack(MonsterController target)
    {
        this.transform.LookAt(target.transform.position);
        this.monsterTarget = target;
        //공격애니메이션을 1회 실행
        this.PlayAnimation(GameEnums.eHeroState.Attack);
        //Debug.LogError("Stop");
    }

    private void PlayAnimation(GameEnums.eHeroState state)
    {
        //this.state : prev 현재, state: current: 미래
        //중복막기
        if (this.state != state)
        {
            Debug.LogFormat("State: {0} -> {1}", this.state, state);
            this.state = state;
            this.anim.SetInteger("State", (int)state);

            switch (state)
            {
                case GameEnums.eHeroState.Attack:

                    this.StartCoroutine(this.WaitForCompleteAttackAnimation());
                    break;
            }
        }
        else
        {
            Debug.LogFormat("{0}은 현재와 동일한 상태입니다.", state);
        }


    }
    //코루틴 함수
    //Update와 동일하게 동작 가능 
    private IEnumerator WaitForCompleteAttackAnimation()
    {
        yield return null; //1프레임 건너뜀

        Debug.Log("공격 애니메이션이 끝날때까지 기다림");
        AnimatorStateInfo animStateInfo = this.anim.GetCurrentAnimatorStateInfo(0);
        //실제 애니메이션 이름으로 찾아야 한다
        bool isAttackState = animStateInfo.IsName("Attack01");
        Debug.LogFormat("isAttackState: {0}", isAttackState);
        if (isAttackState)
        {
            Debug.LogFormat("Hero's animStateInfo.length: {0}", animStateInfo.length);
        }
        else
        {
            Debug.LogFormat("Attack State가 아닙니다");
        }

        yield return new WaitForSeconds(this.impactTime);
        Debug.Log("<color=red>Impact!!!!</color>");
        //대상에게 피해를 입힘
        this.monsterTarget.HitDamage(this.damage);

        yield return new WaitForSeconds(animStateInfo.length - this.impactTime);
        //Idle 애니메이션을 함
        //this.anim.SetInteger("State", 0);
        this.PlayAnimation(GameEnums.eHeroState.Idle);
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Item")
        {
            this.GetItem(other);
        }
        if (other.gameObject.tag == "Portal")
        {
            GameObject.Find("GameMain").GetComponent<GameMain>().isHeroPortalIn=true;
        }
    }

    private void GetItem(Collider other)
    {
        GameEnums.eItemType itemType = other.gameObject.GetComponent<ItemController>().ItemType;
        this.getItems.Add(other.GetComponent<ItemController>());
        Debug.LogFormat("<color=lime>{0}을 획득</color>", other.gameObject.name);
        
        if (itemType == GameEnums.eItemType.Sword)
        {
            InfoManager.instance.SwordType = itemType;
            this.damage += 10;
            Debug.LogFormat("영웅의 현재 공격력: {0}", this.damage);

            Transform weaponChild = GameObject.Find("Weapon").transform;
            Vector3 weaponPos = weaponChild.GetChild(0).gameObject.transform.position;

            Destroy(weaponChild.GetChild(0).gameObject); //전에 장착하고 있던 검 삭제
            other.gameObject.transform.SetParent(weaponChild, false);

            other.gameObject.transform.localPosition = Vector3.zero; //위치 초기화
            Destroy(other.gameObject.GetComponent<BoxCollider>());
        }
        else if (itemType == GameEnums.eItemType.Shield)
        {
            InfoManager.instance.ShieldType = itemType;
            this.armor += 10;
            Debug.LogFormat("영웅의 현재 방어력 :{0}", this.armor);

            Transform shieldChild = GameObject.Find("Shield").transform;
            Vector3 shieldPos = shieldChild.GetChild(0).gameObject.transform.position;

            Destroy(shieldChild.GetChild(0).gameObject); //전에 장착하고 있던 방패 삭제
            other.gameObject.transform.SetParent(shieldChild, false);

            other.gameObject.transform.localPosition = Vector3.zero; //위치 초기화
            Destroy(other.gameObject.GetComponent<BoxCollider>());
        }
        else if (itemType == GameEnums.eItemType.Potion)
        {
            Destroy(other.gameObject);
            this.hp += 50;
            Debug.Log("Health Potion 획득");
            Debug.LogFormat("<color=lime>영웅의 Hp: {0} </color>", this.hp);
        }
        //other.gameObject.transform.SetParent(this.transform);
        Debug.LogFormat("getItemList: {0}", getItems[index].name);
        index++;
    }
    //이벤트 함수
    private void OnDrawGizmos()
    {
        GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, 1, 40);
    }
}

ItemController

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

public class ItemController : MonoBehaviour
{
    [SerializeField]
    private GameEnums.eItemType itemType;
    public GameEnums.eItemType ItemType
    {
        get { return this.itemType; }
    }
}

GameEnums

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

public class GameEnums
{
    public enum eMonsterType
    {
        Turtle, Slime, Chest
    }

    public enum eItemType
    {
        Shield, Sword, Potion
    }

    public enum eMonsterState
    {
        Idle, GetHit, Die
    }

    public enum eHeroState
    {
        Idle, Run, Attack
    }
}

GameMain

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

public class GameMain : MonoBehaviour
{
    [SerializeField]
    private MonsterGenerator monsterGenerator; //몬스터 생성기
    [SerializeField] 
    private ItemGenerator itemGenerator; //아이템 생성기
    [SerializeField]
    private HeroGenerator heroGenerator; //영웅 생성기 

    [SerializeField]
    private GameObject hitFxPrefab; //피격 이펙트 

    [SerializeField]
    private GameObject portalPrefab; //포탈 프리팹
    [SerializeField] 
    private GameObject portalFxPrefab; //포탈 이펙트 

    private List<MonsterController> monsterList;
    private List<ItemController> itemList;
    private HeroController heroController;

    private MonsterController turtle;
    private MonsterController slime;
    private MonsterController chest;

    [SerializeField]
    private GameObject FadeOut;

    [SerializeField]
    private GameObject TurtleHp;
    [SerializeField]
    private GameObject ChestHp;
    [SerializeField]
    private GameObject SlimeHp;

    [SerializeField]
    private GameObject HeroHp;
    [SerializeField]
    private GameObject HeroAttack;
    [SerializeField]
    private GameObject HeroArmor;

    public int monsterCount;
    private bool isCreate = false;
    private bool PortalisNotCreate = true;
    private bool HeroisNotCreate = true;

    public bool isHeroPortalIn = false;

    RaycastHit hit;
    // Start is called before the first frame update
    void Start()
    {
        //컬렉션 사용전 반드시 초기화
        this.monsterList = new List<MonsterController>();
        this.itemList = new List<ItemController>();
        //영웅 동적 생성
        this.CreateHero();
        //몬스터 동적 생성 
        this.turtle = this.monsterGenerator.Generate(GameEnums.eMonsterType.Turtle
                , new Vector3(-3, 0, 0));

        Debug.LogFormat("=> {0}", this.turtle);


        this.turtle.onDie = (type) => {
            this.CreateDropItem(type,turtle.transform.position);
            this.monsterList.Remove(this.turtle);
            Destroy(this.turtle.gameObject);
        };
        this.slime = this.monsterGenerator.Generate(GameEnums.eMonsterType.Slime
            , new Vector3(0, 0, 3));
        this.slime.onDie = (type) => {
            this.CreateDropItem(type,slime.transform.position);
            this.monsterList.Remove(this.slime);
            Destroy(this.slime.gameObject);
        };
        this.chest = this.monsterGenerator.Generate(GameEnums.eMonsterType.Chest,
            new Vector3(-3, 0, 3));
        this.chest.onDie = (type) => {
            this.CreateDropItem(type,chest.transform.position);
            this.monsterList.Remove(this.chest);
            Destroy(this.chest.gameObject);
        };
        
        //만들어진 객체들을 그룹화 관리 
        //동적 배열 
        this.monsterList.Add(this.turtle);
        this.monsterList.Add(this.slime);
        this.monsterList.Add(this.chest);
        Debug.LogFormat("this.monsterList.Count:{0}", this.monsterList.Count);
        //리스트의 요소 출력
        foreach (MonsterController monster in this.monsterList)
        {
            Debug.LogFormat("monster:{0}", monster.name);
        }
        Debug.LogFormat("monsterCount: {0}", monsterCount);
        //영웅 이동에 대한 대리자 익명 메서드 
        this.heroController.onMoveComplete = (target) =>
        {
            Debug.Log("<color=cyan>이동을 완료 했습니다.</color>");
            //타겟이 있다면 공격 애니메이션 실행
            if (target != null )
            {
                this.heroController.Attack(target);
            }
        };
        //몬스터가 공격을 받으면 실행하는 익명 메서드
        turtle.onHit = () => {
            Debug.Log("이펙트 생성");
            Vector3 offset = new Vector3(0, 0.5f, 0);
            Vector3 tpos = turtle.transform.position + offset;

            Debug.LogFormat("이펙트 생성위치: {0}", tpos);
            //프리팹 인스턴스 생성
            GameObject fxGo = Instantiate(this.hitFxPrefab);
            //위치 설정
            fxGo.transform.position = tpos;
            //파티클 실행
            fxGo.GetComponent<ParticleSystem>().Play();
        };
        slime.onHit = () => {
            Debug.Log("이펙트 생성");
            Vector3 offset = new Vector3(0, 0.5f, 0);
            Vector3 tpos = slime.transform.position + offset;

            Debug.LogFormat("이펙트 생성위치: {0}", tpos);
            //프리팹 인스턴스 생성
            GameObject fxGo = Instantiate(this.hitFxPrefab);
            //위치 설정
            fxGo.transform.position = tpos;
            //파티클 실행
            fxGo.GetComponent<ParticleSystem>().Play();
        };
        chest.onHit = () => {
            Debug.Log("이펙트 생성");
            Vector3 offset = new Vector3(0, 0.5f, 0);
            Vector3 tpos = chest.transform.position + offset;

            Debug.LogFormat("이펙트 생성위치: {0}", tpos);
            //프리팹 인스턴스 생성
            GameObject fxGo = Instantiate(this.hitFxPrefab);
            //위치 설정
            fxGo.transform.position = tpos;
            //파티클 실행
            fxGo.GetComponent<ParticleSystem>().Play();
        };
    }

    private void CreateDropItem(GameEnums.eItemType type,Vector3 position)
    {
        //드롭 아이템 생성
        Vector3 itemPos = position;
        ItemController dropItem = this.itemGenerator.Generate(type, itemPos);
        this.itemList.Add(dropItem);
        Debug.LogFormat("<color=yellow>드롭 아이템 {0} 생성</color>", dropItem.name);
    }
    private void CreateHero()
    {
        int posX = Random.Range(-4, 5);
        int posZ = Random.Range(-4, 5);

        //히어로 동적 생성
        this.heroController = this.heroGenerator.Generate(new Vector3(posX, 0.0f, posZ));
    }

    private void CreatePortal()
    {
        if (this.monsterList.Count <= 0 && this.PortalisNotCreate == true)
        {
            Debug.Log("<color=lime>포탈이 생성됩니다!</color>");
            GameObject portalGo = Instantiate(portalPrefab);
            int posX = Random.Range(-3, 3);
            int posZ = Random.Range(-3, 3);
            portalGo.transform.position = new Vector3(posX, 0, posZ);
            GameObject portalFx = Instantiate(portalFxPrefab);
            portalFx.transform.position = portalGo.transform.position;
            this.PortalisNotCreate = false;
        }
    }

    private IEnumerator CoPortalCreate()
    {
        yield return new WaitForSeconds(2.0f);
        this.CreatePortal();
    }

    private IEnumerator CoLoadScene()
    {
        yield return new WaitForSeconds(2.0f);
        SceneManager.LoadScene("BossScene");

    }
    //사거리 계산
    private bool isWithinRange(float distance, float radius)
    {
        return radius >= distance; //true
    }

    // Update is called once per frame
    void Update()
    {
        //화면을 클릭하면 클릭한 위치로 영웅이 이동
        if(Input.GetMouseButtonDown(0))
        {
            //Ray 생성
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            float maxDistance = 1000f; //Ray의 최고 길이
            //화면에 Ray를 출력
            Debug.DrawRay(ray.origin, ray.direction * maxDistance , Color.yellow ,2f);
            //충돌검사 

            if(Physics.Raycast(ray,out hit, maxDistance))
            {
                //클릭한 오브젝트가 몬스터라면
                if (hit.collider.tag == "Monster")
                {
                    //거리를 구한다
                    float distance = Vector3.Distance(this.heroController.gameObject.transform.position,
                        hit.collider.gameObject.transform.position);

                    MonsterController monsterController =
                        hit.collider.gameObject.GetComponent<MonsterController>();
                    //각 반지름 더한거와 비교
                    float sumRadius = this.heroController.Radius + monsterController.Radius;

                    Debug.LogFormat("<color=green>distance:{0}, sumRadius:{1}</color>",
                        distance, sumRadius);
                    //사거리 안에 들어옴
                    if (distance <= sumRadius)
                    {
                        //공격

                    }
                    else
                    {
                        //이동
                        this.heroController.Move(monsterController);
                    }
                }
                else if (hit.collider.tag == "Ground")
                {
                    //충돌정보가 hit 변수에 담김
                    Debug.Log(hit.point); //월드 상의 충돌 지점 위치 
                                          //Hero Gameobject 이동
                    this.heroController.Move(hit.point);
                }
                else if(hit.collider.tag == "Item")
                {
                    Debug.Log(hit.point);
                    this.heroController.Move(hit.collider.gameObject.GetComponent<ItemController>());
                }
                else if(hit.collider.tag == "Portal")
                {
                    Debug.Log("포탈로 이동합니다");
                    this.heroController.Move(hit.point);
                }
            }
        }

        if(isHeroPortalIn == true)
        {
            this.FadeOut.SetActive(true); //활성화되고 BossScene 전환
        }
        Text text1 = TurtleHp.GetComponent<Text>();
        Text text2 = SlimeHp.GetComponent<Text>();
        Text text3 = ChestHp.GetComponent<Text>();

        Text textHp = HeroHp.GetComponent<Text>();
        Text textAttack = HeroAttack.GetComponent<Text>();
        Text textArmor = HeroArmor.GetComponent<Text>();

        if (this.turtle != null)
        {
            text1.text = string.Format("TurtleHp: {0}", this.turtle.Hp);
        }
        else
        {
            text1.text = string.Format("TurtleHp: 0");
        }
        if(this.slime != null)
        {
            text2.text = string.Format("SlimeHp: {0}", this.slime.Hp);
        }
        else
        {
            text2.text = string.Format("SlimeHp: 0");
        }
        if(this.chest != null)
        {
            text3.text = string.Format("ChestHp: {0}", this.chest.Hp);
        }
        else
        {
            text3.text = string.Format("ChestHp: 0");
        }
        textHp.text = string.Format("HeroHp:{0}", this.heroController.Hp);
        textAttack.text = string.Format("HeroAttack:{0}", this.heroController.Damage);
        textArmor.text = string.Format("HeroArmor:{0}",this.heroController.Armor);
        StartCoroutine(this.CoPortalCreate());
    }
}

InfoManager

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

public class InfoManager
{
    public static readonly InfoManager instance = new InfoManager();

    private GameEnums.eItemType swordType;
    private GameEnums.eItemType shieldType;

    public GameEnums.eItemType SwordType
    {
        get { return swordType; }
        set { swordType = value; }
    }

    public GameEnums.eItemType ShieldType
    {
        get { return shieldType; }
        set { shieldType = value; }
    }

    private InfoManager()
    {

    }
}

Fade In/Out

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

public class FadeInMain : MonoBehaviour
{
    [SerializeField]
    private GameObject BossMain;

    [SerializeField]
    private Image dim;

    private System.Action onFadeInComplete;

     void Start()
    {
        this.onFadeInComplete = () => {
            Debug.Log("FadeIn complete!");
            //Boss Main을 활성화
            //this.BossMain.SetActive(true);
        };
        this.StartCoroutine(this.FadeIn());
    }

    private IEnumerator FadeIn()
    {
        Color color = this.dim.color;

        while (true)
        {
            color.a -= 0.01f;
            this.dim.color = color;

            Debug.Log(this.dim.color.a);
            if(this.dim.color.a <= 0)
            {
                break;
            }
            yield return null;
        }
        Debug.LogFormat("fadeout complete!");

        this.onFadeInComplete();   //대리자 호출 
    }
}

 

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

public class FadeOutMain : MonoBehaviour
{
    [SerializeField]
    private Image dim;

    private System.Action onFadeOutComplete;
    // Start is called before the first frame update
    void Start()
    {
        this.onFadeOutComplete = () => {
            SceneManager.LoadScene("BossScene");
        };
        this.StartCoroutine(FadeOut());
    }

    private IEnumerator FadeOut()
    {
        Color color = this.dim.color;

        //어두어짐
        while (true)
        {
            color.a += 0.01f;
            this.dim.color = color;

            Debug.Log(this.dim.color.a);

            if(this.dim.color.a >= 1)
            {
                break;
            }
            yield return null;
        }
        Debug.LogFormat("fadeout complete!");

        this.onFadeOutComplete();
    }
}

BossScene

BossSceneMain

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

public class BossSceneMain : MonoBehaviour
{
    [SerializeField]
    private PortalGenerator portalGenerator;
    [SerializeField]
    private HeroGenerator heroGenerator;
    // Start is called before the first frame update
    void Start()
    {
        //포탈 동적 생성
        GameObject portal = this.portalGenerator.Generate(Vector3.zero); 
        //영웅 동적 생성
        HeroController heroController =this.heroGenerator.Generate(Vector3.zero);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

PortalGenerator

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

public class PortalGenerator : MonoBehaviour
{
    [SerializeField]
    private GameObject portalPrefab;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    public GameObject Generate(Vector3 initPosition)
    {
        GameObject prefab = this.portalPrefab;
        //프리팹 인스턴스를 생성
        GameObject go = Instantiate(prefab);
        //위치 설정
        go.transform.position = initPosition;

        return go;
    }
}