프로젝트해요/게임_개발

[Unity] #9. 슈팅 게임 제작(5)

yenas0 2023. 7. 7. 12:42
반응형

 

 

5번째 글을 마지막으로 슈팅 게임 제작을 마무리 할 예정이다. 이번에는 마지막으로 최적화에 조금 더 신경을 써보기로 했다.

 

목차
1. 싱글톤패턴으로 관리자 만들기
2. 오브젝트풀을 이용한 메모리 관리

 

1. 싱글톤패턴으로 관리자 만들기

 

 

싱글톤패턴

객체의 인스턴스가 오직 한개만 생성되는 패턴을 의미한다.

 

일종의 전역변수, 전역클래스

 

정적변수를 사용해서 만들게 된다.

 

Gameobject.Find를 사용하면 Hierarchy창의 모든 오브젝트를 검색하기 때문에 효율적이지 못하다. 그래서 싱글톤 패턴을 사용한다.

 

 

 

ScoreManager 스크립트를 싱글톤 패턴으로 구현한 것이다.

public class ScoreManager : MonoBehaviour
{
	. . . (생략) . . .
	// 싱글톤 객체
	public static ScoreManager Instance = null;
	// 싱글톤 객체에 값이 없으면 생성된 자기 자신을 할당
	void Awake()
	{
		if(Instance == null)
		{
			Instance = this;
		}
	}
	. . . (생략) . . .
}

 

 

 

 

다음은 싱글톤 객체 참조로 변환하기 이다

Enemy 스크립트로 이동하여 ScoreManager의 Instance 멤버에 접근 하는 것으로 변경한다.

public class Enemy : MonoBehaviour
{
	//1. 적이 다른 물체와 충돌 했으니까.
	private void OnCollisionEnter(Collision collision)
	{
		// 에너미를 잡을 때마다 현재 점수 표시하고 싶다.
		ScoreManager.Instance.SetScore(ScoreManager.Instance.GetScore() + 1);
		//2.폭발 효과 공장에서 폭발 효과를 하나 만들어야 한다.
		GameObject explosion = Instantiate(explosionFactory);
		//3.폭발 효과를 발생(위치) 시키고 싶다.
		explosion.transform.position = transform.position;
		Destroy(collision.gameObject);
		Destroy(gameObject);
	}
}

 

 

 

 

 

get/set 선언을 하고 구현을 해보도록하자.

사용법은 편수의 편의성을 가져오고, 제작은 함수의 몸체를 갖도록 만든다.

 

SoreManager에서 Score이름을갖는 get/set 프로퍼티를 만든다. 

public class ScoreManager : MonoBehaviour
{
	. . . (생략) . . .
	public static ScoreManager Instance = null;
	public int Score
	{
		get
		{
			return currentScore;
		}
		set
		{
			// to do
		}
	}
}

 

 

 

 

Set의 구현부는 SetScore함수의 구현 내용을 복사 붙여넣기 해준다

GetScore함수와 SetScore함수는 제거하여 준다.

public class ScoreManager : MonoBehaviour
{
	public int Score
	{
		. . . (생략) . . .
		set
		{
			// 3.ScoreManager 클래스의 속성에 값을 할당 한다.
			currentScore = value;
			// 4.화면에 현재 점수 표시하기
			currentScoreUI.text = "현재점수 : " + currentScore;
			//목표: 최고 점수를 표시하고 싶다.
			//1.현재 점수가 최고 점수 보다 크니까
			// -> 만약 현재 점수가 최고 점수를 초과 하였다면”
			if (currentScore > bestScore)
			{
				//2.최고 점수가 갱신 시킨다.
				bestScore = currentScore;
				//3.최고 점수 UI 에 표시
				bestScoreUI.text = "최고점수 : " + bestScore;
				// 목표 : 최고점수를 저장하고싶다.
				PlayerPrefs.SetInt("Best Score", bestScore);
			}
		}
	}
}

 

 

 

Enemy 스크립트로 이동하여 Score 변경 코드를 다음 코드와 같이 변경한다.

public class Enemy : MonoBehaviour
{
	//1. 적이 다른 물체와 충돌 했으니까.
	private void OnCollisionEnter(Collision collision)
	{
		// 에너미를 잡을 때마다 현재 점수 표시하고 싶다.
		ScoreManager.Instance.Score++;
		//2.폭발 효과 공장에서 폭발 효과를 하나 만들어야 한다.
		GameObject explosion = Instantiate(explosionFactory);
		//3.폭발 효과를 발생(위치) 시키고 싶다.
		explosion.transform.position = transform.position;
		Destroy(collision.gameObject);
		Destroy(gameObject);
	}
}

 

 

 

 

 

