Bouncing game -- how to program with OO?

Started by
4 comments, last by FurryKef 16 years ago
I've got this game, and I've already implemented a prototype written in Python with a naïve implementation. Now I'm rewriting it "for real" in C++ and I want to do it right this time. OK, here are the basic rules of the game, as far as this problem is concerned: * This is a simple "bouncing" game, like the Game & Watch game "Fire". You bounce balls off your paddle three times to safety on the other side of the screen. * The platform can only move left or right, according to the player's input. * All balls are spawned from a machine on the left side of the screen. I'll call this the "spawner". * There are three types of 'normal' balls: red, green, and blue. ** The player must change the color of the paddle to match the color of the ball. If the ball hits the wrong color platform, the player dies. ** If the ball hits the ground, the player dies. ** All three of these ball types behave exactly the same other than the color they need to be matched with and the height they reach when they are bounced. * There is a black ball. ** If the black ball touches the paddle, no matter the paddle's color, the player dies. ** If the black ball touches the ground, it bounces in the same manner as the other balls do when they hit the paddle. * There are bonus balls that give power-ups. ** If the bonus ball touches the paddle, it bounces regardless of the paddle's color. ** If the bonus ball touches the ground, it disappears and no bonus is given. ** If the bonus ball is counted as "safe", then a power-up is given. * No matter what type of ball it is, 1 point is given each time it bounces. * No matter what type of ball it is, after it bounces three times, it is counted as "safe" and bounced into the right machine, where it is removed from the game. I'll call the right machine the "receptacle". ** 1 point is given when any ball goes into the receptacle. * The movements of the balls are in sync with the music. All balls except the black ball always meet the platform on the beat; the black ball meets the ground on the beat. This screenshot from the prototype we showed to the IGF might help illustrate what's going on. You can also play the prototype itself here to see it all in action. (Instructions are contained inside the game itself. It is not fully implemented and does have some bugs and poor play control. Best played with an analog stick in my opinion.) Now here's the info on my naïve implementation: All the game objects are basically held in a global object (actually more like a struct in C terms) called "gvars". The player sprite, the platform, the spawner, the receptacle, and all the balls are here. I have a basic Ball class from which the other types of balls are derived. The idea was that the different behavior for each ball could be implemented using polymorphism. The ball has its initial position given to it when it is constructed, so it doesn't need to know where the spawner is. It can also take the location of the receptacle as a "destination" parameter so it doesn't need to know where it is. (I didn't implement it that way, but let's pretend I did.) Each ball bounces three times at the 1/4, 2/4, and 3/4 points between source and destination points (although it actually bounces to these points +/- a small random number). The ball paths are implemented as quadratic (single-handle) Bezier curves. This easily gives me control of the source, height, and destination of the ball while also ensuring that the ball reaches the destination at the designated time (so that it will make contact on the beat). The balls need to know where the platform is (so that we can ensure that they touch the platform on the beat), and the black ball needs to know where the floor is instead, because it's timed to hit that on the beat. The score is a global variable that is manipulated by the ball objects themselves. (Though this is ugly, to my credit, I did use a function to bump the score instead of just doing "gvars.score += 1", which allowed me to easily use multipliers.) My game logic was basically in one big ugly routine that did everything. So basically the game logic would call ball.update() on every ball, check for collisions, and call ball.collide() whenever one collided with the platform, or ball.hitFloor() whenever one hit the ground. If this event resulted in the player's death, ball.dead would be set to True. (The collide function for the red, green, and blue balls would look at gvars.platform.color to determine this. Hello, ugly coupling!) Now to think of how to start cleaning this up... My first thought here is that the balls could be created using the Abstract Factory pattern. The factory may be allowed to know these things, while the balls themselves do not; instead, the factory will tell the ball where it should bounce. Hmmm... I'd also like to use Model-View-Controller, but I'm not sure what exactly I should do... decoupling the view seems obvious enough: keep sprites separate from the objects they represent. I'm not so sure about the separation of the model and the view, though, especially where the movement of the balls is concerned. Although I think I'm a decent programmer, it's surprising how easily I am humbled in trying to figure out how to do things the "right" way. I'm also experimenting with test-driven development and I'm having trouble there, too -- but right now let's focus on what's good OO. I'd like to keep writing about what I'm thinking here, but it's getting late and I think it's time I let you guys chime in. (I know it looks like I'm really just being lazy and trying to make you guys do all the thinking for me, but I really have thought about this stuff and I'm still a bit stumped.) What do you think I should do? - Kef
Advertisement
I think you should request that a moderator should move this post to a more appropriate forum (probably Game Programming) where this post will receive far more views. I could imagine writing a big response to this post but it's late now and I won't try; perhaps tomorrow. Good luck!
The program didn't run for me, but from your descriptions, it's enough to develop a rough sketch. I've been developing a simple lite framework for games as well and can pass on some lessons learned. From the sound of it your interested in making a flexible and extensible framework using the MVC paradigm.

First off some basic explanation of MVC, try these links:

http://www.gamasutra.com/features/20050414/rouwe_pfv.htm
http://www.codeproject.com/KB/cs/PraticalMvc.aspx

First link gives a good explanation of how to use it in games, 2nd link a practical implementation example.

Thing to keep in mind MVC can be implemented in an infinite variety of methods each with their pros and cons. Get the gist of the ideas, that is :

-separation of controlling logic from internal simulation.
-internal simulation is self contained system with well defined exposed data which can be transformed into various views. How u expose this data is up too you.
-data transformed into various output ( be it graphical, audio, etc.. ) is done by a entirely separate module.

Model = internal simulation, holds game rules and logic in this case
View = output code, taking the Model as input
Controller = logic which controls and transforms the Model.

Fundamental technologies which you will need in one form or another:

>Resource manager
--For all the resources you will need, since u already have the prototype you should have a good idea of what they are (scripts, configurations, tuning parameters, gfx data, sfx data, gui, etc..) Centralized resource manager allows you to simplify resource management for all subsystems. Idea here build once, reuse everywhere.

>Finite State Machine
--I've seen so many game programmers skimp on this fundamental technology. Trying to implement the logic as complex if statements or switch blocks until it grows out of control and impossible to follow. Save yourself time and headaches, read up on FSM and Hierarchical FSM. Here are some links :

http://en.wikipedia.org/wiki/Finite_state_machine
http://www.ddj.com/cpp/184402040;jsessionid=PX2KGLQF4LLQIQSNDLPCKHSCJUNN2JVN?_requestid=25350

Build a HFSM framework which u can reuse for all the subcomponents ( controller (GUI flow, complex stateful control logic, etc..), Model (game logic), View (not so much here) )

The idea is once you build a robust framework with debugging you can disentangle complex state interactions in a consistent and meaningful way. Using a graphing software like (http://www.graphviz.org/) you can easily see the flow of logic, which makes for a very powerful tool. Idea here, Start simple build only as much as you need and incrementally add, don't over build, you might never need some of the more exotic features of HFSM.

>Event driven architecture.
-Why this technology? From experience, there are many problems when approached from a event driven arch. allows for the best compromise of simplicity and flexibility.

http://en.wikipedia.org/wiki/Event_Driven_Architecture
http://elementallinks.typepad.com/bmichelson/2006/02/eventdriven_arc.html

For example some places which you want to use event driven arch.

-input handling ( all inputs are descritized into events which are then passed into appropriate event handlers which allows you to decouple dependency from input create to input consumer ). Instead of checking if the stick state every frame, broadcast the stick delta events to appropriate handlers.

-networking : packets naturally map into the concept of events (discrete package of data ). What's stopping you from creating a data stream between 2 peers which automatically serialize input events from one peer and send it too the other which then apply to the local model? You've just built a simple network impl which falls out naturally from the architecture.

-loose coupling observers: lets say you have a visual representation of the object in the View well call a visual proxy. The visual proxy has the state of the object which it cares about ( position and rotation ) of the Model object. When you go to render the visual proxy, how do u make sure the visual proxy matches the model object? You could share data ( ie matrix ) but then you have a complicated linkage of memory dependency which leads to problems with scope, sure its solve able but increases program complexity. What if when the model change state ( ie moved or rotated ) it broadcast a message to all peers watching it ( in this case just visual proxy ). When the visual proxy receives such an event it updates its internal data to reflect this, thus removing the need to poll or directly bind the data to the model. You can add additional proxy easily, say an audio proxy, network proxy, AI focus proxy etc..

That's the idea, build a robust event framework ( they are simple to build ) and reuse it, it will pay off in reduced coupling and complexity if used properly. Some problems will not fit a event framework, don't try to squeeze it in there for sake of it.

>Scripting
--This pretty much is obvious, you will need 2 levels of framework a hard scaffolding for performance and structure and a soft framework for fast flexible safe iterative changes to behavior of the game/application. You can build all the game logic and rules and misc in C++ but that means you have to do full compile, test, fix, compile, test iteration steps. This vs a full featured scripting system with in game reset functionality, where you can play, fix script, reset/reload, play. The 2nd paradigm is orders of magnitude faster for projects of any size. Idea here, build in game reset functionality with script driven game logic for fast iterations.

Of course the script system should use the preceding technologies, ie HSFM, Events, Rsrc Manager to simplify itself as much as possible. The scripts are responsible for logic of the game. IE in this case what happens when balls bounce, controlling the ball bouncing, spawning, player movements, etc.. All of which is driven by events which queue up for the script each frame which get consumed within the scripts main loop, that's just an idea.

The division of how much logic is in C++ and script is up 2 u. You can easily define the entire object class hierarchy in script and build the barest C++ primitives ( curve solver, collision detection etc.. ) and get away with it. Or you can build mosty of the object properties in C++ and expose various functions to scripts, etc..

This should get you started.

Good Luck!

-ddn
Thanks, this helps a bit... I'm still not really sure how to implement my particular case, like how I should properly decouple the balls from the other stuff, but maybe I'll get it with some more thought. (Unfortunately, I don't have terribly much time to think about it yet because I have to rush a second prototype, which will probably be just as poorly engineered as the first one. Yeah, I don't really want to do it that way either, but stuff happens.)

Why does my program not run? Does it give an error message? Anything? It's a prototype, but it's still supposed to work...

By the way, using a scripting language would be overkill for this project... the rules that I described in the first post describe a large portion of the entire game. You say that a scripting language is fine for a project of any size, but I think this is a case where the game really is too small to need one. (And I'm well aware of the value of scripting languages... after all, I wrote the entire first prototype in one. ;))

- Kef
I fixed the problem by modifying the crisis.cfg script to use pygame vs opengl. I don't have opengl installed, as i assume most people don't either. It's a fun challenging game.

The idea is to build a resuable and flexible architecture which u can leverage to create tons of varied high quality content, not just for this game but many games in the future. The prototype was throwaway code which you've proven the game play concepts, the next step is building a robust framework from whcih u can expand upon that gameplay concept. Don't build more throwaway code, that's just burning your time.

Those technolgoies which I mentioned are all in some form or antoher used in every game/application. Getting a handle on them will give u the "tools" to visualize how u can breakdown the architecture of a game/app.

For your game, I would still use a scripting core for the gameplay rules and levels. Your going to have levels? In each level the ball beahvior will change in some way? Or maybe the player or spawner logic? How will u encode the gameplay changes per level? In C++ with hard coded configruations or maybe an external config file? or in a flexible scripting language describing that beahvior per level? Either way its your choice.

Here's a proposed breakdown using MVC for your game:

MODEL:
-holds the playing field container ( this is just a box for now )
-holds the spawner, player, and balls ( each represneted as some primtiive geometric object ie box, sphere )
-does the collision detection for objects ( ie player to ball, ball to floor, ball to spawner )
-holds the logic for each object ( the logic I would encode within a scripting language but u can encode it all in C++ too )
-each update step the MODEL moves all the object if they can be moved and does collision checks. This can be fixed or variable timestep dependeing upon what your criteritons are.
-when an object moves or collides or changes state an event is generated. These events are broadcast to any event listener ( read up on event driven arch. for these terms ). Some event listener are: View proxy objects, Script logic object, etc..
-lets say you are using a scripting engine and the script logic object gets an event of type collision where the partcipants are ball and player. Inside the script it parses the event and if the ball collides with the players paddle the script changes the velocity of the ball to make it bounce and also can play a nice sound sfx depending upon the balls color and speed.

This encapsualtes all the logic inside the model.

VIEW:
Upon starting up the VIEW queries the MODEL about all object inside it. For each object it checks if there is a "visual" represntation for that object. Usually the VIEW will hook itself to the MODEL event service to listen for events during game like spawning of new objects and deletion of objects.

Lets say the model player object has inside it a string call mVisualProxyType, and that has a value of "Player". And in the VIEW inside some big mapping of string to types. Using this this string the VIEW creates a new object of the given type. This object in this case is a visual player proxy object which is made specificly for this player. Upon creation, the visual proxy object registers an event listener to the model object its associated with. This will allow it to recive events about the player object.

Lets say the player hits a black ball wtih some electic enegry effect around it, the script upon reciving the event sets the player state to "dead" and sets the appendum deathtype as "electrical". Upon setting this state an event is emitted about player state change. This event trickles it way too the visual proxy object. The proxy object sees that the player had died to it plays the death animation. It also sees that the death type was electrical maybe it also plays some special gfx effect of electical sparks coming out of the players body too.

The VIEW and MODEL both have update function called each frame. The ususal order of events is to call the MODEL first then the VIEWs update.

CONTROLLER:
This guy also updates per frame, before the MODEL. It holds things like GUI state and some game play logic outside of the MODEL simulator. For instance when u begin the game, there is a menu. This menu is an object created by the CONTROLLER but lives really in the VIEW. It has no internal represntation inside the MODEL, since there really isn't an need for that, unless you really want too put all gameplay logic in the MODEL.

The CONTROLLER acts as a filter and for inputs. Let's say you move the joystick left. The CONTROLLER within its update step polls all input devices its intrested in. It sees the joystick has moved and creates an input event for that. That event can either go directly to the MODEL and is parsed somewhere inside it or sometimes people parse the input event inside the CONTROLLER and produce a refined input event tailored to the model. Lets say if your pressing the "A" button down while moving left you do a dash. If the MODEL does the input parsing/handling it would have to also do that statefully, ie keep track of all buttons to do this kind of logic. But using the 2nd impl where the CONTROLLER consumes raw input events, it keep tracks of such statefull data and instead of emitting an a 2ndary normal move event it emits a dash move event. There are pro cons to each, how you impl is up 2 u.

Good Luck!

-ddn

Quote:Original post by ddn3
I fixed the problem by modifying the crisis.cfg script to use pygame vs opengl. I don't have opengl installed, as i assume most people don't either.


Eh? OpenGL has been bundled with Windows since Windows 95. (Granted, possibly not the first release of it, but that's being picky. ;)) There's obviously a compatibility problem of some kind, but I doubt it's a matter of something not being installed, unless it's an issue specific to your video card driver.

Unfortunately, I'll need to get through this second prototype before I can think much about the OO side of things again, but you have given me some ideas, and I'll revisit this thread when I'm ready to take on my design "for real". Thanks :)

- Kef

This topic is closed to new replies.

Advertisement