게임 프로그래밍 패턴 2장
명령
명령 패턴은 메서드 호출을 실체화(객체화)한 것이다.
객체의 형태로 캡슐화함으로써 해당 메서드는 매개변수로 활용할 수 있게 된다.
게임에서 입력키를 변경하는 예제를 봐보자
# InputController.cs
MonoBehaviour에서 Update로 Input을 받는 기본적인 방법이다.
using UnityEngine;
public class InputController : MonoBehaviour
{
private void Update()
{
if (Input.GetKeyDown(KeyCode.X)) Jump();
else if (Input.GetKeyDown(KeyCode.A)) Dash();
else if (Input.GetKeyDown(KeyCode.B)) Fire();
}
}
작동은 이상없지만 유저가 인게임에서 키 변경을 하고 싶을 경우 지원을 못하는 코드 형식이다.
어떤 키에 Jump(), Dash() 같은 메서드를 직접 호출하는 것이 아니라 객체를 넘겨 상황에 맞게 바꿔줄 수 있게 하면 된다.
# Command.cs
추상 클래스 Command를 만들고 하위 클래스에서 해당 명령의 실제 동작을 구현한다.
using UnityEngine;
public abstract class Command
{
public virtual void Execute();
}
public class JumpCommand : Command
{
public override void Execute()
{
Jump();
}
}
public class DashCommand : Command
{
public override void Execute()
{
Dash();
}
}
public class FireCommand : Command
{
public override void Execute()
{
Fire();
}
}
# InputController.cs
Key에 메서드를 직접 연결하는 것이 아니라 Command 객체를 연결했다.
이제 각 Command 객체를 다른 Command를 넣어 키에 해당하는 동작을 바꿔줄 수 있다.
using UnityEngine;
public class InputController : MonoBehaviour
{
private Command _buttonXCommand;
private Command _buttonACommand;
private Command _buttonBCommand;
private void Update()
{
HandleInput();
}
private void HandleInput()
{
if (Input.GetKeyDown(KeyCode.X)) _buttonXCommand.Execute();
else if (Input.GetKeyDown(KeyCode.A)) _buttonACommand.Execute();
else if (Input.GetKeyDown(KeyCode.B)) _buttonBCommand.Execute();
}
private void SetCommandX(Command newCommand)
{
_buttonXCommand = newCommand;
}
}
위의 코드는 잘 작동하지만 한계가 있다.
InputController에서 명령을 실행할 캐릭터 객체를 알고 있어야 하는 커플링의 문제다.
메서드를 실행할 객체를 밖에서 전달해주어 유연하게 작동시켜 보자.
# Command.cs
캐릭터 객체인 Actor을 Command.Execute의 인자로 받아 해당 Actor의 메서드를 호출해준다.
using UnityEngine;
public abstract class Command
{
public virtual void Execute(Actor actor);
}
public class JumpCommand : Command
{
public override void Execute(Actor actor)
{
actor.Jump();
}
}
public class DashCommand : Command
{
public override void Execute(Actor actor)
{
actor.Dash();
}
}
public class FireCommand : Command
{
public override void Execute(Actor actor)
{
actor.Fire();
}
}
이제 같은 Command 객체로 게임에 등장하는 여러 Actor에게 명령을 전달할 수 있다.
# InputController.cs
HandleInput에서는 어떤 Actor를 넘겨줘야 할지 모르기 때문에 Command.Execute를 실행할 수 없다.
유저 Input에 따라 Command 객체만 받아오고, Actor 객체를 넘겨준다.
using UnityEngine;
public class InputController : MonoBehaviour
{
private Command _buttonXCommand;
private Command _buttonACommand;
private Command _buttonBCommand;
private Actor _actor;
...
private void Update()
{
var command = HandleInput();
if (command != null && _actor != null)
command.Execute(_actor);
}
private Command HandleInput()
{
if (Input.GetKeyDown(KeyCode.X)) return _buttonXCommand;
else if (Input.GetKeyDown(KeyCode.A)) return _buttonACommand;
else if (Input.GetKeyDown(KeyCode.B)) return _buttonBCommand;
return null;
}
public void SetActor(Actor actor)
{
_actor = actor;
}
}
이로써 Actor만 바꾸면 플레이어 Input에 따라 어떤 Actor든 조종이 가능하게 되었다.
'Design Pattern' 카테고리의 다른 글
프로토타입 (Prototype) (0) | 2020.07.19 |
---|---|
관찰자 (Observer) (0) | 2020.07.05 |
경량 (Flyweight) (0) | 2020.06.24 |