본문 바로가기
Unity

[유니티] 3D 슈팅게임 만들기

by Skull Crusher 2021. 5. 21.
728x90

1. 엣셋다운

 

 

2. 라이팅 세팅

 

3. 애니메이션

애니스테이트 -> Die 연결

 

4. 무기장착

 

5.

Rigidbody 추가 -> Capsule Collider 추가

 

6. 바닥레이어

 

 

7. 캐릭터 이동

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

public class PlayerMovement : MonoBehaviour
{
    private Vector3 movement;

    private float h;
    private float v;

    public float speed;

    public Rigidbody rigidbody3d;

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

    // Update is called once per frame
    void Update()
    {
        h = Input.GetAxis("Horizontal");
        v = Input.GetAxis("Vertical");
    }

    private void FixedUpdate()
    {
        //이동 방향 벡터 생성
        movement = new Vector3(h, 0f, v).normalized;
        movement = movement * speed * Time.fixedDeltaTime;

        //Rigidbody.MovePosition을 이용해 이동 처리를 수행
        rigidbody3d.MovePosition(transform.position + movement);
    }
}

 

캐릭터 회전하기

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

public class PlayerMovement : MonoBehaviour
{
    private Vector3 movement;

    private float h;
    private float v;

    public float speed;

    public Rigidbody rigidbody3d;

    private int floorMask;

    // Start is called before the first frame update
    void Start()
    {
        LayerMask.GetMask("Floor");
    }

    // Update is called once per frame
    void Update()
    {
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");
    }

    private void FixedUpdate()
    {
        Move(); //이동처리
        Turn(); //회전처리
    }

    void Move()
    {
        //이동 방향 벡터 생성
        movement = new Vector3(h, 0f, v).normalized;
        movement = movement * speed * Time.fixedDeltaTime;

        //Rigidbody.MovePosition을 이용해 이동 처리를 수행
        rigidbody3d.MovePosition(transform.position + movement);
    }

    void Turn()
    {
        Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);

        RaycastHit FloorHit;

        //레이와 바닥과의 충돌 여부를 확인하고, 충돌될 경우 정보를 가져옴 -> floorHit
        if (Physics.Raycast(cameraRay, out FloorHit, 100f, floorMask))
        {
            //캐릭터의 회전 방향 벡터
            Vector3 direction = FloorHit.point - transform.position;

            direction.y = 0f;

            //회전 방향을 향한 쿼터니언을 구함
            Quaternion dirRotation = Quaternion.LookRotation(direction.normalized);

            //캐릭터를 회전함
            rigidbody3d.MoveRotation(dirRotation);
        }
    }
}

 

애니메이션 적용

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

public class PlayerMovement : MonoBehaviour
{
    private Vector3 movement;

    private float h;
    private float v;

    public float speed;

    public Rigidbody rigidbody3d;

    private int floorMask;

    public Animator animator;

    // Start is called before the first frame update
    void Start()
    {
        floorMask = LayerMask.GetMask("Floor");
    }

    // Update is called once per frame
    void Update()
    {
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        //이동 여부에 따른 애니메이션을 적용
        bool walking = (h != 0f || v != 0f);
        animator.SetBool("IsWalking", walking);
    }

    private void FixedUpdate()
    {
        Move(); //이동처리
        Turn(); //회전처리
    }

    void Move()
    {
        //이동 방향 벡터 생성
        movement = new Vector3(h, 0f, v).normalized;
        movement = movement * speed * Time.fixedDeltaTime;

        //Rigidbody.MovePosition을 이용해 이동 처리를 수행
        rigidbody3d.MovePosition(transform.position + movement);
    }

    void Turn()
    {
        Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);

        RaycastHit FloorHit;

        //레이와 바닥과의 충돌 여부를 확인하고, 충돌될 경우 정보를 가져옴 -> floorHit
        if (Physics.Raycast(cameraRay, out FloorHit, 100f, floorMask))
        {
            //캐릭터의 회전 방향 벡터
            Vector3 direction = FloorHit.point - transform.position;

            direction.y = 0f;

            //회전 방향을 향한 쿼터니언을 구함
            Quaternion dirRotation = Quaternion.LookRotation(direction.normalized);

            //캐릭터를 회전함
            rigidbody3d.MoveRotation(dirRotation);
        }
        
    }
}

 

추적카메라

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

public class CameraFollow : MonoBehaviour
{
    // 추적 타겟
    public Transform target;

    // 부드러움 수치 (보간수치)
    public float smoothing = 5f;

    // 추적 간격(거리) 오프셋
    Vector3 offset; 

    // Start is called before the first frame update
    void Start()
    {
        offset = transform.position - target.position;
    }

    // LateUpdate(): 모든 Update가 호출된 다음에 실행되는 이벤트 메소드
    // * 캐릭터의 이동처리가 완료된(Update의 처리 후가 보장된) 후 실행되는 이벤트 Update메소드
    void LateUpdate()
    {
        //갱신된 추적 대상(캐릭터)의 위치에 오프셋값을 더함
        Vector3 targetCamPos = target.position + offset;

        //카메라의 위치를 변경함
        transform.position = Vector3.Lerp(transform.position, targetCamPos, smoothing + Time.deltaTime);
    }
}

 

무기 발포하기

무기에 라이팅 추가

 

Line Renderer 추가 ->Edit Key

 

 

발포 라이트

 

돌에 충돌 추가

 

발포 라이트 효과

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; //발포 효과 재생 시간


    // 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)
        {
            gunLight.enabled = false;
            faceLight.enabled = false;
        }
    }

    void Shot()
    {
        time = 0f;

        gunLight.enabled = true;
        faceLight.enabled = true;

        Debug.Log("발포!!!");
    }
}

 

발포 레이저

firePos 만들기

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)
        {
            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);
        }
        else
        {
            //레이의 충돌이 없으면 레이 방향으로 지정된 발포 길이만큼으로 라인의 끝점을 설정함
            fireLine.SetPosition(1, shootRay.origin + shootRay.direction * fireRange);
        }

        Debug.Log("발포!!!");
    }
}

 

몬스터 만들기

Plane, Environment -> Navigation Static -> Bake

 

유니티 짱 쉐이더로 몬스터 바꾸기

유니티짱 쉐이터 복사 -> 붙여넣기

 

몬스터 애니메이터

몬스터에 애니메이터 연결

 

파라미터 추가

 

플레이어 추적하기

 

 

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;

    // Start is called before the first frame update
    void Start()
    {
        //플레이어 게임오브젝트를 찾음 (태그로)
        GameObject player = GameObject.FindGameObjectWithTag("Player");
        //플레이어를 추적대상으로 설정
        attackTarget = player.transform;
    }

    // Update is called once per frame
    void Update()
    {
        //플레이어를 추적함
        navMeshAgent.SetDestination(attackTarget.position);
    }
}

 

 

몬스터에 충돌 콜라이더 추가

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

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

    //현재체력
    public int currentHealth;

    //사망 상태
    public bool isDie;

    //애니메이터 컴포넌트 참조
    public Animator animator;

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

    //피격 처리 (피격 데미지)
    public void TakeDamage(int damage)
    {
        currentHealth -= damage;
        if (currentHealth <= 0 && ! isDie)
        {

        }
    }

    //몬스터 사망 처리
    public void Death()
    {
        isDie = true;
        animator.SetTrigger("Die");
    }
}
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)
        {
            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);
            }
        }
        else
        {
            //레이의 충돌이 없으면 레이 방향으로 지정된 발포 길이만큼으로 라인의 끝점을 설정함
            fireLine.SetPosition(1, shootRay.origin + shootRay.direction * fireRange);
        }

        Debug.Log("발포!!!");
    }
}

 

몬스터 사망

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;

    // 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()
    {
        isDie = true;
        animator.SetTrigger("Die");
        GetComponent<NavMeshAgent>().isStopped = true;

        Destroy(gameObject, 2f);
    }
}

 

 

 

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)
        {
            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.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()
    {
        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;

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

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

    public float genDelayTime; // 생성 주기

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

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

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

 

랜덤생성할때 넓게 생성하기

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

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

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

    public float genDelayTime; // 생성 주기

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

    void EnemyGenTimer()
    {
        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);
    }
}

 

'Unity' 카테고리의 다른 글

[유니티] 3D 슈팅게임 3  (0) 2021.05.28
[유니티] 3D 슈팅게임 2  (0) 2021.05.27
[유니티] 네이게이션  (0) 2021.05.20
[유니티]UI (드롭박스)  (0) 2021.05.20
[유니티] UI/UX Samples  (0) 2021.05.18

댓글