https://jpub.tistory.com/1321

 

누구나 할 수 있는 유니티 2D 게임 제작

게임 개발, 유니티, 프로그래밍 모두 처음인 사람을 위한 단 한 권의 책 도서구매 사이트(가나다순) [교보문고] [도서11번가] [알라딘] [예스이십사] [인터파크] [쿠팡] 전자책 구매 사이트(가나다

jpub.tistory.com

2D 게임 제작에 관한 책인듯 해서 도서관에서 빌려서 공부하기로 했습니다.

이 강좌는 어느 정도 유니티를 아시는 분들을 전제로 작성하였습니다.

사실 유니티는 2D모드가 따로 있는게 아니고  3D모드에서 개발하지만 위에서 볼뿐이고 2D 개발을 용이하게 해주는 Sprite같은 기능들이 추가해서 사용할 수 있을 뿐이다.

 

예제파일은 위쪽 출판사에서 다운 받을 수도 있다.

https://bit.ly/unity2d_jpub

 

unity2d_jpub.zip

 

drive.google.com

 

좀비 AI 기능을 구현하는 좀비 스크립트는 다음과 가능 기능을 구현해야 합니다.

LivingEntity에서 제공하는 기본 생명기능

외부에서 초기 능력치 초기화 가능

주기적으로 목표 위치를 찾아 경로갱신

피탄받았을때 효과 재생

트리거 콜라이더를 이용 감지된 적을 공격

사망시 추적 중지

사망시 사망효과 재생

 

  1. 좀비 필드
  2. public LayerMask whatIsTarget; // 추적 대상 레이어
  3. private NavMeshAgent navMeshAgent; // 경로계산 AI 에이전트
  4. public ParticleSystem hitEffect; // 피격시 재생할 파티클 효과
     public AudioClip deathSound; // 사망시 재생할 소리
     public AudioClip hitSound; // 피격시 재생할 소리
  5.     private Animator zombieAnimator; // 애니메이터 컴포넌트, 여기서는 사용안함
        private AudioSource zombieAudioPlayer; // 오디오 소스 컴포넌트, 효과음 재생을 위
        private Renderer zombieRenderer; // 렌더러 컴포넌트, 좀비 색을 변경시 사용
  6.     public float damage = 20f; // 공격력
        public float timeBetAttack = 0.5f; // 공격 간격
        private float lastAttackTime; // 마지막 공격 시점
  7. 추적할 대상이 존재하는지 알려주는 프로퍼티, targetEntity이고 죽지 않을 겨우만 true이므로 생명체를 체크할수 있음
// 추적할 대상이 존재하는지 알려주는 프로퍼티
private bool hasTarget  {
    get {
        // 추적할 대상이 존재하고, 대상이 사망하지 않았다면 true
        if (targetEntity != null && !targetEntity.dead)  {
            return true;
        }     
        return false; // 그렇지 않다면 false
    }
}

private void Awake() {}  사용할 컴포넌트들을 연결합니다.

public void Setup(ZombieData zombieData) {} 좀비의 능력치를 초기화 합니다.

private void Start( UpdatePath() ;) {  // 게임 오브젝트 활성화와 동시에 AI의 추적 루틴 시작 }

private void Update() {    // 추적 대상의 존재 여부에 따라 다른 애니메이션을 재생 }

private IEnumerator UpdatePath() { // 주기적으로 추적할 대상의 위치를 찾아 경로를 갱신 }

타겟도 이동중이므로 타겟이 존재한다면 타겟의 위치를 갱신한다.

                // 추적 대상 존재 : 경로를 갱신하고 AI 이동을 계속 진행
                navMeshAgent.isStopped = false;
                navMeshAgent.SetDestination(targetEntity.transform.position);

private IEnumerator UpdatePath() {
    // 살아있는 동안 무한 루프
    while (!dead)
    {
        if (hasTarget)
        {
            // 추적 대상 존재 : 경로를 갱신하고 AI 이동을 계속 진행
            navMeshAgent.isStopped = false;
            navMeshAgent.SetDestination(
                targetEntity.transform.position);
        }
        else
        {
            // 추적 대상 없음 : AI 이동 중지
            navMeshAgent.isStopped = true;

            // 20 유닛의 반지름을 가진 가상의 구를 그렸을때, 구와 겹치는 모든 콜라이더를 가져옴
            // 단, whatIsTarget 레이어를 가진 콜라이더만 가져오도록 필터링
            Collider[] colliders =
                Physics.OverlapSphere(transform.position, 20f, whatIsTarget);

            // 모든 콜라이더들을 순회하면서, 살아있는 LivingEntity 찾기
            for (int i = 0; i < colliders.Length; i++)
            {
                // 콜라이더로부터 LivingEntity 컴포넌트 가져오기
                LivingEntity livingEntity = colliders[i].GetComponent<LivingEntity>();

                // LivingEntity 컴포넌트가 존재하며, 해당 LivingEntity가 살아있다면,
                if (livingEntity != null && !livingEntity.dead)
                {
                    // 추적 대상을 해당 LivingEntity로 설정
                    targetEntity = livingEntity;

                    // for문 루프 즉시 정지
                    break;
                }
            }
        }

        // 0.25초 주기로 처리 반복
        yield return new WaitForSeconds(0.25f);
    }
}

public override void OnDamage(float damage, }  // 데미지를 입었을때 실행할 처리

public override void Die() { // LivingEntity의 Die()를 실행하여 기본 사망 처리 실행}

private void OnTriggerStay(Collider other) {
        // 자신이 사망하지 않았으며,
        // 최근 공격 시점에서 timeBetAttack 이상 시간이 지났다면 공격 가능 }

Player를 선택하고Layer를 PLAYER로 바꿉니다. 모든 child에 적용할거냐는 질문에 No 합니다.


  Player를 Prefabs 폴더로 끌어다 프리팹으로 만듭니다. 

하이라키뷰에서 Enemy를 선택하고 public을 연결해줍니다.

 

Zombie.cs
0.01MB
Zombie Data.cs
0.00MB

'유니티좀비게임 > 생명과 좀비' 카테고리의 다른 글

좀비(Enemy) 오브젝트 준비  (0) 2023.05.03
네비게이션 시스템  (0) 2023.05.03
플레이어 체력 UI  (0) 2023.04.30
Event  (0) 2023.04.28
객체지향 다형성 LivingEntity  (0) 2023.04.28

하이라키뷰에서 Player를 선택하고 Ctrl-D를 눌러 복사하고 이름을 Enemy로 변경한다.

매터리얼을 하나 만들어 색을 바꿔준다.

컴포넌트는 Transform, Rigidbody, Capsule Collider만 남겨두고 나머지 스크립트는 전부 제거한다.

위치를 변경한다. (0,0,15)

 

Box Collider를 추가하고 Center(0,1,0.76) Size(0.5, 0.5, 0.5)로 변경한다. 좀비의 공격범위로 사용됩니다.

Audio Component를 추가하고 Play On Awake를 언체크 합니다.

Nav Mesh Agent 컴포넌트를 추가합니다.

Zombie 스크립트를 추가합니다.

 

Prefabs폴더의 BloodSparyEffect를 추가합니다. 피탄효과로 사용됩니다.

BloodSprayEffect.prefab
0.20MB

'유니티좀비게임 > 생명과 좀비' 카테고리의 다른 글

좀비 스크립트  (0) 2023.05.03
네비게이션 시스템  (0) 2023.05.03
플레이어 체력 UI  (0) 2023.04.30
Event  (0) 2023.04.28
객체지향 다형성 LivingEntity  (0) 2023.04.28

경로를 자동으로 계산하는 인공지능 좀비를 구현하기 위해 먼저 유니티 내비게이션 시스템을 살펴보고 좀비게임 오브젝트를 준비합니다.

 

하이라키에 큐브를 3x5=15쯤 만들고 배치합니다. 적이 쫒아 올때 장애물로 사용될겁니다.

Stage 빈폴더를 만들고 Plane과 만금 만든 큐브들을 끌어다 넣고 자식으로 만듭니다. Plane과 큐브들을 선택하고 인스펙트뷰에서 static으로 만듭니다. 

 

유니티는 한 위치에서 다른 위치로의 경로를 계산하고 실시간으로 장애물을 피하며 이동하는 인공지능을 만드는 네비게인션 시스템을 제공합니다. 시스템에 사용하는 오브젝트는 크게 다음 4가지 입니다.

  1. 네비메시 : 에이전트가 걸어 다닐 수 있는 표면
  2. 네비메시 에이전트 : 캐릭터 또는 컴포넌트
  3. 네비메시 장애물 : 에이전트의 경로를 막는 장애물
  4. 오프메시 링크 : 끊어진 내비메시 영역 사이를 잇는 연결 지점(뛰어넘을 수 있는 울타리나 타고 올라갈수있게)

네비메시 빌드

  1. 네비메시 굽기
    1. 네비게이션 창 열기 > Window > AI > Navigation
    2. 네비게이션 창에서 Bake 탭 클립, 잘 구어지면 다음과 같이 다닐수 있는 곳은 마스크가 생긴다.

 

'유니티좀비게임 > 생명과 좀비' 카테고리의 다른 글

좀비 스크립트  (0) 2023.05.03
좀비(Enemy) 오브젝트 준비  (0) 2023.05.03
플레이어 체력 UI  (0) 2023.04.30
Event  (0) 2023.04.28
객체지향 다형성 LivingEntity  (0) 2023.04.28

플레이어 캐릭터의 체력을 구현하기 전에 먼저 체력을 띄울 UI를 구현하겠습니다. 체력은 원형 슬라이더로 플레이어 캐릭터의 몸체에 표시됩니다.

  1. UI 슬라이더 준비
    1. 새 슬라이더 오브젝트 생성 : Create > UI > Slider
    2. 하이라키창에서 Cavas 게임오브젝트 선택
    3. Cavas 컴포넌트의 Render Mode를 Word Space로 변경 -> 플레이어 캐릭터를 따라 다녀야 하므로
    4. Canvas Scaler 컴포넌트의 Reference Pixels per Unit를 1로 변경 -> 깔끔하게 보임
  2. 캔버스의 위치와 크기 설정
    1. Canvas 게임 오브젝트를 Player Chracter 게임 오브젝트의 자식으로 만들기
    2. Canvas 게임 오브젝트 선택
    3. Rect Transform 컴포넌트의 위치를 0, 0.3, 0 Width와 height를 1로 변경
    4. Rect Transform 컴포넌트의 Rotation을 (90, 0, 0)으로 변경
  3. 슬라이더 크기 변경
    1. 하이라키창 Canvans 옆 펼치기 버튼을 Alt+클릭로 펼침
    2. Handle Slide Area 게임 오브젝트를 Delete
    3. Slider, Background, Fill Area, Fill 게임 오브젝트를 모두 선택
    4. 앵커 프리셋 클릭 > Alt를 누른채 우측 하단의 stretch클릭
  4. 슬라이더 컴포넌트 설정
    1. Slider게임 오브젝트 선택> 게임 오브젝트의 이름을 Health Slider로 변경
    2. Slider 컴포넌트에서 Interactable 체크 해제 - 상호작용 못하게
    3. Transition을 None으로 변경 - 색반응 못하게
    4. Max Value의 Value를 100으로 변경
  5. 슬라이더 배경 이미지 변경
    1. 하이라키창 Background게임 오브젝트 선택
    2. Image컴포넌트의 Source Image에 Health Circle 스프라이트 할당
    3. Image 컴포넌트의 칼라필드 클릭 > 알파를 30으로 변경
  6. 슬라이더 Fill이미지 변경
    1. Fill 게임 오브젝트 선택
    2. Image 컴포넌트의 Soruce Image에 Health Circle 스프라이트 할당
    3. Image 컴포넌트의 Color 필드 클릭> 컬러를 (255,0,0,150)으로 변경
    4. Image Type을 Filled로 변경
  7. PlayerHealth 스크립트
    1. LivingEntity의 생명체 기본 기능
    2. 체력이 변경되면 체력 슬라이더에 반영
    3. 공격받으면 피격 효과음 재생
    4. 사망시 플레이어의 다른 컴포넌트를 비활성화
    5. 사망시 사망효과음과 사망 애니메이션 재생
    6. 아이템을 감지하고 사용
  8. 스크립트 설명
    1. PlayerHealth 클래스는 LivingEntity를 상속합니다.
    2. healthSlider를 연결합니다.
    3. Awake() : 사용할 컴포넌트를 가져옵니다.
    4. LivingEntity로 상속된 메서드를 오버라이드 합니다.
    5. public override void OnEnable() : PlayerHealth컴포넌트가 활성화될 때마다 체력상태를 리셋하는 처리를 구현
      1. base.OnEnalbe()에 의해 부모메서드 health = startingHealth 가 먼저 실행됨
      2. 체력 슬라이드, Player, shotter등을 활성화합니다.
    6. public override void RestoreHealth()
      1. LivingEntity클래스의 ResotreHealth()를 오버라이드 합니다.
      2. 체력을 회복하는 처리는 이미 상속되었고 여기서는 슬라이더에 반영하는 처리를 추가합니다.
    7. public override void OnDamage()
      1. 상속되는 기능외에 효과음을 재생하고 체력 슬라이더를 갱신합니다.
    8. public override void Die()
      1. 상속되는 기능외에 사망 애니메이션과 효과음을 재생하고 체력 슬라이더를 비활성처리합니다.
    9. OnTriggerEnter()
      1. 트리거 충돌한 상대방 게임 오브젝트가 아이템인지 판단하고 아이템을 사용하는 처리
      2.  충돌한 객체를 IItem item으로 받아 item.Use()메서드로 느슨한 인터페이스 실행
private void OnTriggerEnter(Collider other) { 
    IItem item = other.GetComponent<IItem>(); 
    item.Use(gameObject);

PlayerHealth.cs 전체코드 Player게임오브젝트에 추가.

using UnityEngine;
using UnityEngine.UI; // UI 관련 코드

// 플레이어 캐릭터의 생명체로서의 동작을 담당
public class PlayerHealth : LivingEntity {
    public Slider healthSlider; // 체력을 표시할 UI 슬라이더

    public AudioClip deathClip; // 사망 소리
    public AudioClip hitClip; // 피격 소리
    public AudioClip itemPickupClip; // 아이템 습득 소리

    private AudioSource playerAudioPlayer; // 플레이어 소리 재생기
    private Animator playerAnimator; // 플레이어의 애니메이터

    private PlayerMovement playerMovement; // 플레이어 움직임 컴포넌트
    private PlayerShooter playerShooter; // 플레이어 슈터 컴포넌트

    private void Awake() {
        // 사용할 컴포넌트를 가져오기
        playerAnimator = GetComponent<Animator>();
        playerAudioPlayer = GetComponent<AudioSource>();

        playerMovement = GetComponent<PlayerMovement>();
        playerShooter = GetComponent<PlayerShooter>();
    }

    protected override void OnEnable() {
        // LivingEntity의 OnEnable() 실행 (상태 초기화)
        base.OnEnable();

        // 체력 슬라이더 활성화
        healthSlider.gameObject.SetActive(true);
        // 체력 슬라이더의 최대값을 기본 체력값으로 변경
        healthSlider.maxValue = startingHealth;
        // 체력 슬라이더의 값을 현재 체력값으로 변경
        healthSlider.value = health;

        // 플레이어 조작을 받는 컴포넌트들 활성화
        playerMovement.enabled = true;
        playerShooter.enabled = true;
    }

    // 체력 회복
    public override void RestoreHealth(float newHealth) {
        // LivingEntity의 RestoreHealth() 실행 (체력 증가)
        base.RestoreHealth(newHealth);
        // 체력 갱신
        healthSlider.value = health;
    }


    // 데미지 처리
    public override void OnDamage(float damage, Vector3 hitPoint,
        Vector3 hitDirection) {
        if (!dead)
        {
            // 사망하지 않은 경우에만 효과음을 재생
            playerAudioPlayer.PlayOneShot(hitClip);
        }

        // LivingEntity의 OnDamage() 실행(데미지 적용)
        base.OnDamage(damage, hitPoint, hitDirection);
        // 갱신된 체력을 체력 슬라이더에 반영
        healthSlider.value = health;
    }

    // 사망 처리
    public override void Die() {
        // LivingEntity의 Die() 실행(사망 적용)
        base.Die();

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

        // 사망음 재생
        playerAudioPlayer.PlayOneShot(deathClip);
        // 애니메이터의 Die 트리거를 발동시켜 사망 애니메이션 재생
        playerAnimator.SetTrigger("Die");

        // 플레이어 조작을 받는 컴포넌트들 비활성화
        playerMovement.enabled = false;
        playerShooter.enabled = false;
    }

    private void OnTriggerEnter(Collider other) {
        // 아이템과 충돌한 경우 해당 아이템을 사용하는 처리
        // 사망하지 않은 경우에만 아이템 사용가능
        if (!dead)
        {
            // 충돌한 상대방으로 부터 Item 컴포넌트를 가져오기 시도
            IItem item = other.GetComponent<IItem>();

            // 충돌한 상대방으로부터 Item 컴포넌트가 가져오는데 성공했다면
            if (item != null)
            {
                // Use 메서드를 실행하여 아이템 사용
                item.Use(gameObject);
                // 아이템 습득 소리 재생
                playerAudioPlayer.PlayOneShot(itemPickupClip);
            }
        }
    }
}

PlayerHealth 컴포넌트 설정

Audios.zip
3.71MB

 

 

'유니티좀비게임 > 생명과 좀비' 카테고리의 다른 글

좀비 스크립트  (0) 2023.05.03
좀비(Enemy) 오브젝트 준비  (0) 2023.05.03
네비게이션 시스템  (0) 2023.05.03
Event  (0) 2023.04.28
객체지향 다형성 LivingEntity  (0) 2023.04.28

+ Recent posts