본문 바로가기
Unity

[유니티] 3D 슈팅게임 2

by Skull Crusher 2021. 5. 27.
728x90

1. 점수

Canvas 추가

Create Empty

UI -> Slider

UI -> Image

 

UI -> Text

 

UI -> Image

 

Color 255, 0, 0, 0

 

UI -> Text

알파값 0으로

 

2. 애니메이션

Create -> Empty

 

Paratemter -> Trigger

Canvas -> Window -> Animation

녹화버튼

 

3. 플레이어 피격

C# Script

 

이펙트 비활성화 추가

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

public class PlayerShot : MonoBehaviour
{
    //발포 시간 관련 속성들    
    public float fireDelayTime; // 발포 주기
    public float fireRange; //발포 길이
    private float time; //발포 주기 계산 시간
    
    // 발포 피격 관련 속성들
    private Ray shootRay = new Ray(); // 레이생성
    private RaycastHit shootHit; //레이 충돌 정보
    private int shootableMask; // 발포 충돌 레이어 마스크(충돌 레이어 정수값)
    public int damagePerShot; //피격 데미지

    // 발포 효과 관련 속성들
    public ParticleSystem gunParticleSys; //발포 이펙트 파티클 컴포넌트 참조
    public LineRenderer fireLine; //발포 라인 효과 (라인랜더러)
    public Light faceLight; // 발포 스팟 라이트 효과
    public Light gunLight; // 발포 포인트 라이트 효과
    public float effectDisplayTime; //발포 효과 재생 시간

    public Transform firePos; //발포위치

    // Start is called before the first frame update
    void Start()
    {
        // 충돌 레이어 이름으로 충돌 마스크 값을 구함
        shootableMask = LayerMask.GetMask("Shootable");
    }

    // Update is called once per frame
    void Update()
    {
        //시간을 계산함
        time += Time.deltaTime;

        //마우스 왼쪽 버튼을 누르고
        if (Input.GetButton("Fire1") && time >= fireDelayTime)
        {
            Shot(); //발포 처리
        }

        if (time >= fireDelayTime * effectDisplayTime)
        {
            DisableEffects(); // 이펙트 비활성화
        }
    }

    public void DisableEffects()
    {
        //조명 끄기
        gunLight.enabled = false;
        faceLight.enabled = false;

        //라인 비활성화
        fireLine.enabled = false;
    }

