Welcome, Guest. Please login or register.

Pages: [1]
Print
Author Topic: Need New Model/Game Logic Framework  (Read 7326 times)
Technomage
Admin
Community member

Posts: 80



View Profile
« on: March 12, 2011, 03:11:38 AM »

A major issue that blocked my progress during the sprint was that there wasn't a well-defined and easily extended famework for the model portion of the code. For instance, when I went to attach character statistics to the character class itself I found that there was no easy to way to do so without doing some major refactoring. In the future, I expect that adding new character behaviors, traits, skills, inventory etc. will be equally as difficult given the current system, and will each require separate refactoring of the game logic.

The main problem is that we currently use some kind of hybrid inheritance/component-based design, where we have one monolithic CharacterBase class which hardcodes most of the character's functionality (visual appearance, some behaviors such as dialogue, etc.) and a few components like inventory and attributes which isn't well defined. In general I advocate against using inheritance to define "entities" in the game in which we have a hierarchy of classes with a monolithic base class like CharacterBase which NPCCharacter, PlayerCharacter etc. all inherit from.  The reason I am opposed to such an approach is because each time we add a new behavior or component we would need to refactor not only the child classes they affect, but also their base classes. This leads to bloated base classes that are difficult to maintain in the long run.

I've thought a lot about how to address this problem, and I've come up with two potential solutions.

  • The first solution would be to move to a pure component-based entity system, where there is a single Entity class which acts as a container for components such as behavior, AI, stats etc. An NPC character, for instance, would just be an Entity instance that has a certain set of components. Components then query their parent Entity class (or an component subsystem/observer) for the state of other components it depends upon.

    The benefit of a pure component-based entity system is that such systems are well-defined and have an abundance of literature on the subject, so prototyping work would be minimal.

  • The second solution is to try and implement a hybrid component/reactive system, where again there is a single Entity class which acts as a container but components are broken down into smaller, composable units called "Behaviors". Behaviors are time-dependent functions which are not executed directly, but which are called when one of their dependencies change (e.g. time), hence the "reactive" term.

    The benefit of this approach is that it is far more flexible and easier to manage in the long run because you don't have to explicitly deal with component dependencies, which could become a nightmare to manage otherwise. However reactive entity systems are pretty much an academic pursuit at this point, which means that we'd be on our own in terms of implementing it.

My personal preference is to try and implement a reactive entity system. The difference in total amount of work isn't going to be drastically different between the two, and in the long run a reactive entity system will be much easier to maintain if implemented correctly.

In case your interested in the details of each type of system, I've gathered a few references:
Component-Based Entity Systems
Functional Reactive Programming
Logged

"There are more atoms in the period at the end of this sentence than you can count in a lifetime." Science is awesome.

Beliar
Community member

Posts: 71

KarstenBock@gmx.net
View Profile Email
« Reply #1 on: March 12, 2011, 10:42:31 AM »

Both sound good, though i can't promise i can help you with either of them if we decide to use it.

Will look through the links though when i have the time.
Logged

Wing IDE - http://wingide.com/wingide - Free for OS use
aspidites
Community member

Posts: 29


View Profile Email
« Reply #2 on: March 12, 2011, 05:27:33 PM »

After reading through  most of the links you provided, i have reached a few conclusions:
1. As interesting a concept as FRP is,l it seems to mostly b an accademic one.After hours of research, I was only able to find one example (traits) of FRP in python. I, unlike you, think the learning curve for such an implementation would be steep.

2. The entities system seems more feasible, considering the prevelance of it in the gam industry. Im more confident that it would be easier to adapt this code to this paradigm than the former.

3. Either strategy would require a lot of facade code to wrap the existing fife api, which might cause some overhead.

I suppose we could create experimental branches for either or both. Either way, I agree that something should be done
Logged
kb1pkl
Community member

Posts: 24


View Profile Email
« Reply #3 on: March 17, 2011, 12:14:10 AM »

I swore I already made a post..

And since I don't feel like retyping it all, I'll paraphrase: I agree with aspidites.
Logged
Technomage
Admin
Community member

Posts: 80



View Profile
« Reply #4 on: April 12, 2011, 04:30:18 PM »

After a few weeks of researching and prototyping a reactive entity system I think I finally have a grasp upon the concept, so here's my take on it.