2. 오브젝트풀을 이용한 메모리 관리

 

 

 

PlayerFire에 오브젝트 풀 크기 속성과, 오브젝트 풀을 위한 배열을 추가하여 준다. 

public class PlayerFire : MonoBehaviour
{
	public GameObject bulletFactory;
	// 탄창에 넣을 총알 개수
	public int poolSize = 10;
	// 오브젝트풀 배열
	GameObject[] bulletObjectPool;
}

 

 

목표: 생성될 때부터 오브젝트풀에서 총알을 하나씩 생성

순서

1. 생성

2. 오브젝트풀을 총알 담을 수 있는 크기로 생성

3. 오브젝트 풀에 넣을 총알 개수 만큼 반복

4. 총알 생성

5. 오브젝트풀에 총알 넣기

 

 

 

 

다음과 같이 구현할 수 있다.

public class PlayerFire : MonoBehaviour
{
	. . . (생략) . . .
	void Start()
	{
		// 2. 탄창을 총알 담을 수 있는 크기로 만들어 준다.
		bulletObjectPool = new GameObject[poolSize];
		// 3. 탄창에 넣을 총알 개수 만큼 반복하여
		for (int i = 0; i < poolSize; i++)
		{
			// 4. 총알공장에서 총알 생성한다.
			GameObject bullet = Instantiate(bulletFactory);
            
			// 5. 총알을 오브젝트풀에 넣고싶다.
			bulletObjectPool[i] = bullet;
			// 비활성화 시키자.
			bullet.SetActive(false);
		}
	}

 

 

 

 

 

 

추가적으로 오브젝트 풀을 활용하기 위해 기존 총알을 그때 그때 만들어 발사하던 로직을 수정해야 한다.

 

 

목표: 발사 버튼을 누르면 오브젝트풀에 있는 총알 중 비활성화 된 것을 발사

순서

1. 발사 버튼 누름

2. 오브젝트풀 안에 있는 총알 중에서

3. 비활성화 된 총알을

4. 활성화 시킨다.

 

 

 

public class PlayerFire : MonoBehaviour
{
	. . . (생략) . . .
	//목표
	void Update()
	{
		//1.발사 버튼 누름
		if (Input.GetButtonDown("Fire1"))
		{
			//2.오브젝트풀 안의 총알 중
			for (int i = 0; i < poolSize; i++)
			{
				//3.비활성화 된 총알을
				// - 만약 총알이 비활성화 되었다면
				GameObject bullet = bulletObjectPool[i];
				if (bullet.activeSelf == false)
				{
					//4.활성화 시킨다
					bullet.SetActive(true);
					// 총알을 위치 시키기
					bullet.transform.position = transform.position;
					//총알 발사 하였기 때문에 비활성화 총알 검색 중단
					break;
				}
			}
		}
	}
}

 

 

 

실행해보면 적을 맞추었을 때 그 다음부터 총알이 잘 발사되지 않고 오류가 발생한다. 

따라서 총알을 없애지 말고 다시 비활성화 시켜 오브젝트 풀에 넣어주어야하는 작업이 필요하다. 

 

 

다음과 같이 Enemy 스크립트를 수정한다.

public class Enemy : MonoBehaviour
{
	. . . (생략) . . .
	private void OnCollisionEnter(Collision other)
	{
		. . . (생략) . . .
		explosion.transform.position = transform.position;
		// 만약 부딪힌 객체가 Bullet 인 경우에는 비활성화 시켜 탄창에 다시 넣어준다.
		//1.만약 부딪힌 물체가 Bullet 이라면
		if (other.gameObject.name.Contains("Bullet"))
		{
			//2.부딪힌 물체를 비활성화
			other.gameObject.SetActive(false);
		}
		//3.그렇지 않으면 제거
		else
		{
			Destroy(other.gameObject);
		}
		Destroy(gameObject);
	}
}

 

 

 

 

스크립트를 모두 작성했다면 Rigidbody가 이동 및 회전 처리를 못하도록 막아야한다.

 

Bulelt 프리팹을 더블 클릭하여 프리팹 편집창으로 이동하여 Bullet 객체의 Rigidbody 컴포넌트의 Constraints속성을 체크하여 모두 잠가준다.

 

 

 

 

 

 


 

네..일단 사죄....^^

 

사실 강의 내용의 팔할을 이해하지 못했구요.. 그래서 글이 대충입니다..

 

목표는 총알 오브젝트풀이랑 에너미 오브젝트풀이랑 다 만들고 동적메모리까지 할당해서 메모리 소비좀 줄여볼라했는데 그건 나중에 공부 좀 더 하고나서 시도해보겠습니다,,,,,,,,,, 

 

그냥 램 추가하시구요.. 고사양 컴 사세요..

반응형