如今创建的许多游戏都带有自己的成就系统,奖励玩家/用户完成某些任务。 stackoverflow 上的徽章系统是完全相同的。
但有一些问题我无法找到好的解决方案。
成就系统必须始终留意某些事件,想想一款提供 20 到 30 个成就的游戏,例如:战斗。服务器必须检查这些事件(例如:玩家在这场战斗中避免了对手的x次攻击或者玩家走了x英里)一直< /em>.
- 服务器如何处理如此大量的操作而不减慢速度甚至崩溃?
成就系统通常需要仅在游戏核心引擎中使用的数据,如果没有那些令人讨厌的成就,则无论如何都不需要这些数据(例如:玩家在每次战斗中跳跃的频率,你不知道)不想将所有这些信息存储在数据库中。)。我的意思是,在某些情况下,添加成就的唯一方法是将检查其当前状态的代码添加到游戏核心,这通常是一个非常糟糕的主意。
我的例子可能看起来“无害”,但想想目前《魔兽世界》中的 1000 多个成就以及同时在线的许许多多玩家。
Many games that are created these days come with their own achievement system that rewards players/users for accomplishing certain tasks. The badges system here on stackoverflow is exactly the same.
There are some problems though for which I couldn't figure out good solutions.
Achievement systems have to watch out for certain events all the time, think of a game that offers 20 to 30 achievements for e.g.: combat. The server would have to check for these events (e.g.: the player avoided x attacks of the opponent in this battle or the player walked x miles) all time.
- How can a server handle this large amount of operations without slowing down and maybe even crashing?
Achievement systems usually need data that is only used in the core engine of the game and wouldn't be needed out of there anyway if there weren't those nasty achievements (think of e.g.: how often the player jumped during each fight, you don't want to store all this information in a database.). What I mean is that in some cases the only way of adding an achievement would be adding the code that checks for its current state to the game core, and thats usually a very bad idea.
My examples may seem "harmless" but think of the 1000+ achievements currently available in World of Warcraft and the many, many players online at the same time, for example.
发布评论
评论(4)
成就系统实际上只是一种记录形式。对于这样的系统,发布/订阅是一个很好的方法。在这种情况下,玩家发布有关自己的信息,感兴趣的软件组件(处理个人成就)可以订阅。这使您可以使用专门的日志记录代码来观察公共值,而不会影响任何核心游戏逻辑。
以“玩家走了 x 英里”为例。我会将步行距离实现为玩家对象中的一个字段,因为这是一个简单的增量值,并且不需要随着时间的推移增加空间。奖励步行 10 英里的玩家的成就则成为该领域的订阅者。如果有很多玩家,那么将这一价值与一个或多个中间经纪人级别相加是有意义的。例如,如果游戏中存在 100 万玩家,那么您可以将值与 1000 个经纪人进行聚合,每个经纪人负责跟踪 1000 名个人玩家。然后,成就将订阅给这些经纪人,而不是直接订阅给所有玩家。当然,最佳层次结构和订户数量是特定于实现的。
在您的战斗示例中,玩家可以以完全相同的方式发布他们上次战斗的详细信息。监视战斗中跳跃的成就将订阅此信息,并检查跳跃次数。由于不需要历史状态,因此它也不会随着时间的推移而增长。再次强调,无需修改核心代码;您只需要能够访问一些值。
另请注意,大多数奖励不需要是即时的。这让您在管理流量方面有一定的余地。在前面的示例中,您可能不会更新经纪人发布的行驶距离,直到玩家总共又走了一英里,或者自上次更新以来已经过去了一天(直到那时在内部递增)。这实际上只是一种缓存形式;确切的参数取决于您的问题。
Achievement systems are really just a form of logging. For a system like this, publish/subscribe is a good approach. In this case, players publish information about themselves, and interested software components (that handle individual achievements) can subscribe. This allows you to watch public values with specialised logging code, without affecting any core game logic.
Take your 'player walked x miles' example. I would implement the distance walked as a field in the player object, since this is a simple value to increment and does not require increasing space over time. An achievement that rewards players that walk 10 miles is then a subscriber of that field. If there were many players then it would make sense to aggregate this value with one or more intermediate broker levels. For example, if 1 million players exist in the game, then you might aggregate the values with 1000 brokers, each responsible for tracking 1000 individual players. The achievement then subscribes to these brokers, rather than to all the players directly. Of course, the optimal hierarchy and number of subscribers is implementation-specific.
In the case of your fight example, players could publish details of their last fight in exactly the same way. An achievement that monitors jumping in fights would subscribe to this info, and check the number of jumps. Since no historical state is required, this does not grow with time either. Again, no core code need be modified; you only need to be able to access some values.
Note also that most rewards do not need to be instantaneous. This allows you some leeway in managing your traffic. In the previous example, you might not update the broker's published distance travelled until a player has walked a total of one more mile, or a day has passed since last update (incrementing internally until then). This is really just a form of caching; the exact parameters will depend on your problem.
如果您无法访问源代码(例如在视频游戏模拟器中),您甚至可以执行此操作。例如,可以编写一个简单的内存扫描工具来查找显示的分数。一旦你有了这个,你的成就系统就像每帧轮询该内存位置并查看他们当前的“分数”或其他什么是否高于他们的最高分数一样简单。视频游戏模拟器的一个很酷的事情是内存位置是确定的(没有操作系统)。
You can even do this if you don't have access to source, for example in videogame emulators. A simple memory-scan tool can be written to find the displayed score for example. Once you have that your achievement system is as easy as polling that memory location every frame and seeing if their current "score" or whatever is higher than their highest score. The cool thing about videogame emulators is that memory locations are deterministic (no operating system).
在普通游戏中有两种方法可以实现这一点。
注意:pub/sub 不太适合这个 IME,因为设计者从来不希望“当player.distance = 50 时”。他们真正想要的是“当观看屏幕的人感知到的玩家距离似乎已经经过第一个村庄,或者向右至少 4 个屏幕宽度时”——即比简单的计数器更加模糊和抽象。
在实践中,这意味着逻辑发生在更改发生的地方(甚至在事件发布之前),这是使用发布/订阅的糟糕方法。有一些游戏引擎可以更轻松地执行“逻辑在接收点进行”(“子”部分),但它们并不是大多数,IME。
即几乎与上面的 1 相同,只是您要小心地做两件事:
There are two ways this is done in normal games.
NB: pub/sub is a poor fit for this IME because the designers never want "when player.distance = 50". What they actually want is "when player's distance as perceived by someone watching the screen seems to have travelled past the first village, or at least 4 screen widths to the right" -- i.e. far more vague and abstract than a simple counter.
In practice, that means that the logic goes at the point where the change happens (before the event is even published), which is a poor way to use pub/sub. There are some game engines that make it easier to do a "logic goes at the point of receipt" (the "sub" part), but they're not the majority, IME.
i.e. almost the same as 1 above, except that you're careful to do two things:
如果您的游戏架构是事件驱动,那么您可以使用有限状态机。
If your game architecture is Event-driven, then you can implement achievements system using finite-state machines.