Summary: Differences Between Component-Based and FRP-Based Entity Systems
The core idea behind FRP (functional reactive programming) is that you are able to compose functions which define behavior into more complex behaviors. This is in contrast to a component-based entity system in which you start by defining complex behaviors and then refactor them into smaller, modular components. Both processes are a form of aspect-oriented programming, where you define what an entity "does" rather than what an entity "is" as in object-oriented programming, but are radically different approaches.

In component-based entity systems you define behavior by first defining a general category of behavior. This category of behavior eventually becomes a "Subsystem" or "System", and the specific types of behavior that belong to that category become "Components". The System acts as both a global manager for the Components it contains and defines how those Components communicate with one another and with Components belonging to other Systems. For example, you might define a MovementSystem which provides Position, Walk, Run, and Jump Components. Action-based Components will then need to query the Entity that contains it for other Components that it might need to interact with, e.g. a Run Component would need to interact with a Position Component as well as an Animation Component. In the case of the Animation Component, which belongs to another System (say, RenderingSystem), the Run Component will need to query the RenderingSystem to determine what it can do with the Animation Component. Intra- and inter-communication between Components and Systems quickly becomes a very complex problem, and component-based entity systems require a solid communication framework to work effectively.

In an FRP-based entity system, you don't bother with the intermediary step of defining a general category of behavior - you just write a function that does what you want. However, unlike in component-based entity systems these behavior functions usually do not have side-effects (i.e. they don't modify state), and thus don't have any dependencies to worry about (they are "atomic" operations). Instead, the function will expect some kind of entity state information as input and output the changes to that state. For example, you could write a move(position, velocity_vector) behavior function which would take the entity's current position and a vector defining how fast and in what direction the entity is moving and the move function would return the new position of the entity.

Entities and State in an FRP Entity System
State in an FRP entity system is contained entirely within Entity instances, which are basically fancy dictionaries. However, it is a little bit more complex than that: state is not represented by ordinary values, but by "Signal" instances. A Signal is simply a function that takes time as an input and returns a corresponding value, and represents a value that can vary over time. I must admit that I haven't grasped exactly how Signals and behavior functions are supposed to react to time, but the basic idea is that behavior functions are operations on Signals.

"Controllers" are functions that map an entity's state onto one or more behavior functions, and are the glue code between behavior functions and entities.

Generic Function Composition
The real power of an FRP-based entity system lies in the fact that it allows behavior functions to be generically composed to form more complex behaviors. For example, the move function above could be composed with a render(animation) function into a new walk(position, velocity_vector, animation) function which renders the entity's walk animation cycle.

Of course, the big question is: how do you compose behavior functions in a generic way? Yamppa http://www.haskell.org/haskellwiki/Yampa, which is the FRP Haskell library I based my prototype code on, relies on the Haskell "arrows" framework to do this function composition. The arrows framework is explained quite well on this website http://en.wikibooks.org/wiki/Haskell/Understanding_arrows, but the basic idea is that you can compose functions in one of two ways: in serial (the output of one function becomes the input of the next), and in parallel (input is split or cloned and passed to one function independently of the other, and the output of each is later joined). Arrows works fine for Haskell because it is statically typed and allows functions to be dynamically overloaded using type classes, but in Python it becomes quite cumbersome. I did get a working arrows implementation in Python, but it was so difficult to work with that I ended up scrapping it.

The Problem with Arrows and the Solution
Arrows ended up being very difficult to script without a DSL (domain-specific language) and placed unPythonic restrictions upon what inputs a behavior function can take and what outputs it can return (either a 1- or 2-tuple). I eventually decided to try and treat arrows-composed functions as restricted, directed graphs, which meant that I could use a graph language like Graphviz dot or GML and visual editors to script it.

I quickly found that treating function composition as a graph was really intuitive - functions are just nodes in the graph, and edges connect the inputs and outputs of the functions to each other. Since I was going to use a graph language for scripting anyway I decided to drop the restrictions that arrows placed upon the behavior functions. This meant that any Python callable could be used as a behavior function without modification and that function composition could be much more complex.

If you have ever worked with procedural noise/texture generation then you're probably quite familiar with this method of composing functions. This is what I mean: http://www.world-machine.com/images/ss6t.jpg (world-machine is a fantastic noise/texture/terrain generator, just FYI. Windows-only but works fine in Wine).

Conclusion
So that's my not-so-brief summary of my research and prototyping. There are still a few areas to iron out, but I think I have a good understanding of the theory and mechanics behind an FRP system. I'm curious to hear Beliar's opinion on this, since he was working on a component-based entity system last time I checked in with him.
Logged

"There are more atoms in the period at the end of this sentence than you can count in a lifetime." Science is awesome.

Beliar
Community member

Posts: 71

KarstenBock@gmx.net
View Profile Email
« Reply #5 on: April 14, 2011, 12:28:19 PM »

Sounds good. I think i finally undestand how FRP is supposed to work, though i still doubt i can help you with implementing such a system in PARPG. But if you think it is possibly i won't vote against it.
Logged

Wing IDE - http://wingide.com/wingide - Free for OS use
Technomage
Admin
Community member

Posts: 80



View Profile
« Reply #6 on: April 19, 2011, 04:37:35 PM »

So here's an interesting development: while doing research on entity systems I came across a full-fledged component-based entity system framework written in Python and C++ called "Grease"(http://packages.python.org/grease). The authors say it is alpha software, but it looks quite mature to me.

This definitely changes things. I honestly didn't expect to find any FOSS entity system frameworks on the 'net, much less one written in Python. If Grease works and is stable it could save us months of work developing our own framework, not to mention countless hours of maintenance. Even if Grease isn't production-ready, it's still better than starting from scratch and I'm sure the authors would appreciate the extra development.

My only concern with Grease is that it has the implicit order of execution dependencies (e.g. the rendering system has to be executed after the position system) which seem to plague component-based entity system designs. However, in the larger scheme of things it's probably easier to deal with those dependencies than to try and create the "perfect" entity system.
Logged

"There are more atoms in the period at the end of this sentence than you can count in a lifetime." Science is awesome.

aspidites
Community member

Posts: 29


View Profile Email
« Reply #7 on: April 20, 2011, 10:10:59 PM »

Maybe I read this wrong, but seems a lot of grease's functionality overlaps with fife's. Are you suggesting that we introduce pyglet and grease as a dependency and ignore the parts that fife takes care of (assuming that's possible), or are you suggesting that fife be forked or replaced all together?
Logged
Technomage
Admin
Community member

Posts: 80



View Profile
« Reply #8 on: April 23, 2011, 11:53:00 AM »

FIFE is definitely more than just a rendering engine, and does provide a basic system for organizing objects using Maps and Layers. However, these FIFE "Objects" are tightly integrated with the rendering code and as such it is very difficult to extend Objects to include the actual game logic. Plus this makes it difficult to sort out the rendering code from the game logic.

Grease, on the other hand, is simply a framework for defining the game logic in a modular fashion and integrating the game logic with other subsystems, like rendering. The pyglet dependency is mostly used in the examples that come with grease, though the Clock class is used extensively throughout the library, and since pyglet compiles on pretty much any system I think it is really a non-issue.

There is some overlap between grease and FIFE for sure, but I think that the two can be integrated successfully. I've been playing with grease and I've found that it is fairly trivial to simply wrap much of FIFE into two classes: FIFEInputSystem, and FIFERenderer. I already have a working prototype that can load and display a FIFE map. I think that the key to using grease with FIFE is to very carefully separate the game logic from the rendering and input/event code, which should make it a lot easier to define and extend the game logic without having to worry about the low-level stuff.
Logged

"There are more atoms in the period at the end of this sentence than you can count in a lifetime." Science is awesome.

zenbitz
Community member

Posts: 1164



View Profile
« Reply #9 on: April 27, 2011, 10:12:57 PM »

If I follow (and I am not trying that hard), we would have to implement a "Grease-like" model for our game logic objects anyway - they would just have pointers to FIFE (graphics) objects.

I think that should be OK.
Logged

We are not denying them an ending...
We are denying them a DISNEY ending - Icelus
Beliar
Community member

Posts: 71

KarstenBock@gmx.net
View Profile Email
« Reply #10 on: October 14, 2011, 02:02:27 PM »

A few days ago the move to a Component-Based Entity System, using grease, was completed.
Logged

Wing IDE - http://wingide.com/wingide - Free for OS use
Q_x
Admin
Community member

Posts: 553



View Profile
« Reply #11 on: October 14, 2011, 05:43:07 PM »

Yay! How cool! Thumbs up!
Logged

Pages: [1]
Print
Jump to: