Unity - (4) prefab, 생성과 파괴, audio

이형눈이이상해
이형눈이이상해
∙∙조회 5

1. prefab

1.1. 개요

  • 자주 쓰게 될 주제 1.
  • 클래스처럼 게임 오브젝트계의 클래스의 느낌.
  • 설계도를 짜서 복제품을 사용하는 느낌이다.

<일단 저장만 해두면 계속 꺼내 쓸 수 있다>

  • 작업을 하더라도 Scene 안에서만 돌 수 있는데 설계도를 만들면 어떤 Scene에서든 사용할 수 있다.
  • 공유할 수 있는 작업물의 형태를 만든다는 장점이 있다.
  • 게임이란 것은 프로그램이고 재활용 할 것을 파일로 준비해놓고 그것을 활용 가능하게끔 해야 한다.

<뭐든 준비를 안하는 것보단 준비하는 것이 좋다>

  • 같은 캐릭터를 어느 Scene에서나 사용할 수 있어야 한다.
  • 그런데 계속 복사 붙여넣기 등을 할 순 없는 노릇이다.
  • 미리 데이터로 저장하는 것을 사용해야 한다.

1.2. 사용

  • 드래그 드랍으로 프로젝트 창에 둔다.

  • 이제 Hierachy에 있는 것을 지우고 다시 프로젝트 안에 아까 만든 것을 넣어줄 수 있다.
  • 그래서 프리팹을 기반으로 만들어진 것들을 사용한다.
  • 게임을 만들 때는 특정 맵을 Prefab, UI 등을 미리 설계된 오브젝트 단위를 재활용 할 수 있도록 하는 것이 프리팹, 게임 오브젝트의 설계도이다.

<이렇게 원본을 수정하면 일괄 적용된다>

  • 다른 객체 하나를 고치다가 일괄 적용도 되는데

  • 하지만 구분되어 생성하는 것이 중요하다.
  • 원본이 훼손되면 안될 경우가 있기 때문.

<그러니 오리지널 본을 수정할 때와 아닐 때를 명확히 하자>

  • 이번엔 다른 예제로 만들어보자.

  • 이것을 오브젝트에 아까처럼 객체 하나에다가 넣고 일괄 적용하면 원본에도 적용됨을 볼 수 있음.
  • 게임 오브젝트에 대한 것을 Prefab으로 관리가 가능하다.

  • 이것을 통해 생성을 해볼 것이다. Update로.

  • 이런 형식일 것인데,

  • 이것으로 생성할 수 있고 엄청나게 많은 수가 생성이 된다.
  • 그러나 Destroy를 적용하면

<분명 붙였는데..>

<나노머신 썬>

  • 파괴가 안된다
  • Player 변수에 어떤 것이 들어가있냐면 Player의 프리팹이 들어가있다.
  • 그런데 객체를 없애야 하는데 설계도를 없애봐야 소용이 없다.
  • 내부적으로 List를 써야 한다.

  • 파괴하는 시점에서는 반대로 게임 오브젝트를 꺼내서 제거해야 한다.

  • p를 꺼내오지 않으면 어떤 것을 파괴할지 모르기 때문에 두 줄이 핵심인 셈.
  • 실행은 되는데 한가지 걸린다.
  • 배열 밖으로 벗어나는 것이 찍히기 때문이다.
  • 벗어나면 return을 하면 된다.

2. 생성과 파괴

2.1. 개요

  • 자주 쓰게 될 것 2.
  • 생성 당시에 옵션을 줄 수 있으나 기억해야 할 함수 이름은 Instantiate와 Destroy이다.

2.2. 사용

  • 터렛을 예시로 만들어볼 것이다.

<코가 큰...>

  • 프리팹으로까지의 과정을 진행.
  • 탄환까지 프리팹으로 진행.

  • 다음 스크립트 2개 생성.
  • TurretController, BulletController.

  • Bullet을 만들었을 때 무엇이 필요할까?

  • 이렇게 필요할 것이고 적용을 시키면

  • 이제 TurretController로 이동.

  • 하지만 충돌 처리를 배우고 나면 이런 느낌은 아닐 것이다.
