Object-Oriented Debate Part 2: Okay, so what is OOP?

By Shamus Posted Tuesday Dec 13, 2016

Filed under: Programming 49 comments

To discuss what Object Oriented Programming is, let’s discuss the problem it was designed to solve. Well, let’s get as close as we can to an explanation while keeping this readable for non-coders.

Originally, programming was procedural…

Note: This is different from the kind of “procedural” you find in No Man’s Sky or FUEL. In those games, when we say “procedural” we mean the art assets are created by the program using a set of procedures. In the field of writing code, when we say “procedural” we just mean a program is nothing more than a set of procedures to follow, one after the other. It’s the most obvious and direct way to go about writing software: You tell the computer what to do.

…but the problem (the argument goes) is that procedural programming (PP) can get out of hand in terms of complexity.

Let’s say we’ve got a game. We’ve got a big list of (say) space marines taking part in the simulation. Each space marine is nothing more than a block of data describing where it is, how it’s moving, what weapon it has, how many hit points it has, what team it’s on, what it’s AI is up to, what character model it’s using, if it’s dead, and so on.

Here, let’s make up a pretend data structure to hold our marine:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct space_marine
{
  char*      name;
  int        character_model;
  int        current_weapon;
  int        ammo;
  bool       dead;
  int        hitpoints;
  vector3d   location;
  vector3d   velocity;
  int        animation;
  int        animation_frame;
};

Somewhere else in our code we’ll loop over all the space marines and see if they’ve been crushed under any vehicles. Another bit of code checks to see if they’ve been hit with bullets. Another checks if they’ve been eaten by aliens. Another checks to see if they have picked up weapons. Using vehicles. Interacting with doors. Reaching checkpoints. Using cover. Often these systems need to change the space marine in same way. If they get hit with bullets they need to take damage. If they are knocked into the air they need to play a new animation. And so on.

The first problem.

Every single programmer that writes code to change the state of a space marine needs to know exactly how the internal systems of a space marine works. If they don’t, then they will write code in a way that makes sense to them, but doesn’t work with the code that already exists.

The guy coding the “getting crushed by machinery” code just subtracts a million hitpoints when a marine is crushed. He didn’t notice the variable called “dead”, which is what actually causes the marine to behave like it is dead. You’re supposed to see if HP is at or below zero, and then set the variable “dead” to “true”. Since this programmer didn’t, you wind up with a bug where a character has negative one million hitpoints and yet is still running around alive.

Maybe another coder comes along. She’s a little more careful, so when she writes a new system for characters running into forcefields she notices the “dead” variable and correctly intuits what she’s supposed to do. However, she writes it so that the “dead” flag is set if hitpoints are below zero. Which means that if you’re damaged by a forcefield, it’s possible to have exactly zero hitpoints and still be alive, while most other kinds of damage kill you at or below zero. This creates annoying inconsistencies that are hard to track down.

Our space marine example above is pretty simple, but in real applications things aren’t always this straightforward. In my game Good Robot, the data structure for enemy robots has over 70 fields in it. And that’s just a simple 2D game. As the data becomes more complex, it becomes unreasonable (and infeasible) to expect that every programmer on the project should be able to fully understand every data structure. As the number of different types of data (space marines, weapons, vehicles, multiplayer connections, particle emitters, physics objects, HUD elements, projectiles, menus, etc etc etc.) grows it becomes difficult for any one person to get a a handle on the whole thing and how it all connects.

So now you’ve got half a dozen people all interacting with data structures they don’t fully understand, which can lead to bugs. This leads us to…

The second problem.

These guys are bad dudes who don't play by the rules. Specifically, they clip through scenery, get stuck on terrain, or end up locked into a fixed pose while running.
These guys are bad dudes who don't play by the rules. Specifically, they clip through scenery, get stuck on terrain, or end up locked into a fixed pose while running.

In PP, any part of the code can act on any bit of data at any time.

