Project/Unity 게임 제작

[6] 슈팅 게임 제작 (2)

gapsoo 2023. 7. 6. 02:26

 

[6].1 총알 발사 제작
[6].2 적 이동 & 충돌
[6].3 적 자동 생성
[6].4 적 인공지능

 


[6].1 총알 발사 제작


🔶 PlayerFire 스크립트 생성 및 할당

  • Project 창에 PlayerFire 라는 이름으로 C# 스크립트를 하나 만든다.
  • Player 객체에 드래그 앤 드롭으로 붙여 준다.

Player 객체에 PlayerFire 스크립트 할당

 

 

 

🔶 총알 발사 로직

  • 총의 방아쇠를 당기면 총알 공장에 전화를 하여 생산된 총알을 받아 발사 하는 흐름으로 이해 할 수 있으며 이는 총알 파을 열어 메모리에 올리는 흐름과 같다.
  • 이 흐름을 처리해 주는 함수가 Instantiate 함수.

총알을 발사 할 때의 일어나는 일의 흐름도

 

 

 

🔶 필요 속성 추가

  • 총알을 생성하기 위해 총알 공장이 필요하다.
  • 만들어진 총알을 발사하기 위한 위치가 필요하며 이는 총구로 속성으로 등록한다.

🖥️ PlayerFire.cs 필요속성 설정

public class PlayerFire : MonoBehaviour
{
	//총알 생산할 공장
	public GameObject bulletFactory;
	//총구
	public GameObject firePosition;
}

 

 

 

🔶 Bullet 프리팹 제작

  • Project 창에 폴더를 하나 만들어 이름을 Prefabs 으로 변경
  • 하이어라키의 Bullet 객체를 드래그 앤 드롭으로 Project 창의 Prefabs 폴더에 넣어준다.

Prefabs 폴더 생성
Bullet 을 Prefab 으로 등록

 

 

 

🔶 Bullet 할당

  • 씬에서 Bullet 을 제거한다.
  • PlayerFire 의 Bullet Factory 속성에 Bullet 프리팹을 할당

Bullet Prefab 등록 후 씬 상태
PlayerFire 의 Bullet Factory 속성에 Bullet 프리팹 할당

 

 

 

🔶 총구 제작

  • Player 객체를 선택하고 마우스 우클릭. 
  • Create Empty 를 선택하면 자식으로 빈 게임오브젝트가 등록이 되며, 이름을 FirePosition 으로 변경.

Player 으로 FirePosition 객체 등록

 

 

 

🔶 총구 속성 할당

  • FirePosition 객체를 Player 의 PlayerFire 스크립트에 할당

PlayerFire 의 FirePosition 할당

 

 

 

🔶 사용자가 발사 버튼을 누르면

  • 사용자가 왼쪽 마우스버튼 클릭을 하거나 왼쪽 Ctrl 버튼을 눌렀을때의 입력처리는 “Fire1” 에 해당한다.
  • GetButtonDown 함수를 통해 입력여부를 확인하여 처리를 할 수 있다.

🖥️ PlayerFire.cs 발사 입력처리

public class PlayerFire : MonoBehaviour
{
	. . . (생략) . . .
	void Update()
	{
		//목표: 사용자가 발사 버튼을 누르면 총알을 발사하고 싶다.
		//순서 : 1.사용자가 발사 버튼을 누르면
		// - 만약 사용자가 발사 버튼을 누르면
		if (Input.GetButtonDown("Fire1"))
		{
			//2.총알 공장에서 총알을 만든다.
			//3.총알을 발사한다.
		}
	}
}

 

 

 

🔶 총알 공장에서 총알을 만든다

  • Instantiate 함수의 인자로 총알공장 bulletFactory 를 넣어 주면 총알을 하나 생성 해준다.

🖥️ PlayerFire.cs 총알 생성

public class PlayerFire : MonoBehaviour
{
	. . . (생략) . . .
	void Update()
	{
		//목표: 사용자가 발사 버튼을 누르면 총알을 발사하고 싶다.
		//순서 : 1.사용자가 발사 버튼을 누르면
		// - 만약 사용자가 발사 버튼을 누르면
		if (Input.GetButtonDown("Fire1"))
		{
			//2.총알 공장에서 총알을 만든다.
			GameObject bullet = Instantiate(bulletFactory);
            
			//3.총알을 발사한다.
		}
	}
}

 

 

 

 

🔶 총알을 발사한다

  • 만들어진 bullet 객체에 속성으로 선언한 firePosition 총구의 위치를 할당

🖥️ PlayerFire.cs 총알 발사

public class PlayerFire : MonoBehaviour
{
	. . . (생략) . . .
	void Update()
	{
		//목표: 사용자가 발사 버튼을 누르면 총알을 발사하고 싶다.
        //순서 : 1.사용자가 발사 버튼을 누르면
		// - 만약 사용자가 발사 버튼을 누르면
		if (Input.GetButtonDown("Fire1"))
		{
			//2.총알 공장에서 총알을 만든다.
			GameObject bullet = Instantiate(bulletFactory);
            
			//3.총알을 발사한다.(총알을 총구위치로 가져다 놓기)
			bullet.transform.position = firePosition.transform.position;
		}
	}
}

 

 

 

 


[6].2 적 이동 & 충돌

🔶 Enemy 객체 생성

  • 하이어라키에서 + 버튼을 눌러 그림과 같이 3D Object 의 Cube 를 하나 만들고 이름을 Enemy로 변경

Enemy 게임오브젝트 생성

 

 

 

🔶 Enemy 위치 설정

  • 적이 생성되는 위치는 화면의 위쪽이니 Enemy의 Transform 의 Position 정보를 변경

Enemy Transform 값 설정

 

 

 

 

🔶 Enemy 스크립트 할당

  • Project 창에서 Scripts 폴더에 Enemy 라는 이름의 스크립트를 만든다.
  • 하이어라키의 Enemy 게임오브젝트에 드래그 앤 드롭으로 붙여 준다.

Enemy 스크립트 할당

 

 

 

🔶  이동 코드 작성

  • 이동속도 속성을 선언
  • 방향을 구하고 이동공식으로 코드를 구현한다.

🖥️ Enemy.cs 아래로 이동시키기

public class Enemy : MonoBehaviour
{
	//필요속성 : 이동속도
	public float speed = 5;
    
    void Update()
	{
		// 1. 방향을 구한다.
		Vector3 dir = Vector3.down;
		// 2. 이동하고 싶다. 공식 P = P0 + vt
		transform.position += dir * speed * Time.deltaTime;
	}
}

 

 

 

 

🔶 충돌을 위한 조건

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

 

 

 

 

🔶 충돌체

  • Enemy 게임오브젝트에 붙어 있는 기본 충돌체는 BoxCollider 이다.

Enemy의 충돌체인 Box Collider

 

 

 

🔶 물체끼리 충돌하기

  • Enemy 게임오브젝트에 Physics 항목의 Rigidbody를 클릭해서 컴포넌트를 추가
  • 중력을 사용하지 않게 하도록 Use Gravity 속성을 꺼준다.

Rigidbody 컴포넌트 추가하기
Rigidbody 의 Use Gravity 체크박스 끄기

 

 

 

🔶 충돌한 물체 파괴하기

  • 물체의 충돌을 감지하는 함수는 OnCollisionEnter 함수이며 이곳에서 부딪힌 객체(other) 를 제거하고 본인도 제거한다.

🖥️ Enemy.cs 충돌 처리

public class Enemy : MonoBehaviour
{
	. . . (생략) . . .

	// 충돌 시작
	private void OnCollisionEnter(Collision other)
	{
		// 너죽고
		Destroy(other.gameObject);
		// 나죽자
		Destroy(gameObject);
	}
}

 

 

 

💡여기서... 실행 버튼을 눌러서 충돌시켰더니 씬과 하이어라키에서 그대로 사라짐 ... 

 


[6].3 적 자동 생성

 

🔶 Enemy Prefab 제작 및 제거

  • 하이어라키(Hierarchy)창의 Enemy 게임오브젝트를 프로젝트(Project)창의 Prefabs폴더쪽으로 드래그 앤 드롭해서 프리팹으로 만들어 준다.
  • 하이어라키의 Enemy 게임오브젝트는 Del키를 눌러서 삭제

Enemy 게임오브젝트를 Prefab 으로 등록
Enemy Prefab 등록 후 씬 상태

 

 

 

🔶 역할 정의 및 객체 생성

  • 일정시간 마다 적을 만들어 내도록 하며 EnemyManager의 위치에서 적이 생성 되도록 한다.
  • 하이어라키에서 [+] -> [Create Empty] 을 선택하여 빈 게임오브젝트를 하나 만들고 이름을 EnemyManager로 변경

EnemyManager 게임오브젝트 생성

 

 

 

