유니티 심화

[주말과제/미완] HeroShooter - 총알나가고 삭제

다모아 2023. 8. 28. 01:51

미완

- 총알을 한발씩 발사하고싶은데 어떻게 해야할지 모르겠음[야매로 만들어서 이상함]

- 총알맞으면 피격 애니메이션

- 총알 맞고 죽는 애니메이션

- 죽으면 몇초 후 사라지게하기

- 사라진 후 Text와 문 열리기


 

BulletController

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

public class BulletController : MonoBehaviour
{
    [SerializeField]
    private float bulletSpeed = 500f;
    private Rigidbody rBody;
    private int damage = 1;
    // Start is called before the first frame update
    void Start()
    {
        this.rBody = this.GetComponent<Rigidbody>();
        this.rBody.AddForce(this.transform.forward * this.bulletSpeed);
    }
    public void RemoveBullet()
    {
        Destroy(this.gameObject);
    }

    private void OnTriggerEnter(Collider other)
    {
        if(other.gameObject.tag == "Monster")
        {
            this.RemoveBullet();
        }
    }
}

 

PlayerController

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    //캐릭터 움직임 속도
    [SerializeField]
    private float moveSpeed = 5f;
    //애니메이션
    private Animator anim;
    //포탈태그했을 때
    public Action reachPortal;
    //문에 닿았을 때
    public Action reachDoor;
    //rayOffsetY
    private float rayOffsetY = 0.5f;
    //공격대리자
    public Action<MonsterController> onAttack;
    //공격반복방지
    private Coroutine attackRoutine;
    //총알프리팹
    [SerializeField]
    private GameObject bulletPrefab;
    //총알생성위치
    [SerializeField]
    private Transform bulletTr;
    //총알
    private GameObject bullet;
    //경과시간
    private float elapsedTime = 0f;

    void Start()
    {
        this.anim = this.GetComponent<Animator>();
    }
    //공격
    public void Attack()
    {
        if (this.attackRoutine != null)
        {
            this.StopCoroutine(this.attackRoutine);
        }
        this.StartCoroutine(this.CoAttack());
    }

    //공격 코루틴
    private IEnumerator CoAttack()
    {
        while (true)
        {
            this.elapsedTime += Time.deltaTime;
            //총쏘는 애니메이션
            this.PlayAnimation(EnumState.ePlayerState.Attack);
            //0.5초마다 총알 생성
            if(this.elapsedTime >= 0.5f)
            {
                //총알소환
                this.bullet = Instantiate(this.bulletPrefab, this.bulletTr.position, this.bulletTr.rotation);
                this.elapsedTime = 0f;
            }
            if (bullet == true)
            {
                //마우스 떼면 바로 발사 안함
                break;
            }
        }
        yield return null;
    }
    //움직임
    public void Move(Vector3 moveDir)
    {
        //달리는 애니메이션 실행
        this.PlayAnimation(EnumState.ePlayerState.RunF);
        //Mathf.Atan2와 Mathf.Rad2Deg를 이용해서 회전시키기
        float angle = Mathf.Atan2(moveDir.x, moveDir.z) * Mathf.Rad2Deg;
        //AngleAxis를 이용하기
        Quaternion q = Quaternion.AngleAxis(angle, Vector3.up);
        //캐릭터에 회전 적용
        this.transform.rotation = q;
        //캐릭터 움직이기
        this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
    }
    //애니메이션재생
    public void PlayAnimation(EnumState.ePlayerState state)
    {
        this.anim.SetInteger("State", (int)state);
    }
    //부딪쳤을 때
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.tag == "Door")
        {
            //대리자 사용해서 씬 전환하기
            this.reachDoor();
        }
        else if (collision.collider.tag == "Portal")
        {
            //대리자 사용해서 문 열고, 포탈 제거하기
            this.reachPortal();
        }
    }
    //그림
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(this.transform.position, 30f);
    }
    //몬스터 어딨는지 확인
    public void MonsterRay(Vector3 dir, MonsterController target)
    {
        //레이로 플레이어의 살짝 위에서 몬스터까지의 거리 찍기
        Ray ray = new Ray(this.transform.position + (Vector3.up * this.rayOffsetY), dir);
        //그려보기
        Debug.DrawRay(ray.origin, ray.direction * 100f, Color.green);
        //layerMask.NameToLayer로 레이어 구별해주기
        var layerMask = 1 << LayerMask.NameToLayer("Monster") | 1 << LayerMask.NameToLayer("Car") | 1 << LayerMask.NameToLayer("Wall");
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 100f, layerMask))
        {
            Debug.Log(hit.collider.tag);

            if (hit.collider.CompareTag("Monster"))
            {
                if (target != null)
                {
                    target.HideIndicator();
                }
                var monsterController = hit.collider.gameObject.GetComponent<MonsterController>();
                monsterController.ShowIndicator();
                this.onAttack(target);
                target = monsterController;
            }
            else
            {
                MonsterController[] monsterContorllers = GameObject.FindObjectsOfType<MonsterController>();
                Debug.Log(monsterContorllers.Count());

                foreach (MonsterController monster in monsterContorllers)
                {
                    monster.HideIndicator();
                }
                target = null;
            }
        }
    }
}

 