csharp
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TurretController : MonoBehaviour
{
    // 탄환 소환
        // 총구 위치에
    [SerializeField] private GameObject _bulletPrefab;
    [SerializeField] private Transform _muzzlePoint;
    [SerializeField] private float _coolTime;
    [SerializeField] private Transform _playerTransform;
    [SerializeField] private float _rotateSpeed;
    [SerializeField] private float _detectionRange;
    
    //private float _timer;
    // 플레이어가 특정 거리 밖에 있을 때 돈다.
    // 일정 거리 안이면 플레이어 응시.
    private void Update()
    {
        
        //_timer += Time.deltaTime;
        _coolTime += Time.deltaTime;
        
        float distance = Vector3.Distance(transform.position, _playerTransform.position);
        if (distance <= _detectionRange)
        {
            LookAtPlayer();
            if (_coolTime >= 1)
            {
                SpawnBullet();
                _coolTime = 0;
            }
        }
        else
        {
            RotateTurret();
        }
    }

    private void RotateTurret()
    {
        transform.Rotate(Vector3.up, _rotateSpeed * Time.deltaTime);
    }

    private void LookAtPlayer()
    {
        transform.LookAt(_playerTransform);
    }

    private void SpawnBullet()
    {
        // 쿨타임. deltaTime 누적
        /*if(_coolTime >= _timer)
            return;*/
        
        Instantiate(_bulletPrefab, transform);
        
        /*GameObject bullet = Instantiate(_bulletPrefab);
        bullet.transform.position = _muzzlePoint.position;
        bullet.transform.rotation = _muzzlePoint.rotation;*/
        
        //_timer = 0;
    }
        
    
    // 격발음
}

3. Audio

3.1. 설명

  • 오디오 소스와 리스너로 페어.
  • 지금 격발음이 한 번에 들어와야 하는데 음원 파일이라는 것이 나고 유지되는 시간이 필요하고 하나로만 처리하면 끊기고 소리가 나니 여러개가 있을 필요가 있음.

  • 터렛이 아닌 총알이 오디오 소스를 갖는 상황으로 생각해볼 것.

  • 그럼 소스 코드에서 해보자.
csharp
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletController : MonoBehaviour
{
    // 앞으로 날아가는 것
    [SerializeField] private float _speed;
    [SerializeField] private float _destroyTime;
    
    [SerializeField] private AudioSource _audioSource;
    [SerializeField] private AudioClip _audioClip;
    
    private void Awake()
    {
        // 오디오 접근
        _audioSource = GetComponent<AudioSource>();
        _audioSource.loop = false;
        _audioSource.Stop();
        _audioSource.clip = _audioClip;
        _audioSource.Play();
        
        
        Destroy(gameObject, _destroyTime);
    }

    private void Update()
    {
        MoveForward();
    }

    private void MoveForward()
    {
        transform.Translate(Vector3.forward * _speed * Time.deltaTime);
    }
    
    
    // 충돌 체크
    
}

  • 오디오 리스너가 없으면 콘솔에서 경고를 띄워준다.

정리

  • 힙, 메모리에 축적이 되었다가 관리되는 힙 영역을 청소하는데 Frame이 튈 수 있고 그것을 조심해야 한다.

<이 튄다는 것은 아니지만>

  • 생성 파괴를 최소화하는 운용 기법이 필요함.
  • 에셋 스토어에서 에셋을 볼 때 빌트인 환경이 되는지를 확인할 것.
  • 무료 에셋이라고 해도 .gitignore 단에서 차단되게끔 Import라고 폴더를 별도로 만들어 거기에 에셋들을 두고 이 폴더의 하위들이 git에 올라가지 않도록 관리를 하는 것이 좋을 것이다.
  • 에셋들을 패키징하여 별도로 받아서 사용할 것.