GURU_unity_2주차 슈팅 게임 제작2

2023. 7. 4. 03:17SWU_프로젝트/GURU - unity

이번주도 지난주에 이어서 슈팅게임을 제작해볼 것이다. 

 

오늘 학습내용은 다음과 같다.

5. 적이동 & 충돌
6. 적 자동생성
7. 적 인공지능
8. 충돌 처리

 

5. 적 이동 & 충돌

 

Enemy 객체 생성

Hirerarchy에서 +를 선택, Cube를 누르고 이름을 Enemy로 변경한다.

그리고 적이 생성되는 위치는 화면의 위쪽이니 아래와 같이 Enemy의 Transform 의 Position 정보를 변경해주었다. 

이제 여기에 Enemy에 넣을 Script를 만들어줄 것이다.

이제 이동코드를 Script에 작성해볼 것이다.

Enemy는 아래쪽으로 내려오는 형태로 만들어야하기 때문에 아래와 같은 과정으로 코드를 작성했다. 

이동속도→ public float speed = 5;
방향을 구하고 싶다→ Vector3 dir = Vector3.down;
이동을 하고 싶다 (공식 P = P0 + vt)→ transform.position += dir * speed * Time.deltaTime; 

 

이렇게 적을 완성하였다. 하하

 

 

물체끼리 충돌하기

 

이제 적과 총알의 충돌과 같은 상황시 처리를 해야하는데, 

충동을 위한 조건은 다음과 같다.

  • 몸체에 해당하는 충돌체 Collder 가 필요
  • 물리 역할을 담당할 Rigidbody 가 필요

우선, Enemy를 선택하고 inspector하단에 있는 add component 를 클릭한다.

그리고 rig를 치면 rigidbody가 뜨는데 2d아닌 걸 선택하면 된다.

선택하면  inspector에 이렇게 생겨나는 것을 확인할 수 있다.

주의할 것은 반드시 Use Gravity를 꺼주어야하는 것인데, 끄지않으면 적이 굉장히 빠른 속도로 내려올 수 있기 때문이다.

 

위와 같이 설정을 마치면 아래와 같이 실행된다.

 

충돌한 물체 파괴하기

 

이제 적이 총알을 맞으면 사라질 수 있도록 작성할 것이다.

private void OnCollisionEnter(Collision Collision) 이라고 뜰텐데 저 뒤에 Collision은 뭐가 되든 상관없다.

나는 other로 했다.

Destroy(other.gameObject)
- Destroy 메서드는 오브젝트를 실시간으로 제거하기 위해서 사용하는 것이다.

이렇게 총으로 적을 파괴할 수 있도록 만들었다. 재밌다.

 

5. 적 자동생성

 

이제부터 적이 자동으로 생성되도록 해볼 것이다.

그 전에도 총알이 자동생성 되게했던 것을 기억할 것이다. 총알과 마찬가지로 적도 프리팹을 이용해서 자동생성하도록 해주자.

위 그림처럼 Hierarchy창의 Enemy 게임오브젝트를 프로젝트창의 Prefabs 폴더쪽으로 드래그 해서 프리팹으로 만들어 준다.

그 후 Hierarchy창의 Enemy 게임오브젝트는 Del키를 눌러서 삭제한다.

이제 위 그림처럼 Enemy 오브젝트를 삭제하고 manager역할을 할 Enemymanager오브젝트를 생성할 것이다. 

  • 이는 일정시간 마다 적을 만들어 내도록 하며 EnemyManager의 위치에서 적이 생성 되도록 한다.
  • Hierarchy창에서  [+] -> [Create Empty] 을 선택하여 빈 게임오브젝트를 하나 만들고 이름을 EnemyManager로 변경해준다.
  • position은 보기 좋게 하기위해 Transform 오른쪽 상단에 여기라고 쓰여있는 곳을 누르면 reset할 수 있다.

이제 Scene에서 적당한 위치에 배치 후 잘 보이도록 아이콘 기즈모 를 설정할 것이다.

아이콘 기즈모 설정은 EnemyManager 게임오브젝트를 클릭한 후 Inspector창의 바로 아래쪽에 있는 큐브 모양의 버튼(빨간 박스 모양)을 누르면 된다. 