MonsterController

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

public class MonsterController : MonoBehaviour
{
    [SerializeField]
    private GameObject targetQuad;
    //체력
    private int hp = 5;
    //최대체력
    private int maxHp;
    //애니메이션
    private Animator anim;

    // Start is called before the first frame update
    void Start()
    {
        this.maxHp = this.hp;
        this.anim = this.GetComponent<Animator>();
    }

    public void GetHit(int damage)
    {
        this.hp -= damage;
    }

    public void Die()
    {
        this.PlayAnimation(EnumState.eMonsterState.Die);
        Destroy(this.gameObject, 3f);
    }

    public void HideIndicator()
    {
        this.targetQuad.SetActive(false);
    }

    public void ShowIndicator()
    {
        this.targetQuad.SetActive(true);
    }

    public void PlayAnimation(EnumState.eMonsterState state)
    {
        this.anim.SetInteger("State", (int)state);
    }
}

 

Stage1SceneMain

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

public class Stage1SceneMain : MonoBehaviour
{
    //플레이어
    [SerializeField]
    private PlayerController playerController;
    //몬스터
    [SerializeField]
    private MonsterController target;
    //조이스틱
    [SerializeField]
    private CJoystick joystick;
    //왼쪽 문
    [SerializeField]
    private DoorController leftDoorController;
    //오른쪽 문
    [SerializeField]
    private DoorController rightDoorController;
    //페이드인
    [SerializeField]
    private Image dim;
    //NPC 텍스트
    [SerializeField]
    private Text text;
    //TargetQuad
    [SerializeField]
    private GameObject targetQuadGo;
    //반지름
    private float radius = 30f;
    //딕셔너리
    private Dictionary<GameObject, float> dic = new Dictionary<GameObject, float>();
    //캐릭터가 움직이는 좌표
    private Vector3 moveDir;

    // Start is called before the first frame update
    void Start()
    {
        //씬 전환시 페이드인
        this.StartCoroutine(this.CoFadeInAndSceneManager());
        //포탈에 닿았을 때
        this.playerController.reachPortal = () =>
        {
            //포탈 삭제하고
            //Destroy(this.portalGo.gameObject);
            //문 열기
            //this.leftDoorController.OpenDoor();
            //this.rightDoorController.OpenDoor();
        };

        //문에 닿았을 때
        this.playerController.reachDoor = () =>
        {
            //씬 전환하기
            //this.FadeOutAndSceneManager();
        };

        //공격실행
        this.playerController.onAttack = (target) => {
            if (moveDir == Vector3.zero)
            {
                this.playerController.Attack();
                //공격했을 때 바라보기
                this.playerController.transform.LookAt(target.transform.position);
            }
        };
    }

    // Update is called once per frame
    void Update()
    {
        //수평, 수직 좌표 구하고 moveDir에 넣기
        float h = this.joystick.Horizontal;
        float v = this.joystick.Vertical;
        this.moveDir = new Vector3(h, 0, v);
        //moveDir이 Vector3.zero일 경우
        if (moveDir == Vector3.zero)
        {
            //정지 애니메이션 실행
            this.playerController.PlayAnimation(EnumState.ePlayerState.Idle);
        }
        else // (0,0,0)이 아닐 경우
        {
            //this.IsAttack = false;
            this.playerController.Move(moveDir);
        }
        //OverlapSphere로 몬스터 인식하기
        int layerMask = 1 << LayerMask.NameToLayer("Monster");
        Collider[] hitColliders = Physics.OverlapSphere(this.playerController.transform.position, this.radius, layerMask);

        this.dic.Clear();
        foreach (Collider cols in hitColliders)
        {
            //딕셔너리에 거리차이와 게임오브젝트 추가
            this.dic.Add(cols.gameObject, Vector3.Distance(cols.transform.position, this.playerController.transform.position));
        }

        //만약 dic.Count가 0보다 크다면
        if (dic.Count > 0)
        {
            //작은 값을 short_distance에 넣기
            float short_distance = this.dic.Values.Min();
            //몬스터한 키값 넣어주기
            var target = this.dic.FirstOrDefault(x => x.Value == short_distance).Key;
            //dir에 몬스터와 플레이어의 거리 빼준 값 넣기
            Vector3 dir = this.target.transform.position - this.playerController.transform.position;
            this.playerController.MonsterRay(dir, this.target);
        }
    }

    private IEnumerator CoFadeInAndSceneManager()
    {
        Color color = this.dim.color;
        while (true)
        {
            color.a -= 0.01f;
            this.dim.color = color;

            if (color.a <= 0)
            {
                break;
            }
            yield return null;
        }
    }
}