-
[디자인패턴] 전략 패턴(Strategy Pattern) - Unity로 게임 개발하기Unity 2024. 12. 15. 20:49
전략 패턴(Strategy Pattern)?
런타임에서 동작을 변경하여 객체에 할당할 수 있도록 하는 디자인 패턴.
날아다니면서 플레이어를 공격하는 드론이 있다고 가정해보자.
해당 드론들은- 좌우비행
- 상하비행
- 전후비행
등 다양한 행동 패턴을 가지고 있을 수 있다.
우리는 이들이 어떤 행동을 해야 할지 정의를 해줄 수 있다.
어떤 드론은 횡비행을 하며 공격하고, 다른 드론은 종비행을 할 수 있도록 지정할 수 있다.
전략 패턴은 이러한 행동이 쉽게 가능하도록 하는 디자인 패턴의 한 종류이다.전략 패턴 이해하기
전략이라는 개별 클래스로 나누어 생각해보자.
위에서 언급된 각각의 전략들을 캡슐화 하여 정의하는 것이 가능하다.1. 좌우비행 - x축으로 이동
2. 상하비행 - y축으로 이동
3. 전후비행 - z축으로 이동해당 전략들은 Strategy 인터페이스를 상속받아, 드론을 정의하는 클래스에서 변경이 가능하도록 구성되어야 한다.
먼저 전략 패턴을 위한 기본적인 구조를 살펴보자
클래스 구조 Client
- Context의 전환을 통해 실제 Strategy가 적용되는 클래스.
Context
- 다양하고 구체적인 Strategy 클래스를 사용
- Strategy 인터페이스로 상호작용Strategy(interface)
- Strategy 클래스가 필수로 상속받아야 하는 인터페이스
- execute() 함수를 담고 있어 Context에서 호출되어야 한다.Strategies
- 알고리즘 및 실제 동작을 런타임에서 구체적으로 구현한 것.
- 실제 동작은 Strategy에서 이뤄진다.예시를 통한 전략패턴 이해하기
위에서 정의한 구조대로 Strategy 패턴을 정의해보자.
목표 : 전략 패턴을 활용하여 특정 동작으로 행동하는 Drone 만들기
- Context: Drone.cs
- Client: DroneCreator.cs
- Strategy
- IStrategy.cs
- DroneMovementStrategy.cs
- Strategies
- ForwardBackwardStrategy.cs
- HorizontalFlightStrategy.cs
- VerticalFlightStrategy.cs
1. Context에서 전략을 등록할 Drone.cs를 정의한다
using UnityEngine; public class Drone : MonoBehaviour { private IStrategy _strategy; public void SetStrategy(IStrategy strategy) { _strategy = strategy; } private void Update() { _strategy?.Execute(); } }
2. IStrategy.cs를 정의한다.
public interface IStrategy { void Execute(); }
3. IStrategy를 상속받으며, 공통속성을 정의할 DroneMovementStrategy.cs를 생성한다.
using UnityEngine; public abstract class DroneMovementStrategy : MonoBehaviour, IStrategy { [SerializeField] protected float moveSpeed = 5f; [SerializeField] protected float moveRange = 3f; protected Vector3 startPosition; protected float currentTime = 0f; protected virtual void Start() { startPosition = transform.position; } public abstract void Execute(); }
4. DroneMovementStrategy.를 상속받는 Strategy 클래스들을 생성해준다.
using UnityEngine; public class ForwardBackwardStrategy : DroneMovementStrategy { public override void Execute() { currentTime += Time.deltaTime; float forwardOffset = Mathf.Sin(currentTime * moveSpeed) * moveRange; transform.position = startPosition + Vector3.forward * forwardOffset; } } public class HorizontalFlightStrategy : DroneMovementStrategy { public override void Execute() { currentTime += Time.deltaTime; float horizontalOffset = Mathf.Sin(currentTime * moveSpeed) * moveRange; transform.position = startPosition + Vector3.right * horizontalOffset; } } public class VerticalFlightStrategy : DroneMovementStrategy { public override void Execute() { currentTime += Time.deltaTime; float verticalOffset = Mathf.Sin(currentTime * moveSpeed) * moveRange; transform.position = startPosition + Vector3.up * verticalOffset; } }
5. Drone을 생성하고 Strategy를 넣어주는 DroneCreator.cs를 정의한다.
using UnityEngine; public class DroneCreator : MonoBehaviour { [SerializeField] private GameObject _dronePrefab; [SerializeField] private Vector3 _centerPosition; [SerializeField] private Vector3 _areaLength; [SerializeField] private Color gizmoColor = new Color(0f, 1f, 0f, 0.2f); // 반투명 초록색 private GUIStyle guiStyle; private void Start() { // GUI 스타일 초기화 guiStyle = new GUIStyle(); guiStyle.fontSize = 24; guiStyle.normal.textColor = Color.white; guiStyle.alignment = TextAnchor.UpperCenter; } private void OnGUI() { // 화면 상단 중앙에 텍스트 표시 GUI.Label(new Rect(Screen.width / 2 - 200, 20, 400, 30), "Press SPACE to spawn a drone", guiStyle); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { int random = Random.Range(0, 3); CreateDrone(random); } } public void CreateDrone(int i) { Vector3 position = new Vector3( Random.Range(_centerPosition.x - _areaLength.x / 2, _centerPosition.x + _areaLength.x / 2), Random.Range(_centerPosition.y - _areaLength.y / 2, _centerPosition.y + _areaLength.y / 2), Random.Range(_centerPosition.z - _areaLength.z / 2, _centerPosition.z + _areaLength.z / 2) ); GameObject drone = Instantiate(_dronePrefab, position, Quaternion.identity); IStrategy strategy = null; switch (i) { case 0: strategy = drone.AddComponent<HorizontalFlightStrategy>(); break; case 1: strategy = drone.AddComponent<VerticalFlightStrategy>(); break; case 2: strategy = drone.AddComponent<ForwardBackwardStrategy>(); break; } drone.GetComponent<Drone>().SetStrategy(strategy); } private void OnDrawGizmos() { Color originalColor = Gizmos.color; Gizmos.color = gizmoColor; Gizmos.DrawCube(_centerPosition, _areaLength); Gizmos.color = new Color(gizmoColor.r, gizmoColor.g, gizmoColor.b, 1f); Gizmos.DrawWireCube(_centerPosition, _areaLength); Gizmos.DrawSphere(_centerPosition, 0.2f); Gizmos.color = originalColor; } }
씬 세팅
1. Drone 프리팹을 생성해준다.
- 기본 3D 오브젝트를 생성하고, 해당 객체의 Drone 컴포넌트를 추가한다.
Drone Component - 해당 오브젝트를 폴더로 끌어놓고 프리팹화 시킨다. 기존에 있는 오브젝트는 삭제해준다.
Drone Prefab 2. DroneCreator 오브젝트를 생성한다.
- 빈 게임오브젝트를 만들고 DroneCreator 컴포넌트를 추가한다.
- DroneCreator에 만들어놓은 Prefab을 추가하고, 영역을 지정해준다.
영역을 지정함에 따라 씬에 기즈모로 영역이 표시된다.
현재 코드에서는 DroneCreator가 CenterPosition이 아니고, 인스펙터에서 값을 통해 지정해줘야 한다.드론 스폰 영역 - 플레이를 누르고 스페이스바를 두들겨본다.
생성된 드론에 Strategy 컴포넌트가 부착되어 있는 것을 확인할 수 있다.랜덤으로 Strategy 클래스 부착
결과물
드론 소환 장면
- Space를 누르면 드론이 소환된다.
- DroneCreator에 의해 각 Drone은 랜덤으로 Strategy가 추가되며, 개별 동작을 진행한다.마무리
자, 지금까지 전략 패턴을 써서 드론의 움직임을 만들어봤다.
생각보다 괜찮은 점이 많았는데 한번 정리해보자.전략 패턴 써보니 좋았던 점
- 실행 중에 행동을 자유롭게 바꿀 수 있다
- 게임 도중에 드론의 움직임을 바꿀 수 있다
- 새로운 움직임을 추가할 때 기존 코드를 건드리지 않아도 된다
- 각각의 움직임을 독립적으로 테스트할 수 있다
- 코드를 재사용하기 좋다
- 만든 움직임을 다른 오브젝트에도 쓸 수 있다
- DroneMovementStrategy에 공통된 것들을 모아둬서 코드 중복도 줄였다
- 관리하기 편하다
- 각각의 움직임이 독립적으로 있어서 관리가 쉽다
- Unity Inspector에서 값 수정이 가능하다
앞으로 개선하면 좋을 것들
- 팩토리 패턴 도입
- 지금은 switch로 전략을 만들고 있는데, 팩토리 패턴을 쓰면 더 깔끔해질 것 같다
- 새로운 전략 추가할 때도 DroneCreator 수정 없이 가능해진다
- 실행 중 전략 변경
- 드론이 움직이는 도중에도 전략을 바꿀 수 있게 만들 수 있다
- 여러개의 Strategy를 드론이 생성되는 부분에서 전부 추가해주고, 특정 키입력 또는 Think 로직을 통한 전략 변경도 가능할 듯 싶다
- 더 다양한 움직임 추가
- 플레이어를 쫓아가는 움직임이나 원형으로 도는 움직임도 추가할 수 있다
- 각 움직임에 공격 패턴도 추가하면 더 재밌어질 것 같다
이번에 전략 패턴을 Unity에 적용해봤는데, Unity의 컴포넌트 시스템이랑 잘 어울리는 것 같다. 앞으로 이런 패턴들을 더 공부해서 활용해봐야겠다.
모두 관리하기 쉬운 코드를 작성합시다.
😘즐코(즐거운 코딩 되시길)!
'Unity' 카테고리의 다른 글
[Unity & iOS] 유니티에서 iOS용 앱 빌드하기 (Feat. 처음 iOS개발할때 발생하는 문제들) (0) 2025.02.14 [Firebase] 유니티에 파이어베이스 연동하기 (5) 2025.02.01 [Addressable] 빌드 오류 해결 방법 - Addressables - Unable to load runtime data at location (6) 2025.01.31 [디자인패턴] 옵저버 패턴(Observer Pattern) - Unity로 게임 개발하기 (2) 2024.12.15 싱글톤이란 무엇인가? - Singleton기초 (6) 2024.12.15