3D 콘텐츠 제작

zombero- boss

노재두내 2023. 10. 10. 10:46
 public enum eState
    {
        IDLE = 0,
        TRACE,
        CLOSE_ATTACK,    //근거리 공격 
        LONG_ATTACK,  //원거리 공격
        DIE,
    }
    
    [SerializeField]
    private float closeAttackRange = 2.0f;
    [SerializeField]
    private float traceRange = 30.0f;
    [SerializeField]
    private float longAttackRange = 10.0f;
    
    private readonly int hashState = Animator.StringToHash("State");
    
    private Player target;
    
    
    private void OnDrawGizmos()
    {
        if (state == eState.TRACE)
        {
            Gizmos.color = Color.blue;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.traceRange);
        }
        else if (state == eState.CLOSE_ATTACK)
        {
            Gizmos.color = Color.red;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.closeAttackRange);
        }
        else if (state == eState.LONG_ATTACK)
        {
            Gizmos.color = Color.yellow;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.longAttackRange);
        }
    }

오늘 할 거

보스 총알에 맞으면 색이 변하기

원거리 공격하기

보스 공격에 맞으면 플레이어 색 변경 ?

총알 오브젝트 풀링

 

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;

public class BossController : MonoBehaviour
{
    public enum eState
    {
        IDLE,
        TRACE,
        CLOSE_ATTACK,    //근거리 공격 
        DIE,
        LONG_ATTACK  //원거리 공격 
    }
    public eState state;
    [SerializeField]
    private float attackRange = 2.0f;
    [SerializeField]
    private float traceRange = 10.0f;
    private float monsterHp = 100.0f;
    public bool isDie;
    private Animator anim;

    private Transform bossTrans;
    private NavMeshAgent agent;

    private readonly int hashTrace = Animator.StringToHash("IsTrace");
    private readonly int hashAttack = Animator.StringToHash("IsAttack");
    private readonly int hashLongAttack = Animator.StringToHash("IsLongAttack");

    private Coroutine stateCoroutine;   //상태 코루틴 : 하나의 행동에 하나의 코루틴만 돌아감 
    private Player target;

    private void Start()
    {
        this.Init();    //test , Main에서 Init해주기 
    }

    public void Init()
    {
        this.agent = this.GetComponent<NavMeshAgent>();
        this.bossTrans = this.GetComponent<Transform>();
        this.anim = this.GetComponent<Animator>();
        this.target = GameObject.FindWithTag("Player").GetComponent<Player>();

        this.stateCoroutine = null;
        this.ChageState(eState.IDLE);
        this.CheckTimeToLongAttack();
    }

    private void CheckTimeToLongAttack()
    {
        this.StartCoroutine(this.CoCheckTimeToLongAttack());
    }

    [SerializeField]
    private float delta = 0;
    private float timeToLongAttack = 3f;    //n초에 한번씩 원거리 공격 

    private IEnumerator CoCheckTimeToLongAttack()
    {
        while (true)
        {
            this.delta += Time.deltaTime;

            if (this.delta >= timeToLongAttack)
            {
                if (this.state == eState.CLOSE_ATTACK || this.state == eState.LONG_ATTACK)
                {
                    this.ResetTimeToLongAttack();
                    continue;
                }
                else 
                {
                    //Idle or Trace 
                    this.ResetTimeToLongAttack();
                    this.ChageState(eState.LONG_ATTACK);
                }
            }

            yield return null;
        }
    }

    private void ResetTimeToLongAttack()
    {
        this.delta = 0;//다시 0초로 바꾸기
        this.timeToLongAttack = UnityEngine.Random.Range(3.0f, 6.0f);
    }

    private void ChageState(eState state)
    {
        Debug.LogFormat("{0} -> {1}", this.state, state);
        
        if (this.state != state)
        {
            this.state = state;

            switch (this.state)
            {
                case eState.IDLE:
                    this.Idle();
                    break;

                case eState.CLOSE_ATTACK:
                    
                    break;

                case eState.LONG_ATTACK:
                    this.LongAttack();
                    break;

                case eState.TRACE:
                    this.Trace();
                    break;

                case eState.DIE:
                    
                    break;
            }
        }
    }

    //private IEnumerator CheckMonsterState()
    //{
    //    while (!isDie)
    //    {
    //        yield return new WaitForSeconds(0.3f);

