SZU-WenjieHuang / Unity-Design-Pattern

:tea: All Gang of Four Design Patterns written in Unity C# with many examples. And some Game Programming Patterns written in Unity C#. | 各种设计模式的Unity3D C#版本实现

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Design Patterns Written in Unity3D

This repository is about cool design patterns written in Unity3D C#.

  • Now 23 Gang of Four Patterns have all been finished in Unity3D in this repository.
  • Each pattern contains the corresponding structure implementations, application examples and diagrams. Same way with Naphier/unity-design-patterns, in this repository each pattern is contained in a separate folder. Inside these are a folder ("Structure") to show what classes are used in the pattern's structure in Unity3D(with a scene) and a folder or folders ("Example") showing one or more real-world example of using the pattern in Unity3D along with a scene showing it in action. Each pattern folder may contain one or more Example.
  • Game design patterns from book Game Programming Patterns have been partially implemented.

此repo为Unity3D中各种设计模式的实践与运用。

  • 目前已经在Unity中实现了《设计模式:可复用面向对象软件的基础》一书中提出的23种设计模式。
  • 每种模式都包含对应的结构实现、应用示例以及图示介绍。类似Naphier/unity-design-patterns的结构,此repo中的每种模式用单独的文件夹分开。每种模式对应的文件夹中包含了名为“Structure”的子文件夹,里面存放的是此模式在Unity中的使用代码的基本框架实现,而另外包含的Example子文件夹中存放的是此模式在Unity中使用的实际示例。每种框架实现或实例示例实现都包含对应的场景,每种模式文件夹中可能包含一个或者多个Example。
  • 游戏编程模式》一书中介绍的常用游戏设计模式的Unity版实现也有部分实现。

这一个markdown也会记录我自己对于设计模式的认知和笔记,希望能有帮助:)

Contents

I、Gang of Four Patterns in Unity (23种GOF设计模式的Unity实现)

Behavioral Patterns 行为型模式

Structural Patterns 结构型模式

Creational Patterns 创建型模式


II、Game Programming Patterns in Unity (《游戏编程模式》的Unity实现)


Reference resources(参考)

游戏设计模式 Game Programming Patterns

Base Knowledge

Behaviour类与MonoBehaviour类

Behaviour是一个继承自Component的基础类,它是所有在GameObjects上附加的行为组件的基类。这个类本身不能被实例化, 但它提供了一些基础的功能和生命周期事件(例如启用和禁用行为)。

MonoBehaviour是继承自Behaviour的一个类,它添加了许多常用的生命周期事件(例如Start,Update,FixedUpdate等), 以及许多处理用户输入和协程的功能。在Unity中,大部分自定义的游戏脚本都应该继承自MonoBehaviour。

Part1 - Design Patterns Revisited 重访设计模式

1-Command Patterns 命令模式

前置概念:第一公民函数

"第一公民函数"(First-class functions)是函数式编程的一个概念,它指的是在一个编程语言中,函数被当作一等公民(first-class citizens)。这意味着函数可以像其他任何数据类型一样被使用和操作,包括:

1-能够被赋值给变量或数据结构中的元素。
2-能够作为参数传递给其他函数。
3-能够作为函数的返回结果。
4-在运行时能够创建和修改。

例子如下:

#include <iostream>

// 一个函数,接受两个int类型的参数
int add(int x, int y) {
    return x + y;
}

// 另一个函数,接受两个int和一个函数指针
int apply_func(int x, int y, int (*func)(int, int)) {
    return func(x, y);
}

int main() {
    // 调用函数,将add函数作为参数传递
    std::cout << apply_func(5, 7, add) << std::endl;  // 输出 12
    return 0;
}

理解: 命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象, 同时支持可撤消的操作。

优势在于:

1-解耦
比如一些对按键输入的回调函数,我们在把Command 抽象成对象之后,可以把它与具体的按键解耦, 即这个按键的函数我们可以重新定义。同时,我们也可以进一步把命令的执行和使用者解耦。

2-支持撤销 / 重做 / 重放

例子01 解耦 01

void InputHandler::handleInput()
{
  if (isPressed(BUTTON_X)) jump();
  else if (isPressed(BUTTON_Y)) fireGun();
  else if (isPressed(BUTTON_A)) swapWeapon();
  else if (isPressed(BUTTON_B)) lurchIneffectively();
}

02

void InputHandler::handleInput()
{
  if (isPressed(BUTTON_X)) buttonX_->execute();
  else if (isPressed(BUTTON_Y)) buttonY_->execute();
  else if (isPressed(BUTTON_A)) buttonA_->execute();
  else if (isPressed(BUTTON_B)) buttonB_->execute();
}

在这里,有一个基类Command,然后子类XYAB,分别继承基类并实现自己的方法。 同时定义一个InputHandler类来管理这些Command的实例化指针。

class InputHandler
{
public:
  void handleInput();

  // 绑定命令的方法……

private:
  Command* buttonX_;
  Command* buttonY_;
  Command* buttonA_;
  Command* buttonB_;
};

这样我们就能实现每个按键和具体命令实现之间的解耦。

03

Command* InputHandler::handleInput()
{
  if (isPressed(BUTTON_X)) return buttonX_;
  if (isPressed(BUTTON_Y)) return buttonY_;
  if (isPressed(BUTTON_A)) return buttonA_;
  if (isPressed(BUTTON_B)) return buttonB_;

  // 没有按下任何按键,就什么也不做
  return NULL;
}

更进一步,要是希望把执行的角色和和执行时间与command解耦,我们也可以这样操作;命令与角色和 执行的时间分开。

Command* command = inputHandler.handleInput();
if (command)
{
  command->execute(actor);
}

例子02 撤销 重做 重放

撤销与重做的精髓在于两点:
1-将每一个命令都一次性实例化,并配有execute() 和 undo() 两个函数。
2-将连续的命令用一个列表记录。

Command* handleInput()
{
  Unit* unit = getSelectedUnit();

  if (isPressed(BUTTON_UP)) {
    // 向上移动单位
    int destY = unit->y() - 1;
    return new MoveUnitCommand(unit, unit->x(), destY);
  }

  if (isPressed(BUTTON_DOWN)) {
    // 向下移动单位
    int destY = unit->y() + 1;
    return new MoveUnitCommand(unit, unit->x(), destY);
  }

  // 其他的移动……

  return NULL;
}

这里, 向上移动和向下移动这些command,每次触发时都会返回一个实例化之后的对象。

class MoveUnitCommand : public Command
{
public:
  MoveUnitCommand(Unit* unit, int x, int y)
  : unit_(unit),
    xBefore_(0),
    yBefore_(0),
    x_(x),
    y_(y)
  {}

  virtual void execute()
  {
    // 保存移动之前的位置
    // 这样之后可以复原。

    xBefore_ = unit_->x();
    yBefore_ = unit_->y();

    unit_->moveTo(x_, y_);
  }

  virtual void undo()
  {
    unit_->moveTo(xBefore_, yBefore_);
  }

private:
  Unit* unit_;
  int xBefore_, yBefore_;
  int x_, y_;
};

这样子,执行的时候就调用execute(), 撤销的时候就调用undo(); 我们需要有一个列表,记录玩家的一系列命令; 这样就可以实现连续的重做和撤销。

回放的功能,其实就是用不同角度的camera,把某一时间段的一些列操作按照时间点重新回放了一遍。

2-Flyweight Pattern 享元模式

理解:

享元模式(Flyweight Pattern)是一种结构型设计模式,其主要目的是通过共享大量精细粒度的对象来减少应用程序的内存使用。这是通过将对象的内部状态(intrinsic state)和外部状态(extrinsic state)分离达成的。

在享元模式中:

内部状态是对象的固有属性,它不会随环境改变。例如,在字体渲染的例子中,字体的字符、大小、颜色等可以被视为内部状态。

外部状态是对象的变化属性,它可以随环境改变。例如,字体在文档中的位置就是一个外部状态。

享元模式通过共享包含相同内部状态的对象来减少内存使用。当需要一个对象时,享元工厂会检查是否已经有一个具有相同内部状态的对象存在。如果存在,就直接返回这个对象;否则,就创建一个新的对象。

在C#和其他支持引用语义的语言中,这种共享是通过使用对象的引用(或指针)实现的。因此,你可以说享元模式是通过创建一个对象,并在多个地方使用它的引用,来实现对象的共享。

享元模式的关键是如何将对象的状态分解为内部状态和外部状态,以便可以有效地共享具有相同内部状态的对象!

About

:tea: All Gang of Four Design Patterns written in Unity C# with many examples. And some Game Programming Patterns written in Unity C#. | 各种设计模式的Unity3D C#版本实现


Languages

Language:C# 100.0%