위 그림처럼 적의 위치를 y=6정도로 바꿔주고, 기즈모를 설정하여 "저 위치쯤에서 Enemy가 생성되겠구나" 하는 표시를 만들어준다.

 

다음은 Project 창에서 Scripts 폴더에 EnemyManager 라는 이름의 스크립트를 만들어줄 것이다.

Hierarchy 창의 EnemyManager 게임오브젝트에 드래그 해서 넣어준다.

 

Scripts를 작성할 때의 목표는 다음과 같다.

일정시간마다 적을 생성해서 내 위치에 갖다 놓고 싶다.
필요 속성 : 일정시간, 현재시간, 적 공장
순서 : 1. 시간이 흐르다가
          2. 만약 현재시간이 일정시간이 되면
          3. 적 공장에서 적을 생성해서 내 위치에 갖다 놓고 싶다.

위처럼 코드를 작성한 후 , 생성된 create Time과 Enemy Factory를 확인 후 , 아까 만들어 놓은 Enemy prefab을 넣어준다.

(Factoty는 오타이다. 수정해줬다.)

그리고, update() 에 currentTime이 경과하도록 하기위해 아래 코드를 추가해주었다.

(경과한 시간을 점점 누적해서 더해주도록 한다.)

아래 그림처럼 코드를 추가해서 넣어주었다.

근데, 이렇게 코드를 만들게 되면 문제가 발생한다.

 

해당 영상은 위 코드의 문제를 보여주는 영상이다.

  • Hierarchy창을 보면 적이 여러 개 생성되어 있는 것을 확인할 수 있다.
  • 현재시간이 일정시간이 되고 난 후 계속 일정시간 보다 크기 때문에 적을 계속 생성하게 된다.
  • 아까 만들었던 위 그림의 함수에 의해서 서로 충돌하면서 적이 사라지게 된다.
  • 이것을 해결하기 위해 현재시간이 일정시간보다 커지면 현재시간을 0으로 초기화해야 한다.

이렇게 currentTime=0으로 초기화해주었다.

 

EnemyManager 배치하기

 

EnemyManager 게임오브젝트를 클릭하고 키보드의 [Ctrl + D]키를 눌러 복제한다. 

네 번 복제해서 다섯 개의 EnemyManager를 만들고 가로로 2유닛(미터) 간격으로 배치해 준다. 

이것들을 2유닛 간격으로 아래와 같이 배치해주는데, Enemymanager기본을 0 기준으로 두고 -4,-2,0,2,4로 position X좌표를 변경해준다.

 

적이 생성될 때마다 다음 생성 시간을 랜덤하게 하고자 코드를 추가할 것이다.

필요속성 : 최소, 최대시간

 

아래의 코드를 추가해주었다.

 createTime = UnityEngine.Random.Range(minTime, maxTime); - 생성시간을 랜덤하게 하기위한 메서드

적을 생성한 후 적 생성시간을 다시 설정하고 싶기때문에 ,

createTime = UnityEngine.Random.Range(minTime, maxTime) 코드를 맨 아래 추가해줄 것이다.

실행해보면 아래와 같이 랜덤한 간격으로 공격하는 것을 확인 할 수있다.

 

 

7. 적 인공지능

 

이제 적이 약 30%의 확률로 player에게 돌진하는 코드를 작성해볼 것이다.

Enemy script에 코드를 작성해준다.

 

 int randValue = UnityEngine.Random.Range(0, 10); 에서 UnityEngine은 굳이 작성하지 않아도된다.

맨 위에 using UnityEngine;을 작성했기 때문이다.

확률에 의한 분기처리

 

  • Random 클래스를 이용해서 0~9중 랜덤한 하나의 수를 가져와서 그 값이 3보다 작으면 플레이어방향, 그렇지 않 으면 아래방향이 되도록 조건을 만든다.
  • 먼저 Target 인 Player 를 GameObject.Find 함수 를 이용하여 찾는다. 
  • Vector 의 뺄샘을 활용하여 원하는 방향을 찾아낸 다.
  • 정규화(Normalize) 를 적용하여 Vector 의 크기를 1로 만들어 준다.
  • 타겟 방향이 아닐 경우(else)에는 아래 방향으로 정해준다.
  • 지역변수 dir 을 전역 변수로 변경해준다.
  • 아래와 같이 생략할 것은 생략해준다.
