티스토리 뷰
2020/07/07 - [Game : 좋아하는 것/Unity] - Dodge game - 총알 피하기 게임(2)
(2)에 이어서 오늘은 Bullet과 Bullet Spawner를 만들어 봅시다.
01. Bullet 생성
> Bullet Object 생성
Hierarchy 창에서 Create > 3D Object > Sphere 생성한 후에 이름을 'Bullet'으로 변경해준다. Position (0, 3, 0), Scale(0.5, 0.5, 0.5)로 지정해준다.(Position은 나중에 Spawner를 사용해서 생성할 거라서 크게 상관 쓰지 않아도 괜찮다) 전에 Material로 색을 입혔던 것과 동일하게 Bullet에도 색을 입혀주자.
> Bullet의 Rigidbody와 Collider 설정
Bullet에 Rigidbody Component를 add하고 Use Gravity를 체크 해제해준다. Rigidbody를 사용하게 되면 중력이 작용하는데, 우리가 사용할 Bullet은 중력과 상관없이 날아야 하기 때문에 체크 해제가 필수! 안 그러면 계속 밑으로 떨어진다!
Sphere Collider Component는 Bullet의 충돌을 감지해서 다른 Object의 표면에서 튕겨나가거나 지나가지 못하는 등의 행위가 가능하게 해 준다.
* Is Trigger : Trigger를 설정할 경우에는 다른 Collider를 뚫고 가거나 지나가는 게 가능하지만 충돌은 감지한다.
▶︎ Bullet은 중력이 작용하지 않고 계속 떠있어야 하기 때문에 Use Gravity 해제
▶︎ Bullet은 충돌은 감지하지만 Player나 다른 Bullet 혹은 Spawner를 만났을 때 튕겨나가는 일이 없어야 하기에 Is Trigger를 설정.
02. Bullet Script 생성
> PlayerCtrl Script 수정
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
Rigidbody rigidbody;
public float speed = 8f;
Vector3 moveVec;
// Start is called before the first frame update
void Start()
{
rigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
moveVec.x = Input.GetAxis("Horizontal");
moveVec.z = Input.GetAxis("Vertical");
moveVec.y = 0f;
rigidbody.velocity = moveVec * speed;
}
public void Die()
{
gameObject.SetActive(false);
}
}
저번에 만들었던 PlayerCtrl에 Die() 함수를 추가해준다. Die() 함수는 Player와 Bullet이 서로 충돌하면 Player Object가 비활성화되게 한다.
* gameObject.SetActive(false)
- gameObject : 현재 이 Script를 Component로 add한 Object를 뜻한다. -> Player
- SetActive : 현재 gameObject를 활성화 시킬 지, 말 지 결정한다. -> True는 활성화, False는 비활성화
( 비활성화가 되면 해당 Object가 Game Scene에 보이지 않는다. )
> BulletCtrl Script 생성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletCtrl : MonoBehaviour
{
Rigidbody rigidbody;
public float speed = 8f;
// Start is called before the first frame update
void Start()
{
rigidbody = GetComponent<Rigidbody>();
rigidbody.velocity = transform.forward * speed;
}
}
먼저, PlayerCtrl과 동일하게 Rigidbody를 선언해주고 speed는 8로 정한다. 그리고 Start() 함수에서 Bullet의 rigidbody Component를 가져오고 해당 Bullet이 앞으로 나아가는 방향(transform.forward)으로 rigidbody의 velocity를 정한다.
* transform.forward
- transform : 자신의 게임 Object의 Transform Component를 코드 상에서 transform 변수로 즉시 접근할 수 있도록 함
- forward : 게임 Object의 앞쪽 방향을 가르킴
Start() 함수에서 앞으로 나아가는 코드를 넣은 이유는 Player는 사용자가 방향키를 눌러야 움직이는 반면에 Bullet은 자동으로 움직여 나가야 하기 때문에 Start() 함수에서 움직임을 구현해준다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletCtrl : MonoBehaviour
{
Rigidbody rigidbody;
public float speed = 8f;
// Start is called before the first frame update
void Start()
{
rigidbody = GetComponent<Rigidbody>();
rigidbody.velocity = transform.forward * speed;
}
private void OnTriggerEnter(Collider other)
{
if(other.tag == "Player")
{
PlayerCtrl player = other.GetComponent<PlayerCtrl>();
if(player != null)
player.Die();
}
else if(other.tag == "Wall")
{
Destroy(gameObject);
}
}
}
★ OnTriggerEnter() 함수는 두 Collider 중 하나 이상이 Trigger Collider(위 Collider 상에서 Is Trigger를 선택했을 경우) 일 때 실행됩니다. Trigger Collider를 충돌한 Object 두 개 중 하나에만 있어도 둘 다 OnTrigger 계열의 메서드를 실행합니다.
* OnTrigger 계열의 메서드
# OnTriggerEnter(Collider other) : 충돌한 순간
# OnTriggerStay(Collider other) : 충돌하는 동안
# OnTriggerExit(Collider other) : 충돌했다가 분리되는 순간
위의 코드에서는 OnTriggerEnter를 사용했기 때문에 충돌한 순간을 나타낸다.
# OnTriggerEnter 함수 분석
if(other.tag == "Player") // 만약 현재 충돌한 물체의 tag가 "Player"라면
PlayerCtrl player = other.GetComponent<PlayerCtrl>(); // other의 오브젝트에 추가된 PlayerCtrl를 찾아서 가져온다.
if(player != null) // 만약 PlayerCtrl를 가지고 있지 않다면 실행하지 않고 있다면 실행
player.Die(); // PlayerCtrl의 Die() 함수를 실행
else if(other.tag == "Wall") // 만약 현재 충돌한 물체의 tag가 "Wall"이라면
Destroy(gameObject); // 현재 gameObject를 파괴해라!
> Wall tag 설정
위의 코드가 잘 작동하기 위해서는 Wall의 tag를 'Wall'로 설정해줘야 한다. 그렇지 않으면 other.tag == "Wall"에 상응하는 tag가 존재하지 않아서 Wall에 부딪쳐도 사라지지 않는다.
03. Prefab 설정
Bullet에 먼저 Script를 추가해준 후에 Bullet Object를 Asset으로 끌어서 옮기면 Prefab이 된다.(드래그 & 드롭)
* Prefab : 언제든지 재사용할 수 있는 미리 만들어진 게임 Object Asset(현재 Scene뿐만 아니라 다른 Scene에서도 사용 가능)
-> 비슷한 게임 Object를 여러 개 만들 때 매번 다시 설정하는 번거로움을 줄이기 위해서 사용
04. Bullet Spawner 생성
> BulletSpawner 생성
Hierarchy 창에서 Create > 3D Object > Cylinder 생성하고 이름을 "BulletSpawner"로 설정, Position은 (8, 1, 0)로 설정해준다. Bullet Color를 BulletSpawner에 넣어준다.
> BulletSpawner Script 생성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletSpawner : MonoBehaviour
{
[SerializeField]GameObject bulletPrefab;
Transform target;
public float spawnRate;
public float spawnRateMin = 0.5f;
public float spawnRateMax = 3f;
float timeSpawn;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
앞에 사용된 [SerializeField]는 해당 변수가 Private 하지만 Game Inspector상에 public처럼 보이도록 한 것이다. 나중에 bulletPrefab를 GameObject부분에 넣어주어야 해서 해당 부분이 보이도록 [SerializeField]로 적용해줬다.
* Script 속의 변수 선언
- GameObject : 다른 게임 Object를 호출하기 위해서 사용
- Transform : Object 상의 Transform Component를 의미
- SpawnRate : Spawn이 되는 총알의 생성 주기를 의미(Min, Max는 최소, 최대 생성 주기)
- timeSpawn : 최근 생성 시점에서 지난 시간
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletSpawner : MonoBehaviour
{
[SerializeField]GameObject bulletPrefab;
Transform target;
public float spawnRate;
public float spawnRateMin = 0.5f;
public float spawnRateMax = 3f;
float timeSpawn;
// Start is called before the first frame update
void Start()
{
// timeSpawn를 0으로 초기화
timeSpawn = 0f;
// SpawnRate를 최소, 최대Spawn시간 내에서 랜덤으로 생성
spawnRate = Random.Range(spawnRateMin, spawnRateMax);
// PlayerCtrl를 가진 게임 오브젝트의 transform 컴포넌트를
// transform으로 접근해서 target에 할당
target = FindObjectOfType<PlayerCtrl>().transform;
}
// Update is called once per frame
void Update()
{
// 생성 시점에서 지난 시간이 증가
timeSpawn += Time.deltaTime;
// 생성 주기 이상으로 시간이 증가하면
if(timeSpawn >= spawnRate)
{
// 0으로 초기화하고
timeSpawn = 0f;
// BulletPrefab를 transform 위치와 회전으로 생성
GameObject bullet = Instantiate(bulletPrefab, transform.position, transform.rotation);
// 생성된 bullet 게임 오브젝트의 정면 방향이 target를 향하도록 회전
bullet.transform.LookAt(target);
// 다음번 생성 간격을 다시 랜덤 지정
spawnRate = Random.Range(spawnRateMin, spawnRateMax);
}
}
}
* FindObjectOfType() 메서드
: Scene에 존재하는 모든 Object를 검색하여 원하는 타입의 Object를 찾아낸다.
- update에서 사용하기에는 처리 비용이 커서 프로그램이 심각하게 느려짐
- 해당 타입의 Object를 하나만 찾음 ↔ FindObjectsOfType() : 해당 타입의 Objects를 모두 찾아 배열로 반환
* Time.deltaTime
- Update() 실행 사이의 시간 간격을 알기 위해 내장 변수 Time.deltaTime이 사용됩니다.
- 이전 프레임과 현재 프레임 사이의 시간 간격이 자동으로 할당(특정 시점으로부터 시간이 얼마나 흘렀는지 표현)
* Instantiate
: 게임 도중에 실시간으로 Object를 생성할 때 메서드 사용
- 원본 Object를 주면 해당 Object를 복제한 Object를 생성
► Instantiate(원본, 위치, 회전) => bulletPrefab의 복제본이 BulletSpawner의 위치와 회전으로 생성된다.
* LookAt()
: 입력받은 transform의 게임 Object를 바라보도록 자신의 transform 회전을 변경한다.
> BulletSpawner Script에 Prefab 적용
Bullet Spawner에 Bullet Spawner Script를 넣는다. 그러면 해당 이미지와 같은 부분이 나오는데, Bullet Prefab부분이 비어있기 때문에 이 곳에 Prefab를 넣어줘야 한다. 우리가 Spawn 하고자 하는 것은 Bullet Prefab이기 때문에 해당 Prefab를 넣어주자.
05. Prefab 설정
> BulletSpawner Prefab
Bullet Prefab를 넣고 나서 Bullet Prefab과 동일하게 Prefab으로 만들어 준다. 그러면 Bullet Spawner도 Bullet과 같은 Prefab이 된다. 나중에 Spawner를 따로 더 생성하지 않아도 Prefab만 드래그해서 사용하면 현재 만든 Spawner와 동일한 속성을 가진 Spawner를 사용할 수 있다.
Bullet Spawner를 Prefab으로 둔 걸 드래그해서 Hierarchy창에서 사용하는 모습이다.
* Bullet Spawner의 위치(다르게 놔도 상관없다)
# BulletSpawner ( 8, 1, 0 )
# BulletSpawner (1) ( -8, 1, 0 )
# BulletSpawner (2) ( 0, 1, -8 )
# BulletSpawner (3) ( 0, 1, 8 )
똑같이 따라 하지 않아도 된다..!!
위의 과정들을 마치고 Game를 실행시키면 위와 같은 모습으로 실행된다.
'Unity' 카테고리의 다른 글
Dodge Game - 총알 피하기 게임(최종) (0) | 2020.08.02 |
---|---|
Dodge Game - 총알 피하기 게임(4) (0) | 2020.08.01 |
Dodge game - 총알 피하기 게임(2) (0) | 2020.07.07 |
Dodge game - 총알 피하기 게임(1) (0) | 2020.07.06 |