    //        if (this.state == eState.DIE) yield break;

    //        float distance = Vector3.Distance(this.playerTrans.position, this.bossTrans.position);

    //        if (distance <= attackRange)
    //        {
    //            state = eState.CLOSE_ATTACK;
    //        }
    //        else if (distance <= traceRange)
    //        {
    //            state = eState.TRACE;
    //        }
    //        else
    //        {
    //            state = eState.IDLE;
    //        }
    //    }
    //}


    //private IEnumerator MonsterAction()
    //{
    //    while (this.isDie == false)
    //    {
    //        switch (this.state)
    //        {
    //            case eState.IDLE:
    //                this.Idle();
    //                break;
    //            case eState.CLOSE_ATTACK:
    //                this.CloseAttack();
    //                break;
    //            case eState.TRACE:
    //                this.Trace();
    //                //yield return new WaitForSeconds(3.0f);
    //                //this.LongAttack();
    //                break;
    //            case eState.DIE:
    //                this.Die();
    //                break;
    //        }
    //        yield return new WaitForSeconds(0.3f);
    //    }
    //}
    private void LongAttack()
    {
        if (this.stateCoroutine != null)//이미 다른 state가 있을때
            this.StopCoroutine(this.stateCoroutine);// 기존꺼를 멈추고

        this.stateCoroutine = this.StartCoroutine(this.CoLongAttack()); //새로운거를 시작
    }

    private IEnumerator CoLongAttack()
    {
        this.anim.SetBool(hashLongAttack, true);
        this.agent.isStopped = false;

        //투사체 발사 
        yield return new WaitForSeconds(1.0f);

        //발사 완료 

        //거리 다시 체크 Idle할건지 Trace할건지 
        //float distance = Vector3.Distance(this.transform.position, this.target.transform.position);
        //if (distance <= this.traceRange)
        //{
        //    this.ChageState(eState.CLOSE_ATTACK);
        //}
        //else if (distance <= this.attackRange)
        //{
        //    this.ChageState(eState.TRACE);
        //}
        //else
            this.ChageState(eState.IDLE);

    }

    private void OnDrawGizmos()
    {
        if (state == eState.TRACE)
        {
            Gizmos.color = Color.blue;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.traceRange);
        }
        else if (state == eState.CLOSE_ATTACK)
        {
            Gizmos.color = Color.red;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.attackRange);
        }
    }
    
    //Idle 상태 
    
    public void Idle()
    {
        this.stateCoroutine = this.StartCoroutine(this.CoIdle());
    }

    private IEnumerator CoIdle()
    {
        this.agent.isStopped = true;    //멈춤 
        this.anim.SetBool(hashTrace, false);    //애니메이셔 실행 
        
        while (true)
        {
            //사거리 안에 들어오는지 계속 체크 해야 함
            
            float distance = Vector3.Distance(this.transform.position, this.target.transform.position);
            if (distance <= this.traceRange)
            {
                //상태 변환 
                this.ChageState(eState.TRACE);
                break;
            }

            yield return null;//다음 프레임으로 이동 
        }

        Debug.Log("Idle 상태 종료");
    }



    private void CloseAttack()
    {

    }
    //private IEnumerator CoCloseAttack()
    //{
        //this.anim.SetBool(hashAttack, true);
        //this.anim.SetBool(hashLongAttack, false);


    //}
    public void Trace()
    {
        if (this.stateCoroutine != null)
        {
            this.StopCoroutine(this.stateCoroutine);
        }

        this.stateCoroutine = this.StartCoroutine(this.CoTrace());
    }

    private IEnumerator CoTrace()
    {
        this.agent.SetDestination(this.target.gameObject.transform.position);
        this.agent.isStopped = false;
        this.anim.SetBool(hashTrace, true);
        this.anim.SetBool(hashAttack, false);
        this.anim.SetBool(hashLongAttack, false);

        while (true)
        {
            //근거리 사거리에 들어왔는가를 체크 
            float distance = Vector3.Distance(this.transform.position, this.target.transform.position);
            if (distance <= this.attackRange)
            {
                //상태 변환 
                this.ChageState(eState.CLOSE_ATTACK);

                break;
            }

            yield return null;
        }

        Debug.Log("Trace 상태 종료");
    }




    private void Die()
    {
        this.isDie = true;
        this.agent.isStopped = true;
        this.anim.SetTrigger("Die");
        Destroy(this.gameObject, 1.5f);
    }
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            Destroy(collision.gameObject);

            this.monsterHp -= 10;
            Debug.LogFormat("monsterhp:{0}", this.monsterHp);
            if (this.monsterHp <= 0)
            {
                this.state = eState.DIE;
            }
        }
    }
}

 

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;

