Wednesday, April 06, 2011

Code ownership

When working in object oriented games, a developer often has to think about what object owns some data, this is because we tend to write code that implements data as being managed by objects, handled by them, processed by them, and contained by them. Sometimes it can seem pretty obvious what should own the data, such as when you realise that you need a new member in all your entities to say whether or not it is active. The obvious answer is that the entity owns that data. It's wrong, but we'll get back to that later. Other times it can be a difficult question to answer, like where to store the pathing information for an ad-hoc group of AIs, should they determine a leader and store the pathing in the leader? Should their be a pathing manager that they can all subscribe to? Should it be part of the road / map manager seeing as the A* is running on that data more than anything else?

This is one of those things that keeps coming back to bite us when we try to solve real world problems following real world patterns in a computer simulation written in a computer language. The dysfunction there is that we're trying to solve a real world problem when in fact we should never submit that we're solving a real world problem at all. We're solving the wrong problem. We should never try to solve a real world problem in code. That's usually impossible.

What we do is simulate. We should first appreciate that no matter how much our codebase contains nice identifiers like Ally, or emotive words like Aggressive, or real world nouns like Car, or state adjectives like Dead, what we're actually doing, always, is processing data.

The idea of code owning data seems to have survived in spite of many reasons to not trust it. I've seen rooms owning their portals, even though they obviously share the portal with another room. I've seen player structures own their position data, even if there is not a world for that position to exist in (think about the last time you moved a player to a safehouse somewhere just so they wouldn't be rendered in a cutscene.) I've seen players own their guns (so they become tiresome to drop), and because of limitations, bullets are owned by the world (and then become tiresome to fire from the gun)

Code doesn't need to own data; it needs to process it into more data.

So, going back, your entity needs a new state for when it's active. Who owns this state data? No-one owns it. Ownership is for people or corporations. You can simulate ownership in your game, but code cannot own data. Got it yet? No? Object oriented approaches might pretend that this is possible, but it's not. Data is just data. You can lock yourself away from being able to transform it in simple and direct ways by hiding the data, but no code can actually own the data. Instead of focusing on who owns the data, focus on what needs it and how it's used to make the game run. In this case, rather than adding a new bool to a possibly overcrowded and messy class, don't add it at all. Add accessors for your crackpot brethren who think it should be inside the class, but instead of accessing a member variable, redirect to existence in a set.

That's right: the active state may as well be a list of entities that are active. If you want to only do something if an entity is active, by moving to a list of active entities you no longer have to check to see if the entity is active before doing that thing. Instead, you just commit that transform for all the entities in the list.

Pathing, equally as silly. No, pathing data does not belong to anyone. It is requested by whatever event caused the AIs to start pathing, it is used by the AIs, and it goes away once the path is no longer required (probably cleared up by the terminate of the state the AIs are currently in).

Initially there is a transform to generate the path that converts the world data, the AI position and ergonomics and goal position into a set of pathing instructions (waypoints for example, easy to follow breadcrumbs), then there is the next transform that converts the current AI and the pathing information into an entry on the IsFollowingPath list. Entries might take the form of a pair of pair AIPathFollowing andAIEntity. Then there is a per tick transform that transforms the AIEntity.Movement and AIPathFollowingelements based on timestep and current value for AIEntity.Position. Sometimes this transform would notify that the AIPathFollowing element needs to be deleted, and the pair should be removed from the IsFollowingPath list. Finally, there could be a per tick check to see if the IsFollowingPath list reaches zero, at which point, the path is deleted.

Data is just a source or a destination, not something to be owned. Code can transform data from source to destination, but should not be considered as owning data. Let's stop turning computer problems into a computer language descriptions of a real world problems. Let's admit that we're programming computers, not reality.

No comments: