HeroShoot-Stage1: 범위내의 몬스터 자동공격
2023. 8. 28.

 

-코루틴 함수로 상태를 체크하는 함수를 작성했다. DetectMonster()메서드는 몬스터를 감지하는 메서드이다.

 

-distance는 플레이어의 위치와 추출한 오브젝트의 위치 사이의 거리이다.

-공격 사정거리 내에 들어왔을때 가까이에 있는 몬스터를 찾아 this.shortDistanceGo 게임 오브젝트에 저장한다.

-Physics.OverlapSphere를 사용하기 위해 몬스터의 레이어를 Monster로 변경해준다.

몬스터의 레이어/ Player의 애니메이션

공격 구현

-현재는 애니메이션만 넣은 상태이므로 총에서 총알이 나가 몬스터를 공격하도록 수정한다.

-bulletGo에 BulletController.cs 를 부착한다. bullet의 각도는 자식의 각도를 조절한다.

 

몬스터에 removeBullet.cs추가

-총알의 collider를 붙인후 태그를 바꿔주고, 몬스터 프리팹에 스크립트를 추가한다.

Stage1Main.cs/ FloorController.cs

-몬스터가 존재하지 않으면 문을 열리게 하기 위해서 floorController.cs를 추가하였다.

구현결과
-quad 추가 후 material 변경(shader-mobile-particles-multiply)

-quad의 이름을 target으로 변경 후 비활성화 한 뒤 코드를 수정하였다.

-PlayerController의 코드를 다음과 같이 수정하였는데(85~86라인) 동작은 하는데 child 범위가 벗어났다고 오류가 떠서..

어떻게 해결하는게 맞는지 아직 모르겠다.

name을 찍어보면 오류나지않고 이름 잘 출력하는데 음 비활성화된 상태에서 접근하는거라 그런건지..?

 

 

나오는데..오류난다.

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.EventSystems;

public class PlayerController : MonoBehaviour
{
    //[SerializeField] private VariableJoystick joystick;
    public enum eState
    {// 상태 정의
        IDLE, MOVE, ATTACK, DIE
    }
    public eState state = eState.IDLE;//Player의 현재 상태
    private bool isDie;//Player의 사망 여부
    
   // [SerializeField] private float traceDistance = 10.0f;//추적 사거리
    [SerializeField] private float attackDistance = 8.0f;//공격 사거리
    [SerializeField] private float moveSpeed = 3f;
    [SerializeField] GameObject bulletGo;//총알 오브젝트
    [SerializeField] Transform FirePos;//총알 발사 transform
    private float distance;
    private float minDistance = 20f;
    private GameObject shortDistanceGo;
    private float monsterCount;
    private TutorialMain tutorialMain;
    private Stage1Main stage1; 
    private Transform monsterTrans;
    private SceneLoad sceneLoad;
    private Collision collision;
    public bool isMove = false;
    private List<MonsterController> monsterList = new List<MonsterController>();
  //  private Vector3 downPosition;
    //private bool isDown = false;
    public Animator anim;
    
