posted by REDFORCE 2017. 5. 25. 16:51

이번 글에서 다룰 명령 패턴 내용은 실행취소와 재실행에 대한 내용입니다.


어떤 명령 객체가 실행할 수 있다면, 이를 실행취소 할 수 있게 만드는것도 어렵지 않습니다.

게임개발 툴 같은곳에서는 필수적으로 지원이 되야하는 기능입니다. 만약 이 기능이 지원되지 않는다면....


(기획자가 당신을 죽이려들지도...)



그냥 실행취소 기능을 구현하려면 굉장히 어렵습니다. 그러나 명령패턴을 활용하면 이를 쉽게 만드는게 가능합니다. 예를 들어서 턴제 게임에서 이동 취소 기능을 추가한다고 하겠습니다.


이미 명령 객체를 2편에서 명령 객체를 이용해서 입력 처리를 추상화해둔 덕분에, 플레이어 이동도 명령에 캡슐화되어 있습니다. 이제 어떤 유닛을 옮기는 명령을 생각해보도록 하겠습니다.



MoveUnitCommand 클래스는 이전 에졔와 약간 다르게 생겼습니다.

이전 에졔에서는 명령에서 변경하려는 액터와 명령 사이를 추상화로 격리시켰지만 이번에는 이동하려는 유닛과 위치 값을 생성자에서 받아서 명령과 명시적으로 바인드되어 있습니다.

MoveUnitCommand 명령 인스턴스는 '무엇인가를 움직이는' 보편적인(언제든지 써먹을 수 있는) 작업이 아니라 게임에서의 구체적인 실제 이동을 담고있습니다.


2편에서 다룬 에졔 같은 경우, 어떤 일을 하는지를 정의한 명령 객체 하나가 매번 재사용 됩니다.

입력 핸들러 코드에서는 특정 버튼이 눌릴 때마다 여기에 연결된 명령 객체의 execute() 를 호출 했었습니다.


이번에 만든 명령 클래스는 특정 시점에 발생될 일을 표현한다는 점에서 더 구체적입니다.

이를 테면, 입력 핸들러 코드는 플레이어가 이동을 선택할 때마다 명령 인스턴스를 생성해야합니다.



Command 클래스가 일회용이라는 게 장점이라는 걸 이제 명령을 취소할 수 있도록 순수가상함수 undo() 를 정의하여 보면 알 수 있습니다.



Undo( ) 에서는  execute( ) 에서 변경하는 게임상태를 반대로 바꿔주면 됩니다. 이제 MoveUnitCommand 클래스에 실행취소 기능을 넣어보겠습니다.




잘보면 MoveUnitCommand  클래스에 상태가 몇 개 추가되었습니다. 즉 유닛이 이동한 후에는 이전 위치를 알수 없기 때문에, 이동을 취소할 수 있도록 이전 위치를 xBefore_, yBefore_멤버 변수에 따로 저장하도록 합니다.


이제 플레이어가 이동을 취소 할 수 있게 하려면 Ctrl + z 키를 눌러서 취소 하는 것과 같이 undo() 명령을 수행하면 됩니다. 만약 다시 재실행을 하고 싶다면 redo() 함수를 만들어서 사용하면 가능합니다.


끝으로 여러단계의 실행취소를 지원하는 것도 어렵지 않습니다. 방법은 가장 최근 명령만 기억하는대신, 명령 목록을 유지하고 '현재' 명령이 무엇인지만 알고 있으면 됩니다.

유저가 명령을 실행하면, 새로 생성 된 명령을 목록 맨 뒤에 추가하고, 이를 '현재' 명령으로 기억하면 됩니다.