public class BossController : MonoBehaviour
{
    public enum eState
    {
        IDLE = 0,
        TRACE,
        CLOSE_ATTACK,    //근거리 공격 
        LONG_ATTACK,  //원거리 공격
        DIE,
    }
    public eState state;
    [SerializeField]
    private float closeAttackRange = 2.0f;
    [SerializeField]
    private float traceRange = 30.0f;
    [SerializeField]
    private float longAttackRange = 10.0f;

    private float monsterHp = 100.0f;
    public bool isDie;
    private Animator anim;

    private Transform bossTrans;
    private NavMeshAgent agent;

    private readonly int hashState = Animator.StringToHash("State");

    private Coroutine stateCoroutine;   //상태 코루틴 : 하나의 행동에 하나의 코루틴만 돌아감 
    private Player target;

    private void Start()
    {
        this.Init();    //test , Main에서 Init해주기 
    }

    public void Init()
    {
        this.agent = this.GetComponent<NavMeshAgent>();
        this.bossTrans = this.GetComponent<Transform>();
        this.anim = this.GetComponent<Animator>();
        this.target = GameObject.FindWithTag("Player").GetComponent<Player>();

        this.stateCoroutine = null;
        this.ChageState(eState.IDLE);
        this.CheckTimeToLongAttack();
    }

    private void CheckTimeToLongAttack()
    {
        this.StartCoroutine(this.CoCheckTimeToLongAttack());
    }

    [SerializeField]
    private float delta = 0;
    private float timeToLongAttack = 3f;    //n초에 한번씩 원거리 공격 

    private IEnumerator CoCheckTimeToLongAttack()
    {
        while (true)
        {
            this.delta += Time.deltaTime;

            if (this.delta >= timeToLongAttack)
            {
                if (this.state == eState.CLOSE_ATTACK || this.state == eState.LONG_ATTACK)
                {
                    this.ResetTimeToLongAttack();
                    continue;
                }
                else
                {
                    //Idle or Trace 
                    this.ResetTimeToLongAttack();
                    this.ChageState(eState.LONG_ATTACK);
                    break;
                }
            }

            yield return null;
        }
    }

    private void ResetTimeToLongAttack()
    {
        this.delta = 0;//다시 0초로 바꾸기
        this.timeToLongAttack = UnityEngine.Random.Range(3.0f, 6.0f);
    }

    private void ChageState(eState state)
    {
        Debug.LogFormat("{0} -> {1}", this.state, state);

        if (this.state != state)
        {
            this.state = state;

            switch (this.state)
            {
                case eState.IDLE:
                    this.Idle();
                    break;

                case eState.CLOSE_ATTACK:
                    this.CloseAttack();
                    break;

                case eState.LONG_ATTACK:
                    this.LongAttack();
                    break;

                case eState.TRACE:
                    this.Trace();
                    break;

                case eState.DIE:

                    break;
            }
        }
    }

    //private IEnumerator CheckMonsterState()
    //{
    //    while (!isDie)
    //    {
    //        yield return new WaitForSeconds(0.3f);

    //        if (this.state == eState.DIE) yield break;

    //        float distance = Vector3.Distance(this.playerTrans.position, this.bossTrans.position);

    //        if (distance <= attackRange)
    //        {
    //            state = eState.CLOSE_ATTACK;
    //        }
    //        else if (distance <= traceRange)
    //        {
    //            state = eState.TRACE;
    //        }
    //        else
    //        {
    //            state = eState.IDLE;
    //        }
    //    }
    //}


    //private IEnumerator MonsterAction()
    //{
    //    while (this.isDie == false)
    //    {
    //        switch (this.state)
    //        {
    //            case eState.IDLE:
    //                this.Idle();
    //                break;
    //            case eState.CLOSE_ATTACK:
    //                this.CloseAttack();
    //                break;
    //            case eState.TRACE:
    //                this.Trace();
    //                //yield return new WaitForSeconds(3.0f);
    //                //this.LongAttack();
    //                break;
    //            case eState.DIE:
    //                this.Die();
    //                break;
    //        }
    //        yield return new WaitForSeconds(0.3f);
    //    }
    //}
    private void LongAttack()
    {
        if (this.stateCoroutine != null)//이미 다른 state가 있을때
            this.StopCoroutine(this.stateCoroutine);// 기존꺼를 멈추고

        this.stateCoroutine = this.StartCoroutine(this.CoLongAttack()); //새로운거를 시작
    }

    private IEnumerator CoLongAttack()
    {
        this.anim.SetBool(hashLongAttack, true);
        this.agent.isStopped = false;

        //투사체 발사 
        yield return new WaitForSeconds(1.0f);

        //발사 완료 

        //거리 다시 체크 Idle할건지 Trace할건지
        this.ChageState(eState.IDLE);

    }

    private void OnDrawGizmos()
    {
        if (state == eState.TRACE)
        {
            Gizmos.color = Color.blue;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.traceRange);
        }
        else if (state == eState.CLOSE_ATTACK)
        {
            Gizmos.color = Color.red;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.closeAttackRange);
        }
        else if (state == eState.LONG_ATTACK)
        {
            Gizmos.color = Color.yellow;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.longAttackRange);
        }
    }

    //Idle 상태 

    public void Idle()
    {
        this.stateCoroutine = this.StartCoroutine(this.CoIdle());
    }

    private IEnumerator CoIdle()
    {
        this.agent.isStopped = true;    //멈춤 
        this.anim.SetBool(hashTrace, false);    //애니메이셔 실행 

        while (true)
        {
            //사거리 안에 들어오는지 계속 체크 해야 함

            float distance = Vector3.Distance(this.transform.position, this.target.transform.position);
            if (distance <= this.traceRange)
            {
                //상태 변환 
                this.ChageState(eState.TRACE);
                break;
            }

            yield return null;//다음 프레임으로 이동 
        }

        Debug.Log("Idle 상태 종료");
    }


    public void CloseAttack()
    {

        Debug.LogFormat("CloseAttack");

        if(this.stateCoroutine != null)
        {
            this.StopCoroutine(this.stateCoroutine);
        }
        this.stateCoroutine = this.StartCoroutine(this.CoCloseAttack());
    }

    private IEnumerator CoCloseAttack()
    {
        this.anim.SetBool(hashAttack, true);
        //this.anim.SetBool(hashLongAttack, false);
        while (true)
        {
            //사거리 안에 들어오는지 계속 체크 해야 함
            float distance = Vector3.Distance(this.transform.position, this.target.transform.position);
            if (distance <= this.traceRange)
            {
                //상태 변환 
                //this.ChageState(eState.TRACE);
                Debug.Log("근거리-> 추적");
                break;
            }
            yield return null;//다음 프레임으로 이동 
        }

        Debug.Log("근거리 공격 상태 종료");
    }
    public void Trace()
    {
        if (this.stateCoroutine != null)
        {
            this.StopCoroutine(this.stateCoroutine);
        }

        this.stateCoroutine = this.StartCoroutine(this.CoTrace());
    }

    private IEnumerator CoTrace()
    {
        this.agent.SetDestination(this.target.gameObject.transform.position);
        this.agent.isStopped = false;
        this.anim.SetBool(hashTrace, true);
        this.anim.SetBool(hashAttack, false);
        this.anim.SetBool(hashLongAttack, false);

        while (true)
        {
            //근거리 사거리에 들어왔는가를 체크 
            float distance = Vector3.Distance(this.transform.position, this.target.transform.position);
            if (distance <= this.closeAttackRange)
            {
                //상태 변환 
                this.ChageState(eState.CLOSE_ATTACK);

                break;
            }
            yield return null;
        }

        Debug.Log("Trace 상태 종료");
    }

    private void Die()
    {
        this.isDie = true;
        this.agent.isStopped = true;
        this.anim.SetTrigger("Die");
        Destroy(this.gameObject, 1.5f);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            Destroy(collision.gameObject);

            this.monsterHp -= 10;
            Debug.LogFormat("monsterhp:{0}", this.monsterHp);
            if (this.monsterHp <= 0)
            {
                this.state = eState.DIE;
            }
        }
    }
}

 

맞으면 깜빡거리기-> material 을 바꾸기

 

 

sci-fi city