🔶 기즈모 설정

  • Scene에서 적당한 위치에 배치 후 잘 보이도록 아이콘 기즈모를 설정
  • 아이콘 기즈모 설정은 EnemyManager 게임오브젝트를 클릭한 후 Inspector창의 바로 아래쪽에 있는 큐브 모양의 버튼을 누른다

EnemyManager 위치 변경 및 아이콘 기즈모 설정

 

 

 

🔶 EnemyManager 스크립트 생성 및 할당

  • Project 창에서 Scripts 폴더에 EnemyManager 라는 이름의 스크립트를 만들어 준다.
  • 하이어라키의 EnemyManager 게임오브젝트에 드래그 앤 드롭으로 붙여 준다.

EnemyManager 스크립트 할당

 

 

 

🔶 목표설정

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

 

 

 

🔶 필요 속성 선언

  • “일정시간마다 적을 생성해서 내 위치에 갖다 놓고 싶다” 를 구현하기 위해 필요한 정보는 일정시간, 생성할 적 공장, 그리고 경과된 시간인 현재 시간으로 볼 수 있다.
  • Enemy 프리팹을 EnemyManager 의 Enemy Factory 속성에 할당하여 준다.

🖥️ EnemyManager.cs 필요속성 선언

public class EnemyManager : MonoBehaviour
{
	// 현재시간
	float currentTime;
	// 일정시간
	public float createTime = 1;
	// 적 공장
	public GameObject enemyFactory;
}

EnemyManager 의 Enemy Factory 속성에 Enemy 프리팹 할당

 

 

 

🔶 시간이 흐르다가

  • 시간을 계속 흐르게 하기위해 라이프사이클 함수 중 Update 함수에 내용을 구현한다.
  • deltaTime 을 활용하여 시간을 누적시켜 완성

🖥️ EnemyManager.cs 시간이 흐르다가

public class EnemyManager : MonoBehaviour
{
	. . . (생략) . . .
	void Update()
	{
		// 1. 시간이 흐르다가
		currentTime += Time.deltaTime;
	}
}

 

 

 

 

🔶 만약 현재 시간이 일정시간이 되면

  • 경과 시간이 생성 시간을 초과하면 문장을 구현한다.
  • 단순 C# 이라는 언어로 번역하는 형태임을 인지.

🖥️ EnemyManager.cs 현재시간이 일정시간이 되면

public class EnemyManager : MonoBehaviour
{
	. . . (생략) . . .
	void Update()
	{
    
		// 1. 시간이 흐르다가
		currentTime += Time.deltaTime;
    
		// 2. 만약 현재시간이 일정시간이 되면
		if (currentTime > createTime)
		{
			// 3. 적 공장에서 적을 생성해서
			// 내 위치에 갖다 놓고 싶다
		}
	}
}

 

 

 

 

🔶 적 공장에서 적을 생성해서 내 위치에 갖다 놓고 싶다.

  • 적 공장에서 적을 만들어서 내 위치에 갖다 놓는다.

🖥️ EnemyManager.cs 적 생성하기

public class EnemyManager : MonoBehaviour
{
	. . . (생략) . . .
	void Update()
	{
		// 1. 시간이 흐르다가
		currentTime += Time.deltaTime;
		// 2. 만약 현재시간이 일정시간이 되면
		if (currentTime > createTime)
		{
		// 3. 적 공장에서 적을 생성해서
		GameObject enemy = Instantiate(enemyFactory);
		// 내 위치에 갖다 놓고 싶다.
		enemy.transform.position = transform.position; 
		} 
	}
}

 

 

 

🔶 실행 및 문제 확인

  • 그런데 하이어라키를 보면 적이 여러 개 생성되어 있는 것을 확인할 수 있다.
  • 현재시간이 일정시간이 되고 난 후 계속 일정시간 보다 크기 때문에 적을 계속 생성
  • 이것을 해결하기 위해 현재시간이 일정시간보다 커지면 현재시간을 0으로 초기화해야 한다.

Enemy가 여러 개 생성되는 현상

 

 

 

  • 경과 시간을 초기화 하여 문제 해결

🖥️ EnemyManager.cs 현재시간을 0으로 초기화

public class EnemyManager : MonoBehaviour
{
	. . . (생략) . . .
	void Update()
	{
		// 1. 시간이 흐르다가
		currentTime += Time.deltaTime;
		// 2. 만약 현재시간이 일정시간이 되면
		if (currentTime > createTime)
		{
			// 3. 적 공장에서 적을 생성해서
			GameObject enemy = Instantiate(enemyFactory);
			// 내 위치에 갖다 놓고 싶다.
			enemy.transform.position = transform.position;
			// 현재시간을 0으로 초기화
			currentTime = 0;
		} 
	}
}

 

 

 

