Prototype Pattern 프로토타입 패턴
위 글은 이재환님의 게임 디자인 패턴 with Unity 인프런 강의를 듣고 남긴 필기입니다.
Prototype Pattern
UML
- Prototype을 상속받아
clone()
메소드를 구현하는 ConcretePrototype으로 구성되어 있음. - 원형을 하나 가지고 있고, 원형의 Prototype을 상속받아
clone()
메소드를 구현한 상태라면 Client에서 원할 때 원형으로 클론을 만들어 사용할 수 있다.
사용하는 이유
- Prototype pattern은 비슷한 오브젝트를 지속적으로 생성해야 할 때 유용하게 사용할 수 있다.
- 오브젝트를 직접 생성할 때에는 높은 비용이 들기 때문에, 이를 대체할 수 있는 유용한 패턴.
- 원본 오브젝트로부터 새로운 오브젝트를 만들어내며, 각 객체에 따라 데이터 수정이 가능한 메커니즘을 함께 제공한다.
- 동적 클래스 생성
- 이 패턴의 중요한 키워드가 된다.
- 약간의 설정 변경으로 비슷하지만 다른 클래스로의 확장이 가능한 경우, 세부 클래스를 미리 명세하지 않고 런타임에 원형을 복제하고, 그 복사본을 수정함으로써 ‘동적 클래스 생성’을 가능케 하는 패턴이 Prototype 패턴.
예제 1 소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface IUnit
{
IUnit Clone();
}
public class Marine : IUnit
{
public int Hp { get; set; }
public int AttackPower { get; set; }
public IUnit Clone()
{
// 얕은 복사
return this.MemberwiseClone() as IUnit;
}
}
public class Firebat : IUnit
{
public int Age { get; set; }
public int AttackPower { get; set; }
public IUnit Clone()
{
return this.MemberwiseClone() as IUnit;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using UnityEngine;
public class UnitManager : MonoBehaviour
{
private void Start()
{
Marine marine = new Marine();
marine.Hp = 25;
marine.AttackPower = 5;
// marine을 clone 메소드로 복사하자
Marine marineClone = marine.Clone() as Marine;
marineClone.Hp = 30;
marineClone.AttackPower = 6;
Debug.Log("Marine의 스텟");
Debug.Log($"HP : {marine.Hp} / AttackPower : {marine.AttackPower}");
Debug.Log("복제된 Marine의 스텟");
Debug.Log($"HP : {marineClone.Hp} / AttackPower : {marineClone.AttackPower}");
}
}
IUnit
이라는 인터페이스로, 이를 상속하는 클래스에게 스스로를 복제하는 메소드를 구현하게 하고,- 위 예제에서는 얕은 복사를 이용해 새로운 객체를 만들어낸다.
- 하나의 객체를 생성할 때,
new
키워드를 사용하는 것과 같은 방식으로 직접 생성하는 것 보다, 이미 생성된 객체를 원본으로 삼아 복제를통해 만들어낸다는 것이 중요 아이디어이다. - 이러한 방식은 생성, 초기화 과정이 복잡한 객체를 생성할 때 도움이 되며 객체의 생성과 파괴의 빈도를 줄일 수 있어 GC의 부담을 줄이고 성능을 개선할 수 있다.
이러한 아이디어는 유니티에 이미 프리팹 (Prefab)
이라는 형태로 구현되어있다.
프리팹의 형태로 오브젝트의 원본을 저장하고, 이를 instantiate
함으로써 복제하여 새로운 객체를 생성해낸다.
예제 2 소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Marine : MonoBehaviour
{
private void Start()
{
float r = Random.Range(0.0f, 1.0f);
float g = Random.Range(0.0f, 1.0f);
float b = Random.Range(0.0f, 1.0f);
Renderer[] rends = GetComponentsInChildren<Renderer>();
foreach (var rend in rends)
rend.material.color = new Color(r, g, b, 0f);
}
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.name == "Plane")
{
Debug.Log("OnCollisionEnter");
Destroy(gameObject);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UnitManager : MonoBehaviour
{
public GameObject unit;
public Transform tr;
public void CreateUnit()
{
Vector3 pos = tr.position + Random.insideUnitSphere * 3;
pos.y = tr.position.y;
GameObject obj = Instantiate(unit, pos, quaternion.identity);
}
}
- 이 패턴의 개념은 이미 유니티에 너무 잘 구현되어있기 때문에, 코드는 굉장히 단순하다.
- 프리팹을 기준으로
instantiate
의 반복. 그 뿐이다. Marine
은 초기값으로 매번 다른 색을 갖기 때문에, 각각의Marine
은 다른 색을 갖게 됨을 알 수 있다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.