    void Shot()
    {
        time = 0f;

        //이펙트 라이트 on
        gunLight.enabled = true;
        faceLight.enabled = true;

        //발포 파티클 재생
        gunParticleSys.Stop();
        gunParticleSys.Play();

        //라인렌더러 활성화
        fireLine.enabled = true;

        //라인시작점 인덱스 : 0, 두번쨰 연결점 인덱스 : 1, ......2,3,4
        //라인랜더러의 시작점을 총구 위치로 설정
        fireLine.SetPosition(0, firePos.position);

        //발포 레이 시작 위치 설정
        shootRay.origin = firePos.position;

        //발포 레이의 방향 설정
        shootRay.direction = firePos.forward;

        //
        if (Physics.Raycast(shootRay, out shootHit, fireRange, shootableMask))
        {
            //레이가 충돌한 지점까지 라인의 끝점을 설정함
            fireLine.SetPosition(1, shootHit.point);

            //관통된 게임 오브젝트에 EnemyHealth 컴포넌트 존재하는지 확인함
            EnemyHealth health = shootHit.collider.GetComponent<EnemyHealth>();
            if (health != null)
            {
                health.TakeDamage(damagePerShot, shootHit.point);
            }
        }
        else
        {
            //레이의 충돌이 없으면 레이 방향으로 지정된 발포 길이만큼으로 라인의 끝점을 설정함
            fireLine.SetPosition(1, shootRay.origin + shootRay.direction * fireRange);
        }

        Debug.Log("발포!!!");
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UnityEngine.UI;

public class PlayerHealth : MonoBehaviour
{
    public int startHealth; //시작 체력
    public int currentHealth; //현재 체력

    public bool isDie; //사망 상태
    public Animator animator; //애니메이터

    public Slider healthSlider; //체력 슬라이더
    public Image damageImage; // 피격 데미지 이미지
    private float flashTime = 5f; //피격 데미지 효과 표시 시간
    private Color flashColor = new Color(1f, 0f, 0f, 1f); // 붉은색(r,g,b,a)

    //컴포넌트 참조
    public PlayerMovement movement;
    public PlayerShot shot;

    private bool damaged; // 피격상태

    // Start is called before the first frame update
    void Start()
    {
        currentHealth = startHealth; //시작 체력 설정
    }

    private void Update()
    {
        if (damaged)
        {
            //피격 칼라 효과 처리
            damageImage.color = flashColor;
        }
        else
        {
            // 피격 칼라 효과 복원
            damageImage.color = Color.Lerp(
                damageImage.color, Color.clear, flashTime * Time.deltaTime);
        }

        damaged = false;
    }
    public void TakeDamage(int damage)
    {
        damaged = true; //피격 칼라 효고 처리 설정

        //체력 감소
        currentHealth -= damage;

        //체력이 1이고 이미 사망한 상태가 아니라면
        if (currentHealth <= 0 && !isDie)
        {
            Death(); //사망 처리
        }

        //체력 게이지 처리
        healthSlider.value = currentHealth;
    }

    //몬스터 사망 처리
    public void Death()
    {
        //사망 상태 설정
        isDie = true;
        
        //사망 애니메이션 재생
        animator.SetTrigger("Die");

        shot.DisableEffects(); //발포 이팩트 비활성화
        movement.enabled = false; //이동 컴포넌트 비활성화
        shot.enabled = false; //발포 컴포넌트 비활성화
    }
}

 

오브젝트 연결

 

몬스터 공격

C# Scirpt

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

public class EnemyAttack : MonoBehaviour
{
    public float timeBetweenAttack; //공격주기 시간
    public int attackDamage; // 공격 데미지
    public Animator animator; //애니메이터

    private GameObject player; //플레이어 찾기

    private bool playerInRange; //플레이어가 공격범위 안에 있는지 여부
    private float time; // 공격 주기 체크용 타임

    public EnemyHealth enemyHealth; //몬스터 체력
    private PlayerHearlth playerHealth; //플레이어 체력

    // Start is called before the first frame update
    void Start()
    {
        //플레이어를 찾아서 참조
        player = GameObject.FindGameObjectWithTag("Player");
        //플레이어의 체력 컴포넌트를 참조함
        playerHealth = player.GetComponent<PlayerHearlth>();
    }

    public void OnTriggerEnter(Collider collision)
    {
       if (collision.gameObject == player)
        {
            playerInRange = true;
        }
    }

    // 몬스터 공격
    private void Attack()
    {
        time = 0;

        // 플레이어의 체력이 남아 있으면
        if (playerHealth.currentHealth > 0)
        {
            //플레이어 피격
            playerHealth.TakeDamage(attackDamage);
        }
    }

    private void Update()
    {
        // 시간 계산
        time += Time.deltaTime;

        // 몬스터의 공격 주기가 되고 플레이어와 충돌 상태라면
        if (time >= timeBetweenAttack && playerInRange && enemyHealth.currentHealth > 0)
        {
            Attack();
        }
    }
}

 

몬스터와 플레이어가 닿았다가 떨어질때 메소드 이벤트 추가

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

public class EnemyAttack : MonoBehaviour
{
    public float timeBetweenAttack; //공격주기 시간
    public int attackDamage; // 공격 데미지
    public Animator animator; //애니메이터

    private GameObject player; //플레이어 찾기

    private bool playerInRange; //플레이어가 공격범위 안에 있는지 여부
    private float time; // 공격 주기 체크용 타임

    public EnemyHealth enemyHealth; //몬스터 체력
    private PlayerHealth playerHealth; //플레이어 체력

    // Start is called before the first frame update
    void Start()
    {
        //플레이어를 찾아서 참조
        player = GameObject.FindGameObjectWithTag("Player");
        //플레이어의 체력 컴포넌트를 참조함
        playerHealth = player.GetComponent<PlayerHealth>();
    }

    public void OnTriggerEnter(Collider collision)
    {
       if (collision.gameObject == player)
        {
            playerInRange = true;
        }
    }

    //Trigger 충돌 해제 이벤트 메소드
    public void OnTriggerExit(Collider collision)
    {
        //충돌이 일어난 게임오브젝트가 플레이어 게임오브젝트라면
        if (collision.gameObject == player)
        {
            //공격 범위 안에 들어옴
            playerInRange = false;
        }
    }

    // 몬스터 공격
    private void Attack()
    {
        time = 0;

        // 플레이어의 체력이 남아 있으면
        if (playerHealth.currentHealth > 0)
        {
            //플레이어 피격
            playerHealth.TakeDamage(attackDamage);
        }
    }

    private void Update()
    {
        // 시간 계산
        time += Time.deltaTime;

        // 몬스터의 공격 주기가 되고 플레이어와 충돌 상태라면
        if (time >= timeBetweenAttack && playerInRange && enemyHealth.currentHealth > 0)
        {
            Attack();
        }
    }
}

 

플레이어 사망처리

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

//AI(Navigation) 사용
using UnityEngine.AI;

//적군 이동 처리
public class EnemyMovement : MonoBehaviour
{
    //공격 대상 (추적대상)
    public Transform attackTarget;

    //네브메쉬 에이전트 참조
    public NavMeshAgent navMeshAgent;

    //몬스터 애니메이터
    public Animator animator;

    public EnemyHealth enemyHealth;
    private PlayerHealth playerHealth;

    public Rigidbody rigidbody3d;

    // Start is called before the first frame update
    void Start()
    {
        //플레이어 게임오브젝트를 찾음 (태그로)
        GameObject player = GameObject.FindGameObjectWithTag("Player");

        // 츨레이어 체력 컴포넌트 참조
        playerHealth = player.GetComponent<PlayerHealth>();

        //플레이어를 추적대상으로 설정
        attackTarget = player.transform;
    }

    // Update is called once per frame
    void Update()
    {
        if (enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
        {
            //플레이어를 추적함
            navMeshAgent.SetDestination(attackTarget.position);
        }
        else
        {
            rigidbody3d.isKinematic = true;

            //추적 이동 중단
            navMeshAgent.enabled = false;
        }
    }
}

 

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

public class EnemyGenerator : MonoBehaviour
{
    public Transform[] genPositions; //생성 위치들

    public GameObject enemyPrefab; // 생성 몬스터 참조

    public float genDelayTime; // 생성 주기

    public PlayerHealth playerHealth;

    // Start is called before the first frame update
    void Start()
    {
        //몬스터 생성 타이머
        InvokeRepeating("EnemyGenTimer", 2f, genDelayTime);
    }


    void EnemyGenTimer()
    {
        if (playerHealth.currentHealth <= 0) return;

        int genIndex = Random.Range(0, genPositions.Length);

        Vector3 genPosition = genPositions[genIndex].position;

        float x = Random.Range(genPosition.x = 1f, genPosition.x + 1f);
        float z = Random.Range(genPosition.x = 1f, genPosition.z + 1f);

        genPosition = new Vector3(x, 0f, z);

        Instantiate(enemyPrefab, genPositions[genIndex].position, genPositions[genIndex].rotation);
    }
}

 

스코어 올리기

게임매니저 추가

 

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

using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    public static int score = 0;
    public Text scoreText;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        scoreText.text = "SCORE : " + score.ToString(); 
    }
}

 

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

using UnityEngine.AI;

public class EnemyHealth : MonoBehaviour
{
    //시작 체력
    public int startHealth;