🔶 EnemyManager 배치

  • EnemyManager 게임오브젝트를 클릭하고 키보드의 [Ctrl + D]키를 눌러 복제.
  • 네 번 복제해서 다섯 개의 EnemyManager를 만들고 가로로 2유닛(미터) 간격으로 배치해준다.

EnemyManager 게임오브젝트를 복제해서 적절히 배치

 

 

 

🔶 목표 및 필요 속성 추가

  • 목표 : 적이 생성될 때마다 다음 생성 시간을 랜덤하게 하고 싶다.
  • 필요속성 : 최소, 최대시간

🖥️ EnemyManager.cs 최소시간, 최대시간 추가

public class EnemyManager : MonoBehaviour
{
	// 최소시간
	float minTime = 1;
	// 최대시간
	float maxTime = 5;
	. . . (생략) . . .
}

 

 

 

🔶 생성시간 랜덤으로 설정

🖥️ EnemyManager.cs 생성시간을 랜덤하게 적용

public class EnemyManager : MonoBehaviour
{
	. . . (생략) . . .
    
	void Start()
	{
		// 태어날 때 적 생성시간을 설정하고
		createTime = UnityEngine.Random.Range(minTime, maxTime);
	}
	void Update()
	{
		// 1. 시간이 흐르다가
		currentTime += Time.deltaTime;
		// 2. 만약 현재시간이 일정시간이 되면
		if (currentTime > createTime)
		{
			. . . (생략) . . .
			// 적을 생성한 후 적 생성시간을 다시 설정하고 싶다.
			createTime = UnityEngine.Random.Range(minTime, maxTime);
		} 
	}
}

 

 

 


6.[4] 적 인공지능

 

🔶 확률에 의한 분기처리

  • Random 클래스를 이용해서 0~9중 랜덤한 하나의 수를 가져와서 그 값이 3보다 작으면 플레이어방향, 그렇지 않으면 아래방향이 되도록 조건을 만든다.

🖥️ Enemy.cs 확률에 의한 분기처리

public class Enemy : MonoBehaviour
{
	. . . (생략) . . .
	void Start()
	{
		// 0부터 9(10-1) 까지 값중에 하나를 랜덤으로 가져와서
		int randValue = UnityEngine.Random.Range(0, 10);
		// 만약 3보다 작으면 플레이어방향
		if (randValue < 3)
		{
		}
		// 그렇지 않으면 아래방향으로 정하고 싶다.
		else
		{
		}
	}
	. . . (생략) . . .
}

 

 

 

🔶 방향 구하기

  • 먼저 Target 인 Player 를 GameObject.Find 함수를 이용하여 찾는다.
  • Vector 의 뺄샘을 활용하여 원하는 방향을 찾아낸다.
  • 정규화(Normalize) 를 적용하여 Vector 의 크기를 1로 만들어 준다.
  • 타겟 방향이 아닐 경우에는 아래 방향으로 정해준다.

🖥️ Enemy.cs 방향 구하기

public class Enemy : MonoBehaviour
{
	. . . (생략) . . .
	void Start()
	{
		Vector3 dir;
		// 0부터 9(10-1) 까지 값중에 하나를 랜덤으로 가져와서
		int randValue = UnityEngine.Random.Range(0, 10);
		// 만약 3보다 작으면 플레이어방향
		if (randValue < 3)
		{
			// 플레이어를 찾아서 target으로 하고싶다.
			GameObject target = GameObject.Find("Player");
			// 방향을 구하고싶다. target - me
			dir = target.transform.position - transform.position;
			// 방향의 크기를 1로 하고 싶다.
			dir.Normalize();
		}
		// 그렇지 않으면 아래방향으로 정하고 싶다.
		else
		{
			dir = Vector3.down;
		}
	}
	. . . (생략) . . .
}

 

 

  • 지역변수 dir 을 전역 변수로 변경
  • 유니티에서 실행하여 결과 확인

🖥️ Enemy.cs 방향 정해서 그 방향으로 이동

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;
	}
	. . . (생략) . . .
}

 

'Project > Unity 게임 제작' 카테고리의 다른 글

[8] 슈팅 게임 제작 (4)  (0) 2023.07.07
[7] 슈팅 게임 제작 (3)  (0) 2023.07.06
[5] 슈팅 게임 제작 (1)  (6) 2023.07.04
[4] C# 스크립트 (3)  (6) 2023.06.30
[3] C# 스크립트 (2)  (6) 2023.06.29