OOP中的正确方式.游戏示例.Player::walk或Map::playerWalk

Correct way in OOP. Game example. Player::walk or Map::playerWalk?

本文关键字:Player walk playerWalk Map 游戏 方式 OOP      更新时间:2023-10-16

让我们假设有一个游戏。有一个地图类和一个玩家类。地图存储字段,字段存储玩家。这将是OOP中正确的做法。负责玩家行走的方法是player::walk还是Map::playerWalk?

关于第一个例子(Player::walk),这似乎是正确的做法,也是现实生活中的相似之处——它的玩家走路,然而,它必须通过地图实例访问目的地字段,检查它是否可以走到那里,删除它的起始字段并添加它的目的地字段。我觉得Player会"知道太多"。

最终这是一个设计问题,两者都可以很好地适应OOP范式。

我倾向于把方法放在语义上最有意义的类上。在这种情况下,这意味着Player::walk,除非地图做了一些事情来让"玩家"移动(即在鳍状肢游戏中,游戏板让球[又名"玩家"]移动),否则该实体调用Board::movePlayer可能更具语义。

如果您选择Player::walk设计,则应该将地图实例传递给玩家。所以你最终得到了Player::walk(Map &map /*more parameters here, maybe a direction or vector speed?*/)

另一件需要指出的事情是,你应该试着告诉比你更多。这意味着,而不是:

//in Player::walk
if (map.cells[wantToWalkTo] == 0) {
    map.cells[wantToWalkTo] = this.playerId;
}
//TODO: handle failed moves

你应该做一些类似的事情:

bool moved = map.moveTo(position, this); //encapsulate the logic for moving a player to a position
//TODO: handle failed moves

您的播放器实例不必"知道"所有这些事情。它可以通过接口与Map实例进行通信。一个人可以向外看,看到一些东西,但看不到其他东西(例如,可以看到一堵墙,但不能看到墙后面的东西)。Map实例可以控制哪些可见,哪些不可见。

Python风格的伪代码:

class Player:
    def __init__(self, Map, location):
        """Create a player, and tell them what Map they live on."""
        self.Map = Map
        self.location = location
    def walk(self, destination):
        """Try to walk to the destination."""
        path = self.Map.path_visible(location, destination)
        if path:
            self.location = destination
class Map:
    def path_visible(self, location, destination):
        """Can a player at location see how to get to the destination?"""

正确的OOP方法是让地图呈现某种接口,允许:

  1. 检查某个字段是否为空/该字段上有什么对象
  2. 在地图上放置/移除对象

移动玩家的逻辑应该完全在玩家类中。因此,玩家应该使用Map接口或其他类提供的信息来检查目标字段是否可供玩家访问、是否为空等。例如,当你想让玩家穿过墙壁、在水上行走或类似的东西时,考虑一下这种情况——是玩家改变了,而不是地图!