Command Pattern và ứng dụng trong Unity – Phần 1
Trong lập trình game, việc tương tác, điều khiển của người chơi với game là rất quan trọng. Do vậy, command pattern được sử dụng rộng rãi và phổ biến.
Command pattern là gì?
“In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.”
Theo wikipedia
Lý thuyết là vậy. Song, command pattern có thể ứng dụng được vào những gì trong lập trình game mới là điều mình muốn đề cập đến. Trong phạm vi bài viết này, mình sẽ giải thích và hướng dẫn một số ứng dụng dưới đây:
- Xây dựng hệ thống điều khiển, tương tác với nhân vật trong game.
- Cho phép người chơi sửa đổi các nút bấm mặc định theo ý mình.
- Lưu và diễn lại các hành động của nhân vật.
Xây dựng hệ thống điều khiển trong game
Một lỗi mà những ai mới bắt đầu học lập trình game (kể cả mình) thường mắc phải đó là code “cứng” phần điều khiển nhân vật vào class của nhân vật.
Về cơ bản thì code thế này khá ổn cho đến khi có những phát sinh như bạn muốn thay đổi phim điều khiển mà không phải code lại, chơi coop với bạn bè trên cùng một máy,…
Đây là một ví dụ để các bạn dễ hình dung hơn về command pattern:
Giới thiệu đủ rồi, bây giờ chúng ta cùng bắt tay vào code trong Unity.
Ở đây, mình sẽ tạo một class Unit với những hành động cơ bản là nhảy, bắn và thay đổi vũ khí.
public class Unit : MonoBehaviour
{
public virtual void Jump()
{
}
public virtual void Fire()
{
}
public virtual void SwapWeapon()
{
}
}
Đầu tiên là tạo một class abstract BaseCommand để cấu trúc cơ bản cho một command:
public abstract class BaseCommand
{
public abstract void Execute(Unit unit);
}
Sau đó, command cho các hành động khác đều kế thừa từ lớp BaseCommand:
public class FireCommand : BaseCommand
{
public override void Execute(Unit unit)
{
unit.Fire();
}
}
public class JumpCommand : BaseCommand
{
public override void Execute(Unit unit)
{
unit.Jump();
}
}
public class SwapCommand : BaseCommand
{
public override void Execute(Unit unit)
{
unit.SwapWeapon();
}
}
Vậy là chúng ta đã dựng xong các lệnh command cơ bản. Như mình đã nói ở trên là không nên code “cứng” phần Input vào trong class Unit. Do đó, chúng ta sẽ tạo một GameObject riêng biệt là InputController và class InputController.
Cuối cùng chúng ta chỉ cần viết hàm xử lý Input trong hàm Update của InputController là xong.
public class InputController : MonoBehaviour
{
public Unit unitControl;
public KeyCode keyJump;
public KeyCode keyFire;
public KeyCode keySwapWeapon;
private JumpCommand jumpCmd = new JumpCommand();
private FireCommand fireCmd = new FireCommand();
private SwapCommand swapCmd = new SwapCommand();
private void Update()
{
ProcessInput();
}
private void ProcessInput()
{
if (Input.GetKeyDown(keyJump))
{
jumpCmd.Execute(unitControl);
}
else if (Input.GetKeyDown(keyFire))
{
fireCmd.Execute(unitControl);
}
else if (Input.GetKeyDown(keySwapWeapon))
{
swapCmd.Execute(unitControl);
}
}
}
Vậy phải làm sao để thay đổi phím điều khiển hay đổi sang điều khiển nhân vật khác. Những hàm xử lý đó chúng ta chỉ cần viết thêm vào InputController là được.
Để thay đổi phím, chúng ta cần thêm enum để phân biệt các loại phím tương ứng với các hành động của unit;
// Thay đổi unit điều khiển
public void ChangeUnitControl(Unit newUnit)
{
unitControl = newUnit;
}
// Thay đổi phím điều khiển
public void ChangeKeyCode(KeyAction keyAction, KeyCode newKeyCode)
{
switch (keyAction)
{
case KeyAction.Jump:
keyJump = newKeyCode;
break;
case KeyAction.Fire:
keyFire = newKeyCode;
break;
case KeyAction.SwapWeapon:
keySwapWeapon = newKeyCode;
break;
}
}