Author Topic: pedantic object oriented question  (Read 69416 times)

george

  • Rogueliker
  • ***
  • Posts: 201
  • Karma: +1/-1
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #60 on: April 17, 2013, 04:19:00 AM »
Just as a matter of separation of concerns it's quite handy to have a commandVerb and a doVerb for the reasons naughty mentioned.

Remember that the original (Smalltalk) OOP really had nothing to do with most of the conventions that were tacked on top of it later by OOP evangelists. It was basically about messaging (between 'objects'). Pretty much decoupling the parts of your program. Whenever people take an idea and start interpreting it and expounding it there's bound to be a bunch of crap to deal with :).

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #61 on: April 17, 2013, 08:05:04 AM »
requerent: to be honest the games companies (I'm looking at you Epic) that have the complicated hierarchies for their entities are stuck with mid-90s ideas of how to use OO and design game engines.

While I like component systems I wouldn't go as far as to make all components methodless structs. Then again I was first exposed to a design similar to modern day component systems many years ago by a Smalltalk coder who considered it a good OO design and I agree.

The OOP that the component and 'Data Oriented Programing' guys are attacking is over two decades old, it's just a shame it's what is still being taught to undergrads.

So what's a modern, 2013 way of doing it?

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #62 on: April 17, 2013, 08:09:51 AM »
First off why do you need lots of different pre and post steps for movement? I would be quite worried if that happened because it means you derive a new critter class just to get a new kind of movement. If your game does have a lot of very different forms of movement you should be abstracting that out of the critter anyway, e.g. into a hierarchy of movement objects. (I'm assuming that like in most games critters are not uniquely determined by their style of movement.)

If you don't factor it out and you have another method/behaviour on the critter that can vary, an attack() method say, then you could be for a right pain in arse. You only need 3 types of movement and 3 types of attack to lead to 9 different classes you could have to write. Of course you could make a faustian pact and use multiple inheritance but possibly reduce the amount of code you write at the cost of you sanity.

It would be easier to factor both movement and attacking out into their own objects or hierarchies (thereby turning a N*M problem into an N+M problem). This also has the nice side effect of being far easier to make data driven (so new critter types can be added without writing code).

What would these "movement" classes do, exactly? How do they interact with the map?

naughty

  • Rogueliker
  • ***
  • Posts: 59
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #63 on: April 17, 2013, 09:15:39 AM »
Quote
The OOP that the component and 'Data Oriented Programing' guys are attacking is over two decades old, it's just a shame it's what is still being taught to undergrads.

Please elucidate. I need schooling >_<.  A game is really just a database with a UI. Any OOP we do just feels like fluff. You can, of course, program DOP-style in OOP paradigm, but is it not just extra work? If it's all you know- sure, go for it- but are there any strict advantages?

The 'database with a UI' concept is great way of looking at it and a very fruitful perspective to take.
It's also a more 'OO' perspective than a hierachy of classes with draw(), attack() and move() methods.
MVC is pretty much the same idea but it has unfortunately collected loads of crud of the years that
hide the simple idea underlying it.

The early component systems were written by OO programmers trying to fix the problems with code bases
that massively abused inheritance. Inheritance was still used in some places like for controllers or as
a way to have different rendering backends but far less than in previous designs.


If you take the database analogy, OO is useful in maintaining constraints and invariants on the database. For example
you don't want an entity's health to get higher than the max health stat so instead of manipulating
health directly you use methods or functions to ensure that doesn't happen.

From the DOP standpoint (which is really a component system with a focus on low-level performance) you
can encapsulate the details of an AoS to SoA transform in a class or module. Clients of your class don't
even need to know you made the change. Also the basic tutorials on DOP don't show the full picture on the
SoA situation. You really want to align the values in the arrays so that all cache misses happen at the same
time. This very fiddly work but causes far less issues if hidden behind an interface.

Quote
I'm not sure why Attack() should be a method on the Controller, do you have a link to who would have said that?

It's pretty standard nomenclature. Controller.Verb() queues a call to Actor.doVerb(). That's how Unreal does it anyway.

Unreal is a nice enough engine with great tools but it's full of 90s throwback ideas. The controller should be calling Attack(), not having Attack() called on it. This is all IMHO of course.

naughty

  • Rogueliker
  • ***
  • Posts: 59
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #64 on: April 17, 2013, 11:17:33 AM »
So what's a modern, 2013 way of doing it?

There's two ways to parse that:

What's popular or considered  'best practice' in the games industry at the moment?

Component systems are all the rage and with good reason. There's quite a bit of variety in how they're
being implemented but the basic idea of having one type of 'entity' that can have multiple components
which define how it behaves. A rough analogy is that the programmer makes lego blocks and the designer
builds entities from them.

At the high-performance and AAA end of the industry you have 'double buffered' entities (a read-only public
version and a writeable, private version) to help get the most out of multi-core and the graphics hardware.
These designs are heavily concerned with performance issues and Data-Oriented Programming is a major buzzword.

At the MMO end of the industry component systems have been influenced by the need to interface with
Relational Databases (which are quite naturally 'component' based in a lot of ways).

At the indie/mobile end, Unity is component based (but not super strict about it) and is making a very
big impact.

It's worth noting that this isn't new at all. The first devs that actually publicised they were using a
component based system were probably Gas Powered Games who used it on Dungeon Siege (released 2002).
The company I worked for at the time had been experimenting with them around 2000 and quite a few
other companies had done it for a few years before that.

It's also not a panacea, I worked at a well known game studio that decided to use a component system
and they made a total hash of it. They had 100s of different types of components and singleton
manager objects for everything.

I'm leaving out a lot of details for brevity but the web is full of component system articles,
libraries, talks and slides. Some of them are just as dogmatic as the OOP guys used to be though.

What does this grumpy 'get off my OO lawn' programmer called naughty think is the modern way?

Component systems done well are a lot better in many ways than the inheritance hierarchy of entities
that was common in the 90s. For a small game it could be overkill though.

However you can arrive at roughly the same ideas from several angles. More emphasis on being data-driven
leads you to something like components. Performance concerns on modern hardware lead you to components.
Wariness or fear of inheritance leads you to components. Understanding all those OOP slogans and applying
them leads you to components.

requerent

  • Rogueliker
  • ***
  • Posts: 355
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #65 on: April 17, 2013, 09:27:38 PM »
Quote
If you take the database analogy, OO is useful in maintaining constraints and invariants on the database. For example you don't want an entity's health to get higher than the max health stat so instead of manipulating health directly you use methods or functions to ensure that doesn't happen.

That should just be a part of the System that works with those components.

Quote
From the DOP standpoint (which is really a component system with a focus on low-level performance) you
can encapsulate the details of an AoS to SoA transform in a class or module. Clients of your class don't
even need to know you made the change. Also the basic tutorials on DOP don't show the full picture on the
SoA situation. You really want to align the values in the arrays so that all cache misses happen at the same
time. This very fiddly work but causes far less issues if hidden behind an interface.

I'm a little confused about this. There is nothing inherent about DOP that makes AoS to SoA problematic. I mean, the whole idea of thinking of an application as a database is to optimally organize your data- in some minor cases AoS can perform better, but there is nothing preventing you from organizing everything in an SoA manner.

A System is a state-less procedure that evaluates and modifies the game-state/Model. However, each System only works with an explicit subset of Components- or more specifically values within subsets of components. This means that your data should be organized by components or the data within components, not by entities. You can further invert the relationship between keys and data so that everything is "perfectly" SoA if you want. Or only what would perform better that way.




Quote
Quote
I'm not sure why Attack() should be a method on the Controller, do you have a link to who would have said that?

It's pretty standard nomenclature. Controller.Verb() queues a call to Actor.doVerb(). That's how Unreal does it anyway.

Unreal is a nice enough engine with great tools but it's full of 90s throwback ideas. The controller should be calling Attack(), not having Attack() called on it. This is all IMHO of course.
[/quote]

That's what is happening. Controller.Attack() is bound to an input key which queues up the Actor.doAttack().

I will defend Epic for a moment- in that your entire game can be made up of 4 classes if you wish (camera, playerinput, actor, gameinfo). It isn't necessarily a complex minefield of inheritance. It actually uses some component-based systems, but the events are managed by classes, which I dislike- especially when working on a large team.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #66 on: April 17, 2013, 10:18:33 PM »
So what's a modern, 2013 way of doing it?

There's two ways to parse that:

What's popular or considered  'best practice' in the games industry at the moment?

Component systems are all the rage and with good reason. There's quite a bit of variety in how they're
being implemented but the basic idea of having one type of 'entity' that can have multiple components
which define how it behaves. A rough analogy is that the programmer makes lego blocks and the designer
builds entities from them.

(more about component systems -- snipped for brevity)

Mmm. I already programmed a program with a more "traditional" entity "hierarchy" with different subtypes of entities (though I'm just beginning on it, so there isn't too much code for the entity system) instead of components. Should I toss it out and go for components?

naughty

  • Rogueliker
  • ***
  • Posts: 59
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #67 on: April 18, 2013, 07:57:04 AM »
Mmm. I already programmed a program with a more "traditional" entity "hierarchy" with different subtypes of entities (though I'm just beginning on it, so there isn't too much code for the entity system) instead of components. Should I toss it out and go for components?

Without knowing your exact circumstances it's hard to say. If it's a short term project then I wouldn't bother unless you really want to try to implement a component system to see what the fuss is about.

Throwing code away is also something I wouldn't encourage at all. It would be best to convert over in stages unless you're very close to the beginning.

I suppose the pros and cons are:

Pros
  • You'll be trying something new which can be fun.
  • If you're making a large or complex game there will be a benefit.
  • If you're curious about how component systems work, the best way is to try it.

Cons
  • Until you get a feel for how a component system works you will be less productive and probably a bit confused. This can kill motivation especially for hobby projects.
  • It's still possible to go wrong with component systems. One of the more well known game companies I worked at made an absolute mess of it.
  • The benefits of component systems are most felt in very complex games or games that are pushing the hardware to the limits. So if you have a simple enough game it could all seem a bit useless.

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #68 on: April 18, 2013, 09:08:24 AM »
I think strict inheritance/class hierarchy and "all components" are both examples of one directional thinking. People can only understand one paradigm and they follow it strictly. What I know about components is that they are simply data type classes (user defined data types), so it's perfectly ok to use them in other classes. You can also use inheritance at the same time.

naughty

  • Rogueliker
  • ***
  • Posts: 59
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #69 on: April 18, 2013, 09:55:18 AM »
That should just be a part of the System that works with those components.

I can understand that standpoint for examples that require mutation of state (and you're following the rule that only the corresponding system can mutate a particular component's state) but for objects that have involved immutable queries (e.g. a graph with a some complex breadth first based algorithm) you may as well just put the method on it.

I'm a little confused about this. There is nothing inherent about DOP that makes AoS to SoA problematic. I mean, the whole idea of thinking of an application as a database is to optimally organize your data- in some minor cases AoS can perform better, but there is nothing preventing you from organizing everything in an SoA manner.

Sorry I was being a bit vague. If you do an AoS to SoA change all clients need to have their code changed. Use a few inlined getters and they don't. This has stung me in the past when micro-optimising a particle system update. The most memory efficient structure wasn't a pure SoA, e.g.

Code: [Select]
struct Particle {
    vec3 position;
    float turbulence;
    float timer;
    float saturation;
}

Particle AoS[NUM_PARTICLES];

struct SoAParticles {
    vec3 positions[NUM_PARTICLES];
    float turbulences[NUM_PARTICLES];
    float timers[NUM_PARTICLES];
    float saturations[NUM_PARTICLES];
};

// Cache-miss Optimised version.
struct OptSoAParticles {
    vec3 positions[NUM_PARTICLES];
    struct {
        float turbulence;
        float timer;
        float saturation;
    } p[NUM_PARTICLES];
};

The actual code was more complicated but it caused issues because other systems were reading some particle data. It would have been easy to hide the changes for most (read-only) clients if it had been hidden a bit.

A System is a state-less procedure that evaluates and modifies the game-state/Model. However, each System only works with an explicit subset of Components- or more specifically values within subsets of components. This means that your data should be organized by components or the data within components, not by entities. You can further invert the relationship between keys and data so that everything is "perfectly" SoA if you want. Or only what would perform better that way.

Are you using the form of component system where entities are purely a unique id and you have no struct/object? Also do you follow the each component can only be written to by one system? There's quite a bit of variation in these kind of details.

Putting an Attack() method on a controller just leaks the abstraction. If there's user input involved in should be handled in the LocalPlayerController or some such. Then again it's been a while since I was using UE2.5 so I could have got my wires crossed.

requerent

  • Rogueliker
  • ***
  • Posts: 355
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #70 on: April 18, 2013, 07:38:09 PM »
Quote
That should just be a part of the System that works with those components.

I can understand that standpoint for examples that require mutation of state (and you're following the rule that only the corresponding system can mutate a particular component's state) but for objects that have involved immutable queries (e.g. a graph with a some complex breadth first based algorithm) you may as well just put the method on it.

I'm a little confused about this. There is nothing inherent about DOP that makes AoS to SoA problematic. I mean, the whole idea of thinking of an application as a database is to optimally organize your data- in some minor cases AoS can perform better, but there is nothing preventing you from organizing everything in an SoA manner.

Sorry I was being a bit vague. If you do an AoS to SoA change all clients need to have their code changed. Use a few inlined getters and they don't. This has stung me in the past when micro-optimising a particle system update. The most memory efficient structure wasn't a pure SoA, e.g.

As long as components are only communicating with each other through a System, you're technically fine. I have mixed feelings on hybrid approaches though. I feel like components should only be storing data in a manner that is salient for their respective systems. You don't violate this by slapping methods into them, but why would you need to? That method is really just a sub-system of the systems using that component-type. If you're always going to be using those getters, why aren't you storing data in that way to begin with?


Quote
Code: [Select]
struct Particle {
    vec3 position;
    float turbulence;
    float timer;
    float saturation;
}

Particle AoS[NUM_PARTICLES];

struct SoAParticles {
    vec3 positions[NUM_PARTICLES];
    float turbulences[NUM_PARTICLES];
    float timers[NUM_PARTICLES];
    float saturations[NUM_PARTICLES];
};

// Cache-miss Optimised version.
struct OptSoAParticles {
    vec3 positions[NUM_PARTICLES];
    struct {
        float turbulence;
        float timer;
        float saturation;
    } p[NUM_PARTICLES];
};

The actual code was more complicated but it caused issues because other systems were reading some particle data. It would have been easy to hide the changes for most (read-only) clients if it had been hidden a bit.

That's kind of just an issue with using objects and messaging systems to begin with. I don't dispute that such methodologies are powerful and succinct- but in a component system you should just yield to another system to ensure data-consistency. The relationship of systems in a loop can be implemented as an FSM, through which we can easily ensure that data is accessed in a consistent manner. If we get a read request from a client, that request is only going to be processed in its own system, after changes are written to the model.


Quote
A System is a state-less procedure that evaluates and modifies the game-state/Model. However, each System only works with an explicit subset of Components- or more specifically values within subsets of components. This means that your data should be organized by components or the data within components, not by entities. You can further invert the relationship between keys and data so that everything is "perfectly" SoA if you want. Or only what would perform better that way.

Quote
Are you using the form of component system where entities are purely a unique id and you have no struct/object? Also do you follow the each component can only be written to by one system? There's quite a bit of variation in these kind of details.

Yes. I may have used the word 'pure' earlier, but if I didn't i meant to >_<.

edit: Though it can depend on how you store your data. If you want to split up the x,y,z components of your position because you are working with constraining planes and some systems only modify a micro-component, then you may want to have multiple system modifying data-- but I think this is usually a design oversight more than a necessity.

Quote
Putting an Attack() method on a controller just leaks the abstraction. If there's user input involved in should be handled in the LocalPlayerController or some such. Then again it's been a while since I was using UE2.5 so I could have got my wires crossed.

I guess I wasn't being clear. That's how it works. Model inputs and feedback are handled by the Controller that modifies the model as an Actor. Any Controller is hypothetically valid, whether its AI or a Player, so both would be using Controller.Attack() to call Actor.doAttack().

There are two reasons for this-- one, we want to queue actions up so that they happen in a way that is acceptable to the Actor or consistent with the game loop, and two we may want to change the Controller's state and modify what method of Actor Attack() calls.

States in Unreal aren't driven by data, but allow you to change the functionality of any given method. IE. If we're dead and don't have a pawn, we wan't to override Attack() so that it spawns a new pawn for us.
« Last Edit: April 18, 2013, 07:41:15 PM by requerent »

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #71 on: April 18, 2013, 09:14:06 PM »
@requerent: So what exactly is the kind of design approach you're suggesting? You have a big pile of data called "game state" with no member functions that various objects manipulate using their routines, and which has no routines associated to it itself? Also, you say stuff like "It's pretty standard nomenclature. Controller.Verb() queues a call to Actor.doVerb(). That's how Unreal does it anyway.". What's an "actor" in this setup? Is "Controller" a single monolithic object or are there many "Controller"s? Where's this queue? Who has access to it? Etc.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #72 on: April 18, 2013, 09:21:15 PM »
And I'm still not sure on just which approach of the several presented here I should use.
« Last Edit: April 18, 2013, 09:24:40 PM by mike3 »

requerent

  • Rogueliker
  • ***
  • Posts: 355
  • Karma: +0/-0
    • View Profile
Re: pedantic object oriented question
« Reply #73 on: April 18, 2013, 11:40:10 PM »
@requerent: So what exactly is the kind of design approach you're suggesting? You have a big pile of data called "game state" with no member functions that various objects manipulate using their routines, and which has no routines associated to it itself? Also, you say stuff like "It's pretty standard nomenclature. Controller.Verb() queues a call to Actor.doVerb(). That's how Unreal does it anyway.". What's an "actor" in this setup? Is "Controller" a single monolithic object or are there many "Controller"s? Where's this queue? Who has access to it? Etc.
Ah- Naughty and I are going down a route of discussion that, honestly, doesn't remotely answer your question- but let me try and break some of it down.
Unreal Tournament 3, or the Unreal Development Kit, is a big 3d game engine that can do lots and lots of wonderful things. It uses a rather traditional OOP hierarchy, where all game objects (not data objects) extend from a base Actor class. Actors are any entity, static or dynamic (walls and monsters), that occupy or interact with the game space, or model. The 'state' of the game is a discrete configuration of the 'model,' which is the aggregation of data that describes the interactions/relationships of entities within the game (ie their current stats, positions, etc). When we talk about a 'game state' we're talking about a measurably distinct change to the model of the game. In a real-time game, like Unreal, the game state updates on every 'tick,' or game-loop. In effect, each 'tick,' or 'frame' is a state.
I wasn't being as specific as I should have been- Unreal uses a Controller-Pawn relationship, whereby both the Controller and Pawn inherit from Actor. The Controller is the decision maker and the Pawn interacts with the model. A Controller represent the player and handles input and the camera, but can also represent an AI that asks the Pawn what the model looks like from its point of view and then makes decisions for it. There is typically one controller per pawn- but for an RTS game, you may use Pawns to represent each unit and a single controller commanding them all. The 'queue' I'm describing isn't necessarily a universal action queue, but relative to how each Pawn.doAction() should be called. Controller.Action() is called on key press, whereas the controller then calls Pawn.doAction() where it is most logically appropriate. This can be as simple as setting a boolean 'performJumpOnNextTick,' so that the velocity of a jump is applied at the beginning of a tick, giving the Pawn an opportunity to manipulate it in some way before the physics engine updates the state, or so that the action can be replicated across a server.
Is this a useful architecture for a Roguelike? Yes. Pretty much every game can be thought of in terms of MVC, regardless of whether you use OOP or a Component system. A component system would hook a controller component into an entity, whereas OOP would represent the controller as an Object. I don't know any reference material off-hand, but the question is-- do you want to make Games or do you want to make Games and learn how to make games? If the former, do what you already know howt to do, otherwise, try something a little uncomfortable.
It also depends on what sort of features you want.

OOP vs DOP and Component Systems
Unreal, a heavily OOP game engine, uses components to unite all Actors in the game that share a property. The most mundane example are Collision Components. If an actor has a collision component, then it will be evaluated by the collision system. Unreal uses an event handler, where it calls something like 'CollisionComponent.Owner.onBump(actor bumpedActor,vector momentum, vector hitLocation)' whenever two collision components are interacting. There are other features built-in to the engine that will take care of displacement and overlapping issues, but that all depends on other values set in each Actor. In this way, Actors are performing all of the logic relative to their implementation, whereas the System isn't really doing anything except evaluating relational components and calling events on the Actors. Unfortunately, this tends to lead to a Monolithic Entity Superclass that has all of the possible events available to be overridden. Regardless, you are invariably using components even if you aren't using a component system- IE, the location of an entity is a logically discrete component of that entity that gets worked on at some point in the game-loop via the engine. Explicitly defining component-system relationships helps keep our logic succinct.
A 'pure' component system (DOP) looks at it from the other way around. Part of the value of using components have to do with meta-features like saving the game state, sending concise network messages, and some other things mentioned previously. This is where the 'database w/UI' idea comes in. Instead of having classes and objects relate to one another, we just have a Database and routines acting upon it in a Finite State Machine of Systems. This makes a lot of things easier- including Procedural Generation of bizarre items and monsters. Maybe a monster is equippable but can't be picked up, and there is a spell or ability that equips something from a distance. So very trivial to implement in DOP, but potentially very quirky in OOP.
The main problem I have with OOP is that you cannot predict all of the features you will want to implement. With DOP, it never matters.

If you're familiar with OOP already, then you should try a hybrid. It depends on what your goals are and what your experience is. I don't know of any good examples off-hand, but pseudo-code is available if you want.

Disregarding your experience/goals, Yea- I recommend a Database with no member methods/functions, with stateless routines acting on said database as an FSM of Systems.

mike3

  • Rogueliker
  • ***
  • Posts: 125
  • Karma: +0/-0
    • View Profile
    • Email
Re: pedantic object oriented question
« Reply #74 on: April 19, 2013, 01:48:48 AM »
Disregarding your experience/goals, Yea- I recommend a Database with no member methods/functions, with stateless routines acting on said database as an FSM of Systems.

The trouble is, with this particular project, to switch to such a radically different conceptualization would pretty much require throwing away all work done so far and starting over from scratch -- and I don't have any experience with the kind of architecture you describe. I suppose I could try it as part of a new project, though. What did you mean when you said "try something a little uncomfortable"? Did you mean "try something new so as to learn how to do it", and the "uncomfortable" bit is because you'd be starting without any previous experience with that new something?
« Last Edit: April 19, 2013, 02:05:36 AM by mike3 »