Let’s say a bug crops up. At the end of a game loop you find a space marine is in an invalid state. Maybe it has a negative one million hit points, yet isn’t marked as “dead”. Or perhaps it has a negative number of bullets. Perhaps it’s far outside the game area, or swimming when it shouldn’t be. Maybe they’re holding a six-shooter that somehow has 400 bullets in it.

The programmer discovers this incorrect state, but they have no idea where in the code this might have happened. A hundred different systems modify space marines every frame. There’s no way to even begin the search for the problem. As the number of systems that can act on one another grows, the program quickly becomes a terrifying mass of chaos.

Managing State

In the most abstract sense, the program is nothing more than a bunch of logic designed to change the state of variables. At one point in Half-Life 2 Gordon Freeman is running around the city with no weapon, and at another point he’s out in the abandoned highway, running over combine soldiers in his laser buggy. It’s still the same program. The only thing that’s changed are the values stored in all the variables. A particular arrangement of variables is called the “state” of the program. A program like Half-Life 2 does a lot of changing state. A lot of bugs are the result of some unforeseen change in state.

Maybe your game is processing animations for various objects in the scene. A door moves along its open animation and bumps into an explosive barrel. It nudges the barrel, which suddenly can’t cope with the fact that it’s sharing space with another object. This triggers an explosion, which kills the player character.

Meanwhile, somewhere in the high-level code of the game we might have something like this:

1
2
3
4
5
6
7
8
9
//See if the player is dead.
if (player.Dead ()) {
  OpenGameOverMenu ();
  return; //no need to process the scene from here.
}
UpdateAnimations ();
UpdatePlayer ();
UpdateAI ();
UpdateNetwork ();

On line 2 we abort processing if the player is dead. Because of this, we assume the player MUST be alive when we update them on line 7. But in the scenario above, we found an edge case where the player could die during the animation update.

It’s not possible for a programmer to understand how every object in the game can impact every other object in every situation, and it’s certainly not possible for them to grasp all of the cascading changes that might result from interconnected systems. Which means they probably didn’t ever imagine that it would be possible for the player character to be dead when we reach line 7. All of the code in UpdatePlayer () is probably written under the assumption that the player is 100% guaranteed to be alive at this point.

We refer to this problem as “side effects”. Running a bit of code has unexpected side effects on the state of the program, and there’s no way to account for all of the possible side effects of any given action. The systems are too large, the connections between them are too numerous, and the possible side-effects are too complex to be fully understood. The above example is trivial(ish) to detect and fix, but in a real game subtle stuff like this can hide in the crevices of the structure and bedevil our poor programmer.

The Solution

These represent class objects. Look, just go with it.
These represent class objects. Look, just go with it.

Here is where we see the desire to use Object-Oriented programming. Instead of a program where any part of the software can reach in and modify any part of a space marine at any time, we hide all of those internal variables away and build an interface for them.

We create a “class” called SpaceMarine, we stick the variables inside of it, and we lock those variables away so that other parts of the program can’t change them whenever they like. Then we build an interface – a set of controls – by which other programmers can manipulate a SpaceMarine. The variables are essentially in a black box and (we hope) the object can be used without knowing how it works on the inside.

Back in November, I talked about building a good API. At the time I said:

There is a certain art to making a good API. You want the controls to be as simple as possible without taking away functionality. A well-designed library should be obvious to use and shouldn't require you to worry about any messy details of what might be going on inside of that black box.

When you're using a toaster, you don't care about the voltage the device is using, the temperature of the heating coils, or the specific electrical resistance being used by the heating elements. You just know you want your toast to be medium. If a toaster forced you to worry about all those details, then the controls would be too complicated. If it just had an on and off button with no way to choose how dark to make the toast, then it would be too simple. A good API should be designed to hit that sweet spot between complexity and features, and it should gracefully hide everything you don't need to worry about.

Making a good API has a lot in common with a good class interface. In both cases you’re trying to insulate the other programmer from needing to know too much about how something works internally.

To solve the problem we had above:

You can’t change a SpaceMarine’s hitpoints directly. Now there are a couple of functions to do this for you. Maybe you’ll call SpaceMarine.Damage(value) and SpaceMarine.Heal(value). Now you don’t need to know about the dead flag. When you inflict damage, the SpaceMarine does some internal checks and will mark itself as dead if needed.

This is good. Now rules are enforced consistently for all types of damage, and coders don’t need to carefully study a page of space marine data before they know how to properly interact with it.

This solves half of the problem. The other half of the problem is solved by creating object hierarchies. SpaceMarines belong to (say) some new system that’s in charge of managing actors in the scene. Maybe the person writing this new code takes a cue from Hollywood and names this thing the Director. So only the Director is allowed change the state of the marine by calling things like Damage() or Heal(). If some system needs to alter a SpaceMarine, they have to go through the Director.

This supposedly solves the problem where playing a door opening animation might result in a dead SpaceMarine due to cascading side effects.

Next time I’ll get into the original arguments that Brian Will brings up.

 


From The Archives:
 

49 thoughts on “Object-Oriented Debate Part 2: Okay, so what is OOP?

  1. Interfaces are absolutely the best feature of object oriented programming. They clearly define how the system is to interact with that lump of data. And yes the art of good API design and writing a good interface are similar.

    I been maintain a CAM Program since 1985 and ported through several platforms and programming languages (mostly BASIC related) by far the easiest port so far is the current one from VB6 to .NET. It was in VB6 that we switched over to using classes and interfaces. Unlike the ports were based on functions, the interfaces clearly lay out what I need to focus on in the port. Plus they isolate problems to specific methods so we can port and test the stuff that take little effort and focus on the working on the problem area.

    For example it took a while to create a drawing environment that replicated the smoothness we achieved in VB6. But once we had that figured out the rest just worked because we already tested the stuff related to the part geometry and how it saved and stored. A current issue is learning about OpenGL in .NET as some of our parts start out as 3D objects. Again everything else is done and tested for those object and this is the last hurdle to overcome for this part of the program.

    Interfaces also allow a better idea to implement more smoothly. If it well designed the work be concentrated on one side or the other. And if the better idea involves wholesale changes then the older interfaces will act as a list of what must happen as a minimum.

    1. Groboclown says:

      Interfaces are not specific to OOP, but are a great aspect that OOP encourages. Strongly defined interfaces allow for loose coupling between components, which means that one part of the software can radically change, without having to touch another part of your software. As an extreme example of this, the “microservice” trend uses very strict web “interfaces” to bind software together; you can go as far as moving one part of the software to run on a completely different platform (another language, another operating system, another processor type), and the outside software that calls the interface will still work as expected, without needing any changes.

  2. Da Mage says:

    One thing that might deserve a mention.

    With OOP your space marine can also do things to himself each frame. The director could ask the space marine (and any other object) to ‘update’ themselves, allowing to handle their own state and things like animations frames etc. The director doesn’t know what the space marine has to do each frame, it just lets it know when it is the space marine’s turn to handles all it’s details.

    Since every different type of object can have a generic ‘update’ command, the director, without needing to know what an object is, can just ask everything to update themselves for each frame, be it an NPC, a chair or a door.

    1. Cuthalion says:

      Personally, I’d advise against doing things that way because it’s easy to, say, forget to add the new physics check to every type of object’s update command. But that is definitely possible and can be more plausible if most things are subtypes of a single object type that serves as the place where you call all the “everything needs to do this” update stuff.

      1. Da Mage says:

        Yeah, your using inheritance and sub-classes with this. Think how big games do it, every object that is rendered in the world will branch backwards to a single high level object. As you go down the tree they will add things specialised to them into their update command.

        For example, one branch of the tree in my game engine has:
        Component -> World Object -> Item -> Weapon

        Your point with physics is interesting, as that comes back to a design decision. Do you have a physics class that takes in objects to do stuff with them, or do you send a physics reference to each object to update themselves. The first way allows you to keep all the physics code together in one place, but the second allows for better encapsulation. Either way, you’ll have to remember to add the physics to every object, but at least with inheritance, if you forget, the parent class’s physics should still happen.

      2. Daemian Lucifer says:

        On the other hand,it makes finding the bug easier.You know that if just one object is behaving wonky,the problem is just inside it,so you dont have to look around.

      3. Cuthalion says:

        re: Da Mage and DL

        I didn’t phrase my “But” well, but my primary concern is with the Don’t Repeat Yourself (DRY) principle. The more places you have to write the same code, the easier it is to get inconsistent results. Imagine if one object did movement first and physics second, and the other did physics first and movement second! You’d get confusing bugs that don’t show up until six months later, when you unwittingly add something in movement or physics that assumes the other one happened first!

        Inheritance, which I implicitly referenced and Da Mage mentioned in his reply, cuts this problem down quite a bit because you switch to having both default and custom behavior, where you only have to add custom stuff when a particular type of object needs to do something different. Without inheritance (which is how I read the original comment), everything is custom, even if it’s all the same, making it tedious to add or change things.

        DL makes a good point, that this way of doing things may be more intuitive to debug, because you’ll probably see an issue and think, “Those things are doing something wrong,” and you’d have narrowed it down.

        It comes down to that design decision Da Mage described. Personally, I would have a central function that gives orders to your objects, and the objects would just have (inherited, when appropriate) methods to handle each individual process for themselves. So, central has a list of objects and says, “You all, do your movement. Now, all of you, do your physics.” So, the classes have potentially different doMovement() and doPhysics() methods, but they don’t get to decide what order to do them in or leave themselves out by accident (they’d have to do that on purpose!).

        But I’m also half self-taught and am not versed in The Principles that Real(TM) programmers care about.

        1. guy says:

          It’s probably best to have an update() method in your GameObject class and then just have every object inherit from there. That way you don’t have to worry about forgetting to add an object type to the list or edit multiple individual methods.

          What gets interesting here is if you want different objects to do different kinds of things in the update cycle. For instance, objects that aren’t physics-enabled don’t need to do physics calculations. There’s two ways to handle this:

          1. Have the common things every object does in the parent class and override update() in subclasses that do special things, ideally with a call to super.update if the special thing can be exclusively before or after the standard thing. Put these as high in the hierarchy as possible to minimize duplication.
          2. Put all the methods in the primary class, but have the base implementation just return immediately without doing anything, then override them in classes that actually do the things.

  3. Echo Tango says:

    if (player.Dead ()) {
    OpenGameOverMenu ();

    This space between the functions and the parameters/parentheses is contravening like, all the style guides! ^^;

    1. Kian says:

      I didn’t see it when I read the article.

      Now I can’t unsee it! Make it stop!

    2. Warclam says:

      Oh hey, that does look weird now that you mention it. Especially the player.isDead line, with the closing parenthesis right afterward. ()) is a very odd construction.

    3. Abnaxis says:

      There’s a problem with spaces between function names and parenthesis?

      1. Echo Tango says:

        Most languages/specs/guides prefer no space. The problem (in general) is different programmers will be used to different styles, and be slower or faster at reading the code. Best tool to gain widespread use is automatic style-checkers (aka “linters”). Even better is when you do it like Go, and have the lint program automatically reformat the code; Then there’s no reason for arguments, because it’s trivially easy to have everyone’s code match the same style. :)

        1. Stu Hacking says:

          Only if the tool adopts the correct standard! ;-)

    4. Rodyle says:

      Mono actually does this as standard.

  4. Zak McKracken says:

    One more advantage of a good clas interface:
    If you find some bug where your marine ends up with (say) the wrong number of hitpoints, the programmer already knows that the chain of commands which did this must have gone through either through the marine.damage() or the marine.heal() routines. So you directly have a place to set a breakpoint for debugging or trigger some event in the game every time this happens (like, the marine shouting something, or some number in the console or in-game poppoing up, informing the programmer that something just damaged/healed the marine. Waaay easier to deal with than if there were different routines for damage by grenade, by falling, by gunshot, by running into a wall, by radiation…

    That said: much of the code would still need to know whether the marine is dead or not, so programmers will still need to know about the flag. But, if it’s part of an object it will be way easier to spot because you can much quicker get a list of all things associated with the marine object than you would get a list of all variables and functions in procedural code which may or may not be relevant for the marine.

    1. Ingvar says:

      I guess having a Spacemarine.isDead() method (or, possibly, SpaceMarine.isAlive(), if that makes more sense) function returning True (or False) as appropriate would be borderline useful.

      1. guy says:

        Pretty much mandatory; you’d want to make the dead flag private to keep other parts of the program from editing it arbitrarily and winding up with dead space marines with positive HP and breaking something.

      2. Kian says:

        If you’re sticking closely to OOP principles, you probably wouldn’t expose that detail in the first place. Objects should handle themselves, and if they’re dead, react appropriately to the messages they receive. Just as you wouldn’t add “isHorse”, “isCar” or similar methods and check those before selecting the movement animation.

        1. Zak McKracken says:

          Well, if the marine dies, there needs to be some way for the rest of the world to know about it, in order to be able to react to it… so the information about the space marine’s demise must be communicated somehow to the rest of the world, especially if the realization of it happens within the object…

          I mean, yes, playing the death animation, making asound, etc. are all things that the object could conceivably do on its own, but what about the alien that needs to know it can stop attacking? In the strict hierarchical model, the Marine’s “parent” class would be the bearer of the news, but even then, it must get the info, whether it’s the return value of the marine.damage() method, am externally visible object property or some method that can be called — something of that form must exist, and at least some programmer needs to know about it.

          The reason I’m using object oriented code* is that it allows me to bundle relevant bits together, and that makes them way easier to find when coming back after a year and trying to work out how this bit of code works.

          * I’m probably not using “pure” OOP according to some abstract criterion. I’m an Engineer, not a mathematician. And putting stuff in classes is superhelpful. I used to work with somebody else’s procedural code that was doing very modular things. It was … special. And slow, extremely slow. A few well-formed class interfaces could have worked wonders. But then, it was all in Fortran 77, so that wasn’t an option.

      3. Matt Downie says:

        A good rule of thumb is: never store the same bit of information in two places. If you do, it means you (and anyone else who works on the project) will have to always make sure to update both bits of data at the same time, and if you ever forget, it will create problems.

        If no hit points = dead, then storing ‘dead’ as a separate parameter is going against this rule.

        If you get rid of the ‘bool dead’ parameter, and instead use a function that checks if their hit points are less than or equal to zero, then you are guaranteed to never get into a situation where you have a marine that is alive with no hit points, or dead with positive hit points.

        1. Ingvar says:

          Yep, multiple sources of truth tends to cause them to desynchronise. If actually computing “is this space marine dead”, I might, possibly, as an efficiency measure, introduce a boolean as a caching layer, if profiling indicates it’s a good trade-off between speed and maintenance hassle.

          In short, you don’t expose a flag, you have an interrogative method. How that is implemented is really up to the class itself. For them languages that support “make method calls look like attribute lookups” (Python, I’m looking at you), you could certainly make it look as if it is a flag, whereas it’s actually a more clever method masquerading as one.

  5. Kian says:

    This is more closely related to encapsulation (keeping the data and the methods that act on the data tied together, and only act on the data through those methods) than OOP. OOP implies encapsulation, so you can’t have OOP without it, but you can have encapsulation without OOP. Just having well designed classes doesn’t mean you’re using OOP.

    Which is part of why OOP is so hyped despite being so misunderstood. OOP gives us great tools for making better procedural programs, and that is as far as most people go. OOP is actually only useful in a very limited set of applications, but a lot gets labeled OOP without really being designed that way.

  6. Kylroy says:

    I have worked in insurance for over a decade, and every time I see “OOP” I immediately read it as Out Of Pocket. Which made me briefly wonder why an explanation would take an entire article.

    1. Tizzy says:

      17500 or so TLAs is clearly not enough.

      1. Joe Cool says:

        TLI. An acronym is read like a word (laser, radar, gif (pronunciation left as an exercise for the reader), SCOTUS, PIN). In an initialism, each letter is said (ATM, CRT, LCD, LED, LSD, TLA).

        Or so the pedants tell me.

        Then again, I read “OOP” as an acronym and not an initialism. So you may be right. “Who cares?” is also a valid response.

        1. Matt Downie says:

          If it stands for Three Letter Abbreviation, then it works either way.

    2. MrGuy says:

      Who’s down with OOP?

    3. Daemian Lucifer says:

      A a joke,I started reading it as a word.It sounds like something the librarian might say.Oop!Ook,ook,oop,oop!Eeek!

  7. Amarsir says:

    Objects also seem the sanest way to parallel values in a database. If anything can change the struct’s values then you get some very inefficient operations.

    However, I’ve still never found a system that I love for keeping the two intact. It’s not feasible to lock the underlying tables any time I do any calculations. But if I get a concurrency issue then my write() method needs to roll back and trigger a redo of all the work, which is not very encapsulated. I don’t know how everyone else solves this because I’ve read a bunch of patterns and they helped but weren’t ideal.

    1. Richard says:

      LockForRead and LockForWrite are the common pair.

      An unlimited number of readers can all read simultaneously with no issues.

      If anything wants to Write, it has to wait for all the readers to finish, then it will gain the Write lock to the record (or table) that’s affected.

      While anything has the Write lock, nothing can gain the Read lock.

      1. Groboclown says:

        … but since it’s a database, you’ll need the transactions and locks at the database level, not at the program level, because multiple applications can attempt simultaneous updates or reads.

        In the case of Java, tools such as Hibernate handle the object relational mapping well, including deep relationships between tables, and can be setup to handle transactions for batch operations.

        1. Amarsir says:

          Locking mechanics aside, my problem is still the clunkiness thereof. (For lack of a better term.)

          I have rows in Table A, each of which gets updated based on calculations from dozens of rows in Table B, which themselves is dependent on data in Tables C and D. To run through all of A takes several minutes on a decent dedicated server.

          From a query efficiency standpoint, I’d like to select all the rows from A at once. But that’s unfeasible given the processing time. Still, I’m given this choice:

          Method 1) Lock row 1 from A, lock all relevant records on b, c, and d, calculate everything in b, sum up and update a, unlock everything.

          Method 2) Lock row 1 from A, lock a single record from B with affiliated C and D. Calculate that B, then update A. Unlock and relock. Repeat with next row from B.

          If I use method 1 then I’m keeping a lock open for a pretty long time – well into any reasonable definition of “long query”. If I use method 2 the lock gets released frequently but the massive increase in queries has taken a long process and made it considerably longer.

          To which someone says “can’t you do the calculations at the database level, not the program level?” Which is at best a massive duplication of logic.

          That’s why I’m not happy with any answer. Either I’m keeping a row locked for too long, or I’m running lots of extra queries which is inefficiently stressing what already tends to be the bottleneck of the system, or I’m tossing out all encapsulation to make new versions of every action right in the transaction. No matter what I’m doing something bad.

          1. Groboclown says:

            Yeah, databases are tricky. Unfortunately, the right way to do those operations is through a database operation – “select into table_a” style. It’s done through a single operation, and is what a database is designed to handle. Yes, the logic now passes from Java (or whichever) code into SQL code, which makes testing an order of difficulty higher. But without it, you end up essentially writing your own database.

            As for the testing, this means you now really need to have some integration tests that run the code against a real database instance (not the production db!).

  8. Sabrdance (MatthewH) says:

    I do not follow one part.

    In the example of the player dying between the dead check and the player update, I would think that checking that the player is alive (yep!) and then running the animation which sets off the explosion would reduce the player’s hitpoints to 0, so in line 7, update.Player, that is when the player would be recorded as dead, and the menu would be brought up at the next death check.

    As this is not the case, what am I missing?

    1. Groboclown says:

      Subtle issues like that, where one part of the code assumes that something is true (such as player is alive), when it actually isn’t, is a place where bugs happen. Now, if the bug is simply “I’ll recognize the player action as initiating a jump, so I’ll start the jump procedures”, then it’s not too bad. However, in more complex scenarios, it can lead to assuming things like memory is allocated correctly when it isn’t, leading to hard program crashes.

    2. Kian says:

      Shamus was giving a hypothetical example of a poorly designed, deeply spaghettied code base where animation code wrote to player state directly instead of queuing up a “damage player” event for the UpdatePlayer procedure to handle. So after running the animation, the world state is changed immediately and the assumption made in line 2 is no longer true in line 7.

    3. Nick says:

      It would depend on how UpdatePlayer was coded. If, like Shamus said, it assumes Player has to be alive there would be problems.

      Your solution sounds like what a coder might do to resolve the bug Shamus was describing.

      We also don’t know anything about what happens in UpdateAnimations. Maybe the original plan was to only animate background stuff here, but player-killing explosions were added later.

      1. MrGuy says:

        This is also one of those problems where somewhere along the way, something that wasn’t a problem becomes a problem because we expanded the scope of something without realizing the implications.

        Say that UpdateAnimations was originally set up just to perform pre-defined animations that didn’t interact with the game world directly. For example, showing a player switching weapons or reloading their gun. We had a whole separate step UpdateObjects for updating physics objects (objects that could be interacted with/moved).

        Then we saw the game engine was really bogging down on UpdateObjects, because physics is hard. But a clever programmer noted “hey, we have a ton of objects classified as physics objects, but really they’re basically just doing pre-set specific movements, like opening doors and treasure chests. They’re more like the animations than they are like throwable objects like rocks, or destructible items like chairs. Let’s move them to the UpdateAnimations step and take some pressure off the physics engine.

        Which works great. Except that we forgot that, in some specific cases, those doors might bump INTO physics objects, and since we moved the code to a different step now we might have weird interactions where things that SHOULD cause the physics engine to do stuff actually don’t. So a chair in the path of a door will clip through it, because we don’t do collision checks for UpdateAnimations, because UpdateAnimations wasn’t designed with “physical world” interactions in mind…

    4. Sabrdance (MatthewH) says:

      So it isn’t that this section of code would cause a crash, rather the issue is that anything depending on line 7, which assumes player is not dead, could be futzed up, and we have no idea what things rely on line 7, because nothing is cordoned off and everything can affect everything else. In that way, the animation changes the player state, the changes updateAi and updatePlayer, and a whole bunch of other things.

  9. You can't change a SpaceMarine's hitpoints directly. Now there are a couple of functions to do this for you. Maybe you'll call SpaceMarine.Damage(value) and SpaceMarine.Heal(value). Now you don't need to know about the dead flag. When you inflict damage, the SpaceMarine does some internal checks and will mark itself as dead if needed.

    This is good. Now rules are enforced consistently for all types of damage, and coders don't need to carefully study a page of space marine data before they know how to properly interact with it.

    I just realized that I used to play a mod on Warcraft 3 called SOTDRP that totally had elements of this ^. You could modify damage, health points, how objects functioned, though it was typically based off of individual character and some pre-set modifications. Typically rules were enforced by the admin of the game. Still, it’s cool that I probably used to do something similar to this, at least I think it was. Probably not as complex though. That mod was fun.

  10. Jamie says:

    Technically, avoiding side-effects is functional programming’s domain. :)

    EG: if you were writing code in the functional style, the physics code would never modify(mutate) a space marine’s properties at all!

    Instead it would generate a new output which is a list of updates to apply to all objects.

    Then you might group() the list of updates by target
    reduce() each target’s list to a single update and
    map() each spaceMarine to a NEW spaceMarine with the updated properties!

    This kind of batch processing might actually be effective in a massively parallel architecture, since the update data only ever looks backward at the previous frame. This avoids any cache write contention (one core attempts to read data that another core has just overwritten, causing nasty cache misses everywhere)

    1. Zak McKracken says:

      I suppose that at least purely functional programming is also quite hard to wrap your head around and at times difficult to read.

      I’m trying to always assign new variables and avoid changing existing ones. That helps reduce bugs, quite a bit. But religiously clinging to any principle seems like a recipe for desaster.
      I think John Carmack gave a talk about this some time ago (the video was on this blog, afaik), where he said his experience with functional programming was similar. Bumping a global counter can be fine if the alternative is passing it between 15 different functions to make sure you obey all The Principles.

      I feel similar about OOP: Class interfaces are a wonderful thing but I see no reason to force myself to use them exclusively.

      1. JakeyKakey says:

        Pretty much. OOP is great, OOP dogma is bad.

        Otherwise you just end up with people like Robert Martin who are so obsessed with principles they can “refactor” perfectly readable ten lines of code into a ten function monstrosity.

  11. D-Frame says:

    As a terrible self-taught programmer who never learned the very basics and thus writes horrible code incomprehensible to anyone else, I appreciate these explanations. Terrible Car Analogy: It’s like I’ve been driving cars for many years, but I always use the reverse gear to brake, drop the clutch to stop the engine and get in and out of the car through the trunk.

  12. Wysinwyg says:

    This series is bomb and the discussion is fantastic. I feel like I’ve leveled up in coding just from reading the excellent comments.

    Kudos to Shamus for building such a great community and fostering such wonderful discussion.

  13. Jonn says:

    Couple typos:

    Somewhere else in our code we'll loop over all the space marines and sees if they've …

    … ever imagine that it would be possible possible for …

    And, inconsistency / missing “-” :

    Running a bit of code has unexpected side effects on the state of the program, and there's no way to account for all of the possible side-effects of any given action.

  14. Neil Roy says:

    Your argument about solving the space marine problem with OOP, classes etc… reminds me of someone that needs a package delivered across town, so they build a space shuttle to deliver it.

    If you make a class, you still need to have the foresight to check that dead state before you do anything else. When I program a game in C, I have, for example, a pacman style game. I have a function for handling the ghosts, for updating them. In that function ghosts are checked if they are “dead” (which sounds funny when applied to a ghost) etc. before I do anything else. I don’t need a class with restrictions to it’s state etc… I just wrote my code intelligently, and procedural. In your example, you have t he player’s dead state being checkand the later on you have a separate function for updating the player! This isn’t a problem with PP, it’s a problem with a piss poor programmer. Fire the programmer. The player’s dead state should have been checked and acted upon in the player update function, not outside of it. No class OOP needed, just intelligent programmers.

    Sorry, but your example is a really bad one.

    it should simply be…


    UpdateAnimations ();
    UpdatePlayer ();
    UpdateAI ();
    UpdateNetwork ();

    And then you have…


    UpdatePlayer()
    {
    if (player.Dead ()) {
    OpenGameOverMenu ();
    return; //no need to process the scene from here.
    }
    // player update continues here...
    }

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun. Your email address will not be published. Required fields are marked*

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!

You can quote someone like this:
Darth Vader said <blockquote>Luke, I am your father.</blockquote>

Leave a Reply to Daemian Lucifer Cancel reply

Your email address will not be published.