Design Pattern

명령 (Command)

coucou3 2020. 6. 18. 01:56
반응형

게임 프로그래밍 패턴 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