    // Start is called before the first frame update
    void Start()
    {
      this.tutorialMain = FindObjectOfType<TutorialMain>();
      this.stage1 = FindObjectOfType<Stage1Main>(); 
      //this.monsterGenerator = FindObjectOfType<MonsterGenerator>();
      this.sceneLoad = FindObjectOfType<SceneLoad>();
      this.monsterList = this.stage1.monsterList;//stage1의 몬스터를 저장
      //  StartCoroutine(this.CoMonsterLoad());
        StartCoroutine(this.CheckState());//상태확인
        StartCoroutine(this.PlayerAction());//상태에따라 플레이어 동작
    }

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


    }
    private IEnumerator CoMonsterLoad()
    {
        yield return null;
        //this.monsterTrans = this.stage1.monsterList[0].gameObject.transform;
        this.monsterTrans = this.monsterList[0].gameObject.transform;
        Debug.Log(this.monsterTrans);

    }
    private IEnumerator CheckState()
    {//상태 갱신 코루틴 함수: 일정 간격으로 Player의 행동상태 체크
        while (!isDie)
        {
            //yield return null;
            yield return new WaitForSeconds(0.3f);//0.3초마다 상태 갱신
            //yield return new WaitForSeconds(0.3f);
            this.DetectkMonster();//가장 가까운 몬스터 감지
            if (this.isMove)
            {           
                this.state = eState.MOVE;
            }
            else
            {      
                if (shortDistanceGo != null)
                {//몬스터 존재시
                    if (this.distance < this.attackDistance)
                    {                       
                        //string name = this.shortDistanceGo.transform.GetChild(2).gameObject.name;
                        //Debug.Log(name);
                        GameObject go = this.shortDistanceGo.transform.GetChild(2).gameObject;
                        go.SetActive(true);
                        this.transform.LookAt(this.shortDistanceGo.transform.position);
                        this.state = eState.ATTACK;//공격상태로 변경                      
                        this.Fire();//총알 발사
                     //   Debug.Log(this.distance);                       
                    }
                    else //공격범위를 벗어나면
                    {
                       // this.shortDistanceGo = null;
                        yield return null;
                        this.state = eState.IDLE;
                    }
                }
                else//몬스터가 존재하지 않으면
                {
                    this.state = eState.IDLE;
                   
                }           
            }
        }
        
    }

    private IEnumerator PlayerAction()
    {
        while (!isDie)
        {
            yield return new WaitForSeconds(0.3f);
            if (this.state == eState.MOVE)
            {
                Debug.Log("Move");
                this.anim.SetInteger("State", 1);
            }
            else if(this.state == eState.ATTACK)
            {
                Debug.Log("Attack Monster!");
                this.anim.SetInteger("State", 2);
            }         
            else
            {
                this.anim.SetInteger("State", 0);
            }
        }
    }
    public void movePlayer(Vector3 dir)
    {
        float angle = Mathf.Atan2(dir.x, dir.z) * Mathf.Rad2Deg;

        this.transform.Translate(dir.normalized * this.moveSpeed * Time.deltaTime, Space.World);
        this.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.up);//축으로 회전
                                                                               
        //this.anim.SetInteger("State", 1);//run forward animation
      
    }
    private void Fire()
    {
       // Vector3 rotation = this.FirePos.rotation.eulerAngles;
     //   Quaternion rotation = this.FirePos.rotation +Quaternion(0,0,0)
        Instantiate(this.bulletGo,this.FirePos.position,this.FirePos.rotation);
        
        //go.transform.rotation = Quaternion.identity;
        // go.transform.position = this.FirePos.position;
    }
    private void DetectkMonster()
    {//주변에 있는 몬스터 추출
        Collider[] colls = Physics.OverlapSphere(this.transform.position, this.attackDistance, 1 << 3);
        
        foreach (Collider coll in colls)
        { 
           // Debug.Log(coll);            
            this.distance = Vector3.Distance(this.transform.position, coll.gameObject.transform.position);
         
                if (this.minDistance >= this.distance)//거리 측정
                {//distance가 더 작을때 shorDistanceGo에 게임 오브젝트를 저장
                    this.minDistance = this.distance;
                    this.shortDistanceGo = coll.gameObject;
                    Debug.LogFormat("가장 가까운 몬스터:{0}", coll.gameObject.name);
                   
                }           
        }

    }
    private void OnCollisionEnter(Collision collision)
    {
        this.collision = collision;//매개변수를 멤버변수에 저장
        if (collision.collider.CompareTag("Portal"))
        {
            Debug.Log("Portal");
            Destroy(collision.gameObject);
            this.tutorialMain.OpenGate();
        }
        else if (collision.collider.CompareTag("Stage1"))
        {
            this.SceneLoad(this.collision.gameObject.tag);
        }
        else if (collision.collider.CompareTag("Stage2"))
        {
            this.SceneLoad(this.collision.gameObject.tag);
        }
    }

    private void SceneLoad(string sceneName)
    {
        this.sceneLoad.imgDim.gameObject.SetActive(true);
        StartCoroutine(this.sceneLoad.CoFadeOut(sceneName));
    }

    private void OnDrawGizmos()
    {
        if(this.state == eState.ATTACK)
        {
            Gizmos.color = Color.yellow;
            
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RemoveBullet : MonoBehaviour
{
    [SerializeField] private float hp = 10;
    private void OnCollisionEnter(Collision collision)
    {
        Debug.Log(collision);

        if (collision.collider.CompareTag("Bullet"))
        {
            this.hp -= 1;
            Destroy(collision.gameObject); //총알 삭제
            if (this.hp <= 0)
            {
                Destroy(this.gameObject);//monster 사망
                Debug.Log("몬스터 사망");
            }
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FloorController : MonoBehaviour
{
    //private List<Collider> colls = new List<Collider>();
    private Collider[] colls;
   
    public bool isOpen;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.DetectkMonster();
     //   Debug.Log(this.count);
        
        if (this.colls.Length == 0)
        {
            this.isOpen = true;
          //  Debug.Log("문이 열려요");
        }
    }
    private void DetectkMonster()
    {// 몬스터 추출
        this.colls = Physics.OverlapSphere(this.transform.position, 30f, 1 << 3);

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

public class Stage1Main : MonoBehaviour
{
    [SerializeField] private GameObject gateLeftGo;
    [SerializeField] private GameObject gateRightGo;
    [SerializeField] private GameObject npcGo;
    [SerializeField] private GameObject speechGo;
    [SerializeField] private MyJoyStick joystick;
    private Text text;
    private bool isDown;
    private float openDamping = 1f;
    private float timeCount;
    private float monsterCount;
    private PlayerController playerController;
    private MonsterGenerator monsterGenerator;
    private FloorController floorController;
    public List<MonsterController> monsterList = new List<MonsterController>();//생성된 몬스터를 관리
    // Start is called before the first frame update
  
    void Start()
    {
        this.monsterGenerator = FindObjectOfType<MonsterGenerator>();
        this.playerController = FindAnyObjectByType<PlayerController>();
        this.floorController = FindAnyObjectByType<FloorController>();
        MonsterController beholderController1 = this.monsterGenerator.GenerateMonster(GameEnums.eMonsterType.BeholderMonster, new Vector3(3f, -2f, 20));
        //리스트에 추가
        this.monsterList.Add(beholderController1);
        //Debug.Log(this.monsterList[0]);

        this.joystick.onDown = () => {
            this.isDown = true;
        };
        this.joystick.onUp = () => {
            this.isDown = false;
            //this.playerController.anim.SetInteger("State", 0);
            this.playerController.state = PlayerController.eState.IDLE;
        };

        //시작시 안내 UI Active.
        this.npcGo.SetActive(true);
        this.speechGo.SetActive(true);
        this.text = this.speechGo.GetComponentInChildren<Text>();
        this.text.text = "조이스틱에서 손을 놓으면 적팀에게 공격합니다.";
    }
    // Update is called once per frame
    void Update()
    {
        if (this.isDown)
        {
            float h = this.joystick.Direction.x;
            float v = this.joystick.Direction.y;

            Vector3 moveDir = (Vector3.forward * v) + Vector3.right * h;

            //조이스틱으로 입력받아 방향 바라보게함
            if (moveDir != Vector3.zero)
            {//이동
                this.playerController.movePlayer(moveDir);               
            }           
            this.playerController.isMove = true;
           
        }
        else
        {
            this.playerController.isMove = false;
        }
       
        if (this.floorController.isOpen)
        {
            this.OpenGate();
        }
    }

    public void OpenGate()
    {
        //Debug.Log("OpenGate");
        StartCoroutine(this.CoOpenGate());

    }

    private IEnumerator CoOpenGate()
    {
        //this.gateLeftGo.transform.Rotate(0,90,0);
        var left = Quaternion.Euler(new Vector3(0, 90, 0));
        var right = Quaternion.Euler(new Vector3(0, -90, 0));
        var leftRotation = this.gateLeftGo.transform.rotation;
        var rightRotation = this.gateRightGo.transform.rotation;
        while (true)
        {
            this.gateLeftGo.transform.rotation = Quaternion.Slerp(leftRotation, left, this.timeCount * this.openDamping);
            this.gateRightGo.transform.rotation = Quaternion.Slerp(rightRotation, right, this.timeCount * this.openDamping);
            this.timeCount += Time.deltaTime;

            if (this.gateLeftGo.transform.localRotation == left)
            {
                break;
            }
            yield return null;
        }
        this.text.text = "좋습니다! 문을 통해 계속 이동하세요.";
    }
    
}
myoskin