Re: State Object / Process Object design

Thatcher Ulrich <[email protected]> Mon, 12 Jul 2010 15:07:28 -0400
Newsgroups gmane.games.devel.sweng
Message-ID <[email protected]>
On Mon, Jul 12, 2010 at 7:20 AM, Morten Brodersen <[email protected]> wrote:
> * you mention that SO's can inherit from a common interface in order to
> reuse PO's -- but doesn't that undermine their state-only-ness?
>
> It is fine for an SO to inherit from a common interface. It doesn't make it
> a non-state object. It just allows the same PO to use different SO
> implementations. For example, one SO can be memory based and another can be
> disk/network based. You might also have a SO that logs all method calls for
> debugging purposes.
>
> * it sounds like this would result in a lot of dynamic allocation of SO's
> and SO collections, that would not occur in a more conventional OO
> implementation.  Is that true, and is it a problem?
>
> Whether you keep allocating the same SO over and over again or simply keep
> one alive through the whole game is up to you. It is independent of whether
> you use the SO/PO pattern.
>
> * what about a complicated persistent data structure like a spatial index,
> that refers to other objects in the game?  Is this allowed in an SO?  Is it
> an exception to this pattern?
>
> A SO can be big or small. It doesn't matter. What matters is whether it is a
> SO or a PO. In the extreme case the whole game state can be a single SO. And
> there will be a number of PO's "operating" on it. However I wouldn't
> recommend that architecture because the PO's will have to do a lot of
> pointer walking down the SO tree before they get to the object they want to
> operate on. That is a bad idea for a lot of reasons (cache performance,
> lines of code etc.) and if you then change the structure of the SO, a lot of
> PO's will be impacted. The ideal is to have a number of clean standalone
> SO's with PO's transforming between them.
>
> * is there a more detailed writeup or example system (I.e. with source code
> available for study) that illustrates this?
>
> Not anything that is easily digestable unfortunately. What I would recommend
> is to simply try it out. The #1 idea is to stop thinking about creating a
> big object tree for your application. Instead, create a flat graph of
> objects with PO's connecting SO's. For example, for a game you might
> traditionally have designed an architecture like this:
> Boot->Application->Game->Level->Scene->PVS->Entity->Model->VertexBuffer->Vertex->Color->Red
> i.e. a top-down object tree with the boot code (Boot) as its root. Try
> instead to take the hierarchy and flatten it out so that you have a number
> of SO's and PO's that process and convert them. A PO that loads from a file
> (SO) and creates a model (SO). A PO that modifiers the model (SO) in various
> ways. A PO that converts the model (SO) to a DirectX Vertex/Index Buffer (2
> SO's). A PO that takes a controller input (SO) and converts it to a game
> specific input (SO). A PO that creates a height map (SO) from an image (SO).
> A PO that converts a height map (SO) to a DirectX Vertex/Index Buffer. A PO
> that takes Vertex/Pixel shaders (SO) and VB/IB's (SO) and draws a model on a
> render target (SO) etc. A good way to learn the SO/PO approach is to take an
> object hierarchy from one of your application or the idea for an application
> you want to write and convert it to a flat PO/SO structure. Just use a
> drawing tool (or pen and paper for that matter) and use boxes for SO's and
> circles for PO's with arrows connecting them. It is a fun little exercise.
> And hey for the more advanced practicioners, to reach level 2 and become a
> true SOPO Ninja, think hard about how many of the PO's you designed that can
> fairly easily run in its own thread. The answer might surprise you.

Another question that jumps to mind: is a PO the same as a non-member
function?  Or might they ever have state?

I'm leaning towards giving this a try but I'm still a little fuzzy on
the differences (if any) between this and non-OO procedural
programming, or OO programming with very flat inheritance.

-T

>
> * Thanks for any insights!
>
> You are welcome :-)
>
> Morten
>
> -----Original Message-----
> From: [email protected]
> [mailto:[email protected]] On Behalf Of Thatcher
> Ulrich
> Sent: Monday, 12 July 2010 2:06 PM
> To: [email protected]
> Subject: [Sweng-Gamedev] State Object / Process Object design
>
> This sounds really interesting and enticing but I still have a lot of
> questions:
>
> * you mention that SO's can inherit from a common interface in order to
> reuse PO's -- but doesn't that undermine their state-only-ness?
>
> * it sounds like this would result in a lot of dynamic allocation of SO's
> and SO collections, that would not occur in a more conventional OO
> implementation.  Is that true, and is it a problem?
>
> * what about a complicated persistent data structure like a spatial index,
> that refers to other objects in the game?  Is this allowed in an SO?  Is it
> an exception to this pattern?
>
> * is there a more detailed writeup or example system (I.e. with source code
> available for study) that illustrates this?
>
> Thanks for any insights!
>
> -T
>
> On Jun 3, 2010 11:01 AM, "Morten Brodersen" <[email protected]> wrote:
>> There are obviously a lot of people on this list interested in software
>> architecture/design. Especially how to do it in a pragmatic/effective
>> way with language such as C++.
>>
>> During the 15+ years I have been working in the games industry (and the
>> 30 years I have been writing software), I had a lot of opportunities to
>> experiment with software architectures for games (from the engine all
>> the way up). Some architectures worked well, others less so :) But after
>> many interesting experiments I ended up with a non-complex way to design
>> software that works well for small and large scale applications. And it
>> (surprisingly) works well not only for OO style languages but also for
>> functional style languages (and multi-threaded applications!).
>>
>> I have no idea whether the approach will work for anybody else. I am
>> definitely not making that claim. But hey it might be interesting for
>> you so here it is:
>>
>> Think about your application as consisting of 2 types of objects: State
>> Objects and Process Objects.
>>
>> A State Object (SO) handles a state (surprise). It can be anything from
>> a texture to a database to a display to a list of integers. In an OO
>> language, SO's will typically be implemened as classes/objects with the
>> usual set/get/add/remove methods. In a functional language, SO's are
>> typically implemented as (immutable) values.
>>
>> The Process Objects (PO) takes as input one or more input SO's and
>> transforms/outputs it to one or more output SO's. In an OO language,
>> PO's are typically implemented as classes/objects/static
>> methods/functions. The PO's might use temporary data while processing
>> input to output but it normally doesn't maintain a state. In a
>> functional language PO's are typically implemented as functions.
>>
>> Large scale applications are constructed by joining SO's and PO's
>> together in a graph structure:
>>
>> S0 -> [P0] -> S0 -> [P0] -> SO -> [PO] -> SO
>>
>> I can of course only show a linear chain in this email so imagine a
>> number of boxes (SO's) and spheres (PO's) spread around on a piece of
>> paper and lines going from (input) SO's to PO's and from PO's to
>> (output) SO's. The graph typically have loops making it possible to (for
>> example) feed the output of a game tick into the next game tick.
>>
>> The SO/PO approach can be used at any abstraction level. Here is (part
>> of) a very high level architecture for a game:
>>
>> GameConfig -> [GameStateMaker] -> GameState
>> GameConfig+TestConfig -> [GameStateForTesting] -> GameState
>> NetworkMsg -> [GameStateFromNetworkMsg] -> GameState
>> GameState+Time -> [GameTicker] -> GameState
>> GameState -> [GameTester] -> GameTestReport
>> GameState -> [GameSaver] -> GameFile
>> GameFile -> [GameLoader] -> GameState
>> GameState -> [GameStateToNetworkMsg] -> NetworkMsg
>> GameState -> [GameStateVerifier] -> GameStateErrors
>> GameStateErrors -> [GameStateErrorPrinter] -> Console
>> GameStateErrors -> [GameStateWindowMaker] -> Window
>> etc.
>>
>> And some low level ones:
>>
>> Camera+EntityList -> [VisibilityChecker] -> EntityList
>> EntityList -> [EntityRender] -> DirectX
>> EntityList -> [EntityPrinter] -> Console
>> etc.
>>
>> In C++, you could for example implement the very high level architecture
>> this way:
>>
>> static void Game::tick(GameState* gs, float time) { ... }
>>
>> The reason why the SO/PO approach works well is because the application
>> ends up consisting of a flat collection of self contained SO's that are
>> easy to understand, test and reuse. And a collection of PO's that also
>> are easy to understand because they simply transform the input SO's to
>> the output SO's without relying on any internal state.
>>
>> And because the application is broken down into a large number of SO's,
>> you can typically add new functionality by adding an extra
>> serial/parallel/concurrent transformation step to the architecture
>> without having to touch the already working code. You usually do not end
>> up in the "grab the banana and you get the whole jungle" sitation that
>> is typical of C++ software.
>>
>> If a number of SO's inherit from the same SO interface, you can easily
>> pick the best SO for the job without having to touch any of the
>> surrounding PO code that use it.
>>
>> If SO's are organized as lists of simple values/objects, you get the
>> cache performance advantage discuss previously in this email list.
>>
>> If some of the PO's are implemented as threads with the input/output
>> SO's implemented as semaphore protected data/queues, you get a cleanly
>> architectured multi-core/threaded application.
>>
>> Morten
>>
>> _______________________________________________
>> Sweng-Gamedev mailing list
>> [email protected]
>>
>> http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
>
> _______________________________________________
> Sweng-Gamedev mailing list
> [email protected]
> http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
>
>
_______________________________________________
Sweng-Gamedev mailing list
[email protected]
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com