    //현재체력
    public int currentHealth;

    //사망 상태
    public bool isDie;

    //애니메이터 컴포넌트 참조
    public Animator animator;
    public ParticleSystem hitParticle;
    public ParticleSystem deathParticle;
    public Renderer bodyRenderer;

    // Start is called before the first frame update
    void Start()
    {
        currentHealth = startHealth;
    }

    //피격 처리 (피격 데미지)
    public void TakeDamage(int damage, Vector3 hitPos)
    {
        currentHealth -= damage;
        if (currentHealth <= 0 && !isDie)
        {
            Death(); //사망 처리
        }

        if (isDie) return;

        hitParticle.transform.position = hitPos;
        hitParticle.Play();

    }

    //몬스터 사망 처리
    public void Death()
    {
        //점수 증가
        GameManager.score++;

        //사망 상태 설정
        isDie = true;

        //사망 애니메이션 재상
        animator.SetTrigger("Die");

        //네비게이션 이동 중단
        GetComponent<NavMeshAgent>().isStopped = true;
        
    }

    //사망 애니메이션 이벤트 메소드
    public void OnDieAnimationEvent()
    {
        bodyRenderer.enabled = false;

        deathParticle.Play();

        Destroy(gameObject, deathParticle.main.duration);
    }
}

 

몬스터가 죽을때마다 점수 올라감

 

게임종료

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

using UnityEngine.UI;

using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    public static int score = 0;
    public Text scoreText;

    private bool isGameOver = false;
    public Animator canVasAnimator;
    public PlayerHealth playerHealth;

    // Update is called once per frame
    void Update()
    {
        scoreText.text = "SCORE : " + score.ToString(); 

        //플레이어가 사망하고
        if (playerHealth.isDie && !isGameOver)
        {
            canVasAnimator.SetTrigger("GameOver");

            //5초뒤에 게임 재시작
            Invoke("GameReStart", 5f);

            isGameOver = true;
        }
    }

    //게임 재시작
    public void GameReStart()
    {
        score = 0; //점수 초기화

        //Scene activeScene = SceneManager.GetActiveScene();
        //string activeSceneName = activeScene.name;
        //아래 한줄과 같은 기능

        //현재씬(활성씬)을 재시작함
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }
}

 

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

using UnityEngine.UI;

public class PlayerHealth : MonoBehaviour
{
    public int startHealth; //시작 체력
    public int currentHealth; //현재 체력

    public bool isDie; //사망 상태
    public Animator animator; //애니메이터

    public Slider healthSlider; //체력 슬라이더
    public Image damageImage; // 피격 데미지 이미지
    private float flashTime = 5f; //피격 데미지 효과 표시 시간
    private Color flashColor = new Color(1f, 0f, 0f, 1f); // 붉은색(r,g,b,a)

    //컴포넌트 참조
    public PlayerMovement movement;
    public PlayerShot shot;

    private bool damaged; // 피격상태

    // Start is called before the first frame update
    void Start()
    {
        currentHealth = startHealth; //시작 체력 설정
    }

    private void Update()
    {
        if (damaged)
        {
            //피격 칼라 효과 처리
            damageImage.color = flashColor;
        }
        else
        {
            // 피격 칼라 효과 복원
            damageImage.color = Color.Lerp(
                damageImage.color, Color.clear, flashTime * Time.deltaTime);
        }

        damaged = false;
    }
    public void TakeDamage(int damage)
    {
        damaged = true; //피격 칼라 효고 처리 설정

        //체력 감소
        currentHealth -= damage;

        //체력이 1이고 이미 사망한 상태가 아니라면
        if (currentHealth <= 0 && !isDie)
        {
            Death(); //사망 처리
        }

        //체력 게이지 처리
        healthSlider.value = currentHealth;
    }

    //몬스터 사망 처리
    public void Death()
    {
        //사망 상태 설정
        isDie = true;

        //체력 슬라이더 비활성화
        healthSlider.gameObject.SetActive(false);

        //사망 애니메이션 재생
        animator.SetTrigger("Die");

        shot.DisableEffects(); //발포 이팩트 비활성화
        movement.enabled = false; //이동 컴포넌트 비활성화
        shot.enabled = false; //발포 컴포넌트 비활성화
    }
}

 

체력 UI 없어짐 추가

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

using UnityEngine.UI;

public class PlayerHealth : MonoBehaviour
{
    public int startHealth; //시작 체력
    public int currentHealth; //현재 체력

    public bool isDie; //사망 상태
    public Animator animator; //애니메이터

    public Slider healthSlider; //체력 슬라이더
    public Image damageImage; // 피격 데미지 이미지
    private float flashTime = 5f; //피격 데미지 효과 표시 시간
    private Color flashColor = new Color(1f, 0f, 0f, 1f); // 붉은색(r,g,b,a)

    //컴포넌트 참조
    public PlayerMovement movement;
    public PlayerShot shot;

    private bool damaged; // 피격상태

    // Start is called before the first frame update
    void Start()
    {
        currentHealth = startHealth; //시작 체력 설정
    }

    private void Update()
    {
        if (damaged)
        {
            //피격 칼라 효과 처리
            damageImage.color = flashColor;
        }
        else
        {
            // 피격 칼라 효과 복원
            damageImage.color = Color.Lerp(
                damageImage.color, Color.clear, flashTime * Time.deltaTime);
        }

        damaged = false;
    }
    public void TakeDamage(int damage)
    {
        damaged = true; //피격 칼라 효고 처리 설정

        //체력 감소
        currentHealth -= damage;

        //체력이 1이고 이미 사망한 상태가 아니라면
        if (currentHealth <= 0 && !isDie)
        {
            Death(); //사망 처리
        }

        //체력 게이지 처리
        healthSlider.value = currentHealth;
    }

    //몬스터 사망 처리
    public void Death()
    {
        //사망 상태 설정
        isDie = true;

        //체력 슬라이더 비활성화
        healthSlider.gameObject.SetActive(false);

        //현재 체력 슬라이더의 부모 게임오브젝트의 Transform을 참조
        //Transform.parent <- 부모 접근 속성
        Transform healthUITransform = healthSlider.transform.parent;
        //체력 표시용 UI 비활성화 
        healthUITransform.gameObject.SetActive(false);

        //사망 애니메이션 재생
        animator.SetTrigger("Die");

        shot.DisableEffects(); //발포 이팩트 비활성화
        movement.enabled = false; //이동 컴포넌트 비활성화
        shot.enabled = false; //발포 컴포넌트 비활성화
    }
}

'Unity' 카테고리의 다른 글

[유니티] RPG 게임 만들기2  (0) 2021.05.28
[유니티] 3D 슈팅게임 3  (0) 2021.05.28
[유니티] 3D 슈팅게임 만들기  (0) 2021.05.21
[유니티] 네이게이션  (0) 2021.05.20
[유니티]UI (드롭박스)  (0) 2021.05.20

댓글