public class Enemy : MonoBehaviour
{ . . . (생략) . . .
// 방향을 전역변수로 만들어서 Start와 Update에서 사용
Vector3 dir;
void Start()
{
// Vector3 dir; -> 삭제
. . . (생략) . . . }
void Update()
{
// 1. 방향을 구한다.
// Vector3 dir = Vector3.down; -> 삭제
// 2. 이동하고 싶다. 공식 P = P0 + vt
transform.position += dir * speed * Time.deltaTime;
}
. . . (생략) . . . }

 

이렇게 적이 플레이어를 따라서 약 30%의 확률로 돌진하도록 만들어 실행시켜보았다.

 

8. 충돌처리

 

Hierarchy창에서 +버튼을 눌러 3D 오브젝트 cube를 생성해준다.

그리고 DestroyZone_U(up이라는 뜻)이라는 이름으로 변경해준 후 position과 scale을 아래와 같이 수정해준다. 

+ Box collider 에 Is Trigger 체크해준다.(여기에 체크를 했다면 반드시 on Trigger 메서드를 사용해주어야한다.)

 

또한 Rigidbody를 Add component를 통해 추가해주고 Is Kinematic을 체크해준다.

(Rigidbody를 쓴다는 것은 물리적 오브젝트를 사용하겠다는 뜻이다.)

Is Kinematic은 사용자가 지정한 위치에다가 그 오브젝트를 가져다 놓고 싶을때 자동으로 물리적 이동을 막기위해서 사용하는 것이다.

 

[Project – Assets – Scripts] 폴더에 C# 스크립트를 만들 고 이름을 DestroyZone 로 지정하고 , DestroyZone 스크립트를 Hierarchy창에 있는 DestroyZone_U 객체의 컴포넌트로 붙여 준다.

 

이제 스크립트를 작성할 것이다.

 

OnTriggerEnter 함수에 영역안에 들어온 물체를 제거하는 코드를 넣어준다.

public class DestroyZone : MonoBehaviour
{
//영역 안에 다른 물체가 감지될 경우
private void OnTriggerEnter(Collider other)
{
//그 물체를 없애고 싶다.
Destroy(other.gameObject);
}
}

그리고 나서  ctrl+D를 통해 아래와 같이 상하좌우를 만들어준다.

 

상하좌우에 대한 각각의 position값과 scale값이다.

 

이런모양으로 완성된다.

그런데 DestroyZone끼리 모서리가 맞닿아있다.

이렇게 맞닿은 DestroyZone이 서로 충돌해서 사라지지않게하기위해서 나는 아래와 같은 작업을 하였다.

DestroyZone_U를 선택하고 오른쪽 inspector 창에 Layer를 누르면 다음과 같이 Layer를 추가할 수 있게되어있다.

Layer 6번에 DestroyZone을 추가해주었다.

DestroyZone이 생겨났다.

상하좌우 모든영역의 Layer를 DestroyZone으로 변경한다.

 

상단에 Edit메뉴에서 project Settings 를 선택한다.

 

physics를 선택하고 아래로 내리면 , Layer Collision Matrix라는 것이 보이는데 저기에서 DestroyZone끼리 만나는 곳의 체크표시를 없애주면 모서리끼리 만난 부분으로 인해 사라질 우려를 막을 수 있다.

이제부터는 Enemy끼리, Bullet끼리, Player끼리 충돌하여 사라지는 것을 막기위한 작업을 할 것이다.

마찬가지로 Player, Bullet,Enemy 모두 layer를 각자 이름에 맞게 수정해준다.(단, Firepos는 defalut로 둔다.)

그리고 다시 Edit에 project settings에 들어가서 아까와 같은 작업을 해준다.

각자 자기 자신과 충돌하여 사라지지 않도록 체크를 없애고 player와 Bullet이 충돌하여 사라질 필요가 없으니 체크를 없앤다. 또 DestroyZone과 player가 부딪혀도 사라질 필요가 없으므로 체크표시를 없애준다.

 

플레이어가 DestroyZone에 부딪혀도 사라지지 않고, 반면 Enemy는 부딪히면 사라지면서 불필요한 부분들이 정리되었다.

 

오늘은 진짜 게임을 만든 기분이 든다.

쉽지는 않지만 차근차근하다보니 재미있어 시간이 금방간다. 열심히 해야지!