- CGame is the game manager. It directs the objects that manipulate the game's resources, such as Graphics, Sound, and Input managers.
I would describe it as the driving force behind the overall engine, as it dictates when logical and render updates occur and synchronizes other managers to these events. For a specific example, the sound thread will always be updating at a regular interval on its own, but to avoid delay on sound events the CGame class also tells it to update on each frame (this can be capped to once every logical-update duration).
- These would supposedly be in class objects owned by a CGame object (CMyGame), accessible by functions defined for CGame.
Could be.
- CGame doesn't own any CGameState objects-- it owns a StateMachine, which manages the Game States much like the Sound or Graphics managers manage their components.
- CStateFactory is a simple class that creates an object that derives from the virtual base class CGameState (CMainMenu, CGameplay, CPauseScreen, etc.), and returns a pointer to the new object's location for the CStateMachine object to manage.
- CGame calls whatever function that drives the game forward at every iteration through the game loop, which should not contain any game logic such as collisions, whether or not the player is dead, whether or not the room has changed, etc. etc.
- This would supposedly be in, say, CGame's Run() function, which calls the Tick() function of CStateMachine, which would call the Tick() and Draw() function of whatever class derives from CGameState, where the game logic would be executed.
Correct.
- A pointer to CGame is passed to every CGameState class so that game logic that happens in each CGameState class could trigger a change in states, for instance, pressing the Pause button would call CGame->SetState(gsPause, X), the second parameter being either a simple information (level X, enemy script X) or a pointer to a more complex struct or class holding more information; possibly the player's stats, game world flags, etc.?
If you had used any other example besides pausing this would all be correct. If you change states to a pause state you lose all current game information and it is not possible to perfectly resume the game then. Pause is handled manually as an extra layer you have to draw over your game by yourself. It is however possible to have the state machine manage a stack of states (as my engine does) and in this case you could still make the pause menu be its own state, just on top of the rest. This prevents the game state from being wiped from memory.
- This is possible because INT_PTR/UINT_PTR is a pointer typecasted to an integer, so either the literal numerical address of the pointer or a plain integer are acceptable inputs
Typecast to an integer with the same number of bits as a pointer, yes. Meaning it is 64 bits on x64 machines.
- The CGame class would own a member object of the CGameState type, m_sNextState, which has a flag in it of whether the state needs changing or not, bSetNextState. This would be flipped to true if a state calls SetNextState from the CMyGame pointer.
m_sNextState is not a CGameState class. It is a nested structure inside of CGame that holds the ID of the next state, data to pass to it, and the flag to indicate that the next state should be set.
- This is necessary because if the state immediately changed in the middle of whichever CGameState's Tick() function with a more consequential change, such as from a Menu state to a Gameplay state, the rest of the Tick() function wouldn't execute, which may have been important in say, deallocating Menu's resources or playing an animation or tidying up or whatever.
Control would return to Tick(), but the object to which it “belongs” would have already been destroyed and you would likely get a bad access crash (if you are lucky).
- The CGameState objects would contain other objects like players, enemies, buttons, etc. Only their positions, members, flags, etc. would be manipulated. The job of drawing them is up to the Graphics manager.
There are several ways to go about this. Any decent game engine should allow you enough control to have individual objects and manage/manipulate them freely, leaving you with that option, but they should also have a CSceneManager. Your state only needs to create one of these and then populate it with all the game objects and it will handle the rest.
Example:
bool CGameplayState::Tick( CGame * _pgGame ) {
m_smScene.Tick( _pgGame );
// Or-
//m_smScene.Tick( _pgGame->DeltaTime() ); // Etc.
}
The scene manager would handle physics on everything in the world.
bool CGameplayState::Draw( CGame * _pgGame ) {
m_smScene.PrepareToDraw( _pgGame );
// <Insert Camera Updates Etc.>
m_smScene.Draw( _pgGame, m_pcCurCamera );
}
And the scene manager will cull the objects, generate shadow maps, send objects to render queues, and perform overall high-level render management. Actual drawing later gets delegated to the objects themselves, not a render manager. Such thing does not exist in my engine, or exists only loosely in terminology.
- The job of allocating and deleting objects used in CGameStates is up to the CGameStates.
It is but the use of smart pointers make this easy. My game states don’t actually have to release anything because all the objects are owned by the scene manager, and cleared easily via m_smScene.Reset().
1) Does the flag for changing the state on the next Tick() necessarily have to be in the m_sNextState object? I feel like it would be a little cluttered for the CGameState class, because it's not up to the CGameState as to when the state changes. I feel like that's more of the job of the CStateMachine, right?
States decide that they want to change to a new state.
The game class decides when that change happens.
The state machine (or state manager) actually makes that change.
m_sNextState is not related to CGameState.
2) What exactly is a script? You have as an example, EnemyScript3.ini as something that would be triggered by fitting 3 in SetNextState's second parameter. I have no idea what to think of scripts or how to implement them in C++.
Then you are not really ready, and they aren’t necessary for simple games. You can read about LUA for more information.
3) Can you describe what a game engine is, exactly? What would this be in the code? The WinMain() function? The game loop? Some kind of class?
Firstly, a definition: A game engine is a game framework in which the main loop is provided for you. A game framework is a collection of reusable game components that make it easy to build a game. Some argue that frameworks and engines are exactly the same thing, but there is a subtle difference—if the “engine” requires you to make your own game loop then all it is doing is providing you with a bunch of helper tools and classes for creating a game. That is a framework. It becomes an engine when it provides all of that but also provides the implementation for the game loop and causes you to work fully enclosed within it.
In code, of course it is not an engine unless you also provide the helpers for physics, rendering, characters, etc.
But to make it an actual engine would include WinMain(), the message pump, and the CGame class (which decides how to distribute calls to Cick() and Draw()).
4) Say I have my CGame class all set up. So if I created five enemy objects in my Gameplay state, would I pass a pointer to an object that contains their location, sprite details (animation speed, current frame, etc.) to CGame's Graphics handler, via some function like AddObject()? I looked into it a little and it's been suggested that I use a hash map with a uniquely generated ID for each object of the same type so that they can all be drawn separately. Is this necessary, and does it sound like a good idea, or is there a better way?
I don’t see the purpose.
But this question is too encompassing to be fully answered here.
Firstly, read back on what I said about the CSceneManager. It will answer must of this.
To repeat, the scene manager handles high-level orchestration of all of the game objects, and at the end, once the objects have been sorted by the render queue, each object is drawn in sorted order by telling the object to draw itself.
I chose this route because the way in which objects can be drawn varies to such an extreme that it would be too messy to try to generalize it all within a single manager. For example the way to draw GeoClipmap terrain is completely different from how standard models are drawn. And if you plan to support iOS you can’t (without hacks, which will get your app denied) use GeoClipmap so you need GeoMipmap as a fallback if you want to support that. Another totally different method of rendering.
This improves performance as well, since each object initially incurs the overhead of only a virtual function rather than every object having to go through a generalized graphics-manager call with if/else checks on every single possible render state, even if those render states are completely unrelated to the object being rendered.
That’s not to say that some things can’t be generalized. A lot of things can be boiled down to a collection of states that need to be set, and that can easily be wrapped into a structure for each object and passed off to some kind of manager, but I would stop there and let the actual draw calls be issued by the objects themselves. Later once you have enough different types of objects you can find out what they all have in common and push that off to the graphics library, but in order not to get stuck in a hole I would suggest doing that one step at a time.
As for the hash map, there is no purpose.
The model library loads shared models, and at a higher-level (inside the engine module itself in my case) there exists CModelInstance, which as the name suggests is an instance of a shared model. Only instances get loaded into the game world and since each instance is already unique, what would the purpose of a hash map be?
L. Spiro