Successful Execution

posted in WISP
Published May 07, 2007
Advertisement
I just got done running my new prototype for the first time, with no runtime errors (yet). It doesn't do much, but I added a lot of code today.

I added a timer component which currently just gets the time from the Windows performance counter. It does a little bit extra, like remembering a starting point, translating into seconds (as a double), and so forth, but is relatively simple. I also have time iterator component that stores a desired frequency for iterations, and as time passes, it will say how many complete iterations were passed during that time, and the fraction of the way the time is toward the next iteration. This can be used for fixed-rate logic updates, for example. In a game-logic component, I can simply query the time iterator for complete iterations, and simply loop that many times doing the update. (This could be zero times, too, if a frame went by too quickly.) And the fraction toward the next iteration can be used for interpolation purposes.

I also added an Ogre component. I was undecided this morning on how precisely I'd do this, but I decided to keep it real simple. It's primary purpose is to expose a shared_ptr of Ogre::Root, and to do some minimal processing. When this component gets processed, it iterates through all render windows that had started processing from the previous frame, if any, and calls swapBuffers() on them, to present the image to the screen. It then goes through all the render windows again and calls update() on each of them. I separate the update() and swapBuffers() to hopefully get the parallelism benefits available from letting the CPU do other stuff while the GPU does its stuff between update() and swapBuffers(). The Process() function also handles whether I'm using an immediate present mode, v-sync present mode, or a fixed (but unrelated to v-sync) present mode. In the last mode, I use a time iterator to manage it. If the time iterator says 0 complete iterations have occurred, I don't do the update step. If one or more iterations have completed, then I do tell the render windows to update, once. No reason to tell them to update more than once, all one after another in quick succession. Basically, if we missed a few frames, just forget them.

In addition, I provided some helper functions to easily wrap Ogre pointers in a shared_ptr, along with a functor to call the appropriate member function of the appropriate owner object for cleanup. (To destroy an Ogre::RenderWindow, you need to call Ogre::Root::detachRenderTarget(), along with a pointer to an Ogre::Root. And so forth.)

Finally, I added a main menu state. Not that it shows a main menu yet, but it provides a state for the startup state to transition to, once it's done creating everything. Doing this alerted me to some problems with my use of the essence pattern, so I provided a little bit of template codery to help cut down on code that I'd have to write, while not losing any flexibility in design. It now shouldn't be too hard to create a new state, and even easier to write code to switch from one state to another.

I also changed the way my states specify the next state. Previously, at the end of a state's Process() function (which completes before any components are processed that frame), it would return the next state, or itself, if nothing changes. One minor problem is that this required using boost::enable_shared_from_this; no big deal, but I'd rather not have to if I could help it. A more serious problem is that I really don't know if I need to transition to another state until after the components have been processed, especially components like input and the GUI. I had intended allowing a state to inject its own component in the list of components that need processing (since a state gets to specify what goes in the list anyway), and this way, the state could do some processing after all the other components have had their turn. However, this still didn't allow me to specify a new state at that point. I could go back to my previous prototype's design, where I called PreProcess() on a state to get the list of components, I processed all the components, and then I called PostProcess() on the state to let it do its thing and tell me the next state. But I liked having just a single Process() function, so I decided to pass in a "next-state setter" into Process(). It has a function to set what the next state is. If you don't call this function at all, then nothing changes, and the current state remains active. I can then also pass in this object to the internal state component that gets processed at the end, and if it concludes that the state needs to change, it can go right ahead and do that. And since I don't need to do anything if the state doesn't change, then I can usually avoid boost::enable_shared_from_this. I'm happy, more or less.

So this main menu state merely tells a few components to process (timer, graphics time iterator, input, graphics, and internal state component), displays an 800x600 black window created by Ogre, and at the end checks all the keyboard events that occurred this frame. If one of them is a key-down on the escape key, I change set the next state to be an empty shared_ptr, thus terminating the program. Nothing fancy, but it shows that the updated design is working up to this point.

I should be able to get CEGUI working tomorrow pretty easily, assuming I work as well as I did today, and get all the other menu states written. Then I'll once again have interactivity of a semi-meaningful sort.
Previous Entry Collections Solution
Next Entry Slower
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement

Latest Entries

Advertisement