Chatper 2 命令模式 命令是一個具象化(實例化)的方法調用。 A command is a reified method call. 定義一個基類,命令的控制對象由參數傳入: 為不同的游戲命令創建子類: 在輸入處理中,為每個按鈕存儲一個指針,綁定一個命令,且可以靈活替換新的命令: 1 cla ...
Chatper 2 命令模式
命令是一個具象化(實例化)的方法調用。
A command is a reified method call.
定義一個基類,命令的控制對象由參數傳入:
1 class Command 2 { 3 public: 4 virtual ~Command() {} 5 virtual void execute(GameActor& actor) = 0; 6 };
為不同的游戲命令創建子類:
1 class JumpCommand : public Command 2 { 3 public: 4 virtual void execute(GameActor& actor) 5 { 6 actor.jump(); 7 } 8 };
在輸入處理中,為每個按鈕存儲一個指針,綁定一個命令,且可以靈活替換新的命令:
1 class InputHandler 2 { 3 public: 4 void handleInput(); 5 6 // Methods to bind commands... 7 8 private: 9 Command* buttonX_; 10 Command* buttonY_; 11 Command* buttonA_; 12 Command* buttonB_; 13 };
現在,輸入處理返回一個命令指針:
1 Command* InputHandler::handleInput() 2 { 3 if (isPressed(BUTTON_X)) return buttonX_; 4 if (isPressed(BUTTON_Y)) return buttonY_; 5 if (isPressed(BUTTON_A)) return buttonA_; 6 if (isPressed(BUTTON_B)) return buttonB_; 7 8 // Nothing pressed, so do nothing. 9 return NULL; 10 }
接收命令並讓角色執行:
1 Command* command = inputHandler.handleInput(); 2 if (command) 3 { 4 command->execute(actor); 5 }
對游戲中的AI對象也可以簡單地提供命令對象以供執行。
空對象模式:在這裡可以提供一個什麼都不做的命令。
撤銷和重做:
execute中記錄上一次的位置:
1 class Command 2 { 3 public: 4 virtual ~Command() {} 5 virtual void execute() = 0; 6 virtual void undo() = 0; 7 }; 8 class MoveUnitCommand : public Command 9 { 10 public: 11 MoveUnitCommand(Unit* unit, int x, int y) 12 : unit_(unit), 13 xBefore_(0), 14 yBefore_(0), 15 x_(x), 16 y_(y) 17 {} 18 19 virtual void execute() 20 { 21 // Remember the unit's position before the move 22 // so we can restore it. 23 xBefore_ = unit_->x(); 24 yBefore_ = unit_->y(); 25 26 unit_->moveTo(x_, y_); 27 } 28 29 virtual void undo() 30 { 31 unit_->moveTo(xBefore_, yBefore_); 32 } 33 34 private: 35 Unit* unit_; 36 int xBefore_, yBefore_; 37 int x_, y_; 38 };
多次撤銷可以維護一個命令列表和對當前命令的一個引用。
Chapter 3 享元模式
將數據對象切分成兩種類型:
- 不屬於單一實例對象並能被所有對象共用的數據;
- 對每個對象都是唯一的數據。
地形類:
1 class Terrain 2 { 3 public: 4 Terrain(int movementCost, 5 bool isWater, 6 Texture texture) 7 : movementCost_(movementCost), 8 isWater_(isWater), 9 texture_(texture) 10 {} 11 12 int getMovementCost() const { return movementCost_; } 13 bool isWater() const { return isWater_; } 14 const Texture& getTexture() const { return texture_; } 15 16 private: 17 int movementCost_; 18 bool isWater_; 19 Texture texture_; 20 };
使用指向地形對象的網格指針,相同地形的瓦片指向同一地形實例:
1 class World 2 { 3 public: 4 World() 5 : grassTerrain_(1, false, GRASS_TEXTURE), 6 hillTerrain_(3, false, HILL_TEXTURE), 7 riverTerrain_(2, true, RIVER_TEXTURE) 8 {} 9 10 private: 11 Terrain grassTerrain_; 12 Terrain hillTerrain_; 13 Terrain riverTerrain_; 14 15 Terrain* tiles_[WIDTH][HEIGHT]; 16 // Other stuff... 17 };
使用這些地形實例來繪製地面:
1 void World::generateTerrain() 2 { 3 // Fill the ground with grass. 4 for (int x = 0; x < WIDTH; x++) 5 { 6 for (int y = 0; y < HEIGHT; y++) 7 { 8 // Sprinkle some hills. 9 if (random(10) == 0) 10 { 11 tiles_[x][y] = &hillTerrain_; 12 } 13 else 14 { 15 tiles_[x][y] = &grassTerrain_; 16 } 17 } 18 } 19 20 // Lay a river. 21 int x = random(WIDTH); 22 for (int y = 0; y < HEIGHT; y++) { 23 tiles_[x][y] = &riverTerrain_; 24 } 25 }
獲得(x,y)下的地形及屬性:
1 const Terrain& World::getTile(int x, int y) const 2 { 3 return *tiles_[x][y]; 4 } 5 int cost = world.getTile(2, 3).getMovementCost();
Chatper 4 觀察者模式
觀察者模式非常適合於一些不相關模塊之間的通信(成就系統,新手引導)。
觀察者:
1 class Observer 2 { 3 public: 4 virtual ~Observer() {} 5 virtual void onNotify(const Entity& entity, Event event) = 0; 6 }; 7 class Achievements : public Observer 8 { 9 public: 10 virtual void onNotify(const Entity& entity, Event event) 11 { 12 switch (event) 13 { 14 case EVENT_ENTITY_FELL: 15 if (entity.isHero() && heroIsOnBridge_) 16 { 17 unlock(ACHIEVEMENT_FELL_OFF_BRIDGE); 18 } 19 break; 20 21 // Handle other events, and update heroIsOnBridge_... 22 } 23 } 24 25 private: 26 void unlock(Achievement achievement) 27 { 28 // Unlock if not already unlocked... 29 } 30 31 bool heroIsOnBridge_; 32 };
被觀察者:
1 class Subject 2 { 3 private: 4 Observer* observers_[MAX_OBSERVERS]; 5 int numObservers_; 6 }; 7 class Subject 8 { 9 public: 10 void addObserver(Observer* observer) 11 { 12 // Add to array... 13 } 14 15 void removeObserver(Observer* observer) 16 { 17 // Remove from array... 18 } 19 20 // Other stuff... 21 }; 22 class Subject 23 { 24 protected: 25 void notify(const Entity& entity, Event event) 26 { 27 for (int i = 0; i < numObservers_; i++) 28 { 29 observers_[i]->onNotify(entity, event); 30 } 31 } 32 33 // Other stuff... 34 };
單鏈表寫法(避免造成動態記憶體分配):
1 class Subject 2 { 3 Subject() 4 : head_(NULL) 5 {} 6 7 // Methods... 8 private: 9 Observer* head_; 10 }; 11 class Observer 12 { 13 friend class Subject; 14 15 public: 16 Observer() 17 : next_(NULL) 18 {} 19 20 // Other stuff... 21 private: 22 Observer* next_; 23 }; 24 void Subject::addObserver(Observer* observer) 25 { 26 observer->next_ = head_; 27 head_ = observer; 28 } 29 void Subject::removeObserver(Observer* observer) 30 { 31 if (head_ == observer) 32 { 33 head_ = observer->next_; 34 observer->next_ = NULL; 35 return; 36 } 37 38 Observer* current = head_; 39 while (current != NULL) 40 { 41 if (current->next_ == observer) 42 { 43 current->next_ = observer->next_; 44 observer->next_ = NULL; 45 return; 46 } 47 48 current = current->next_; 49 } 50 } 51 void Subject::notify(const Entity& entity, Event event) 52 { 53 Observer* observer = head_; 54 while (observer != NULL) 55 { 56 observer->onNotify(entity, event); 57 observer = observer->next_; 58 } 59 }
C#:event關鍵字,觀察者成為一個代理。