• Advertisement
Sign in to follow this  

General Engine Optimizations

This topic is 1634 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm coding my first Engine and I'm unexperienced in both game programming and C++, so I'd like to ask about general optimizations and methods.

 

First, I'm going to develop games with a group of friends, and I decided to work in the engine, so my goal is to keep things as simple as possible for them and whoever else codes the game itself.

For example, I don't want to tell the game programmer "to create an object, you need to first load the object from the resource manager, call the object manager, load it to the graphic card, position it's orientation, then add it to the sceneGraph".

Instead, I'd like to keep it as simple as:

 

//Game Programming
GameStaticObject* playerHouse = new GameStaticObject();
playerHouse->Load("House1"); //done!

 

I'm going to be the only one coding the engine, so I'd like to hide how it's working and keep things simple, so when programming the game whoever does it doesn't need to know how the engine is doing it, but instead focus on creating the game.

 

So far my main layout looks like this:

 

                                  ___________________
                                 |    GameEngine     |
                                 |  .GraphicsModule  |
                                 |  .OSModule        |
                                 |  .SoundModule     |
                                 |  .NetworkModule   |
                                 |  .ResourceModule  |
                                 |  .InputModule     |
                                 |                   |
                                 |  .GameTime        |
                                 |  .GameConfig      |
                                 |  .GameScene       |
                                 |  .GameState       |
                                 |  .GameErrors      |
                                 |                   |
                                 |  .SetKey()        |
                                 |  .SetButton()     |
                                 |  .SetMouseMove()  |
                                 |  .Update()        |
                                 |  .Render()        |
                                 |___________________|
 
 __________________      ________________      ________________      _______________________
|  GraphicsModule  |    |    OSModule    |    |  OtherModules  |    |      GameObjects      |
|  .Init()         |    |  .Init()       |    |  .Init()       |    |  //Not really a class,|
|  .Uninit();      |    |  .Uninit()     |    |  .Uninit()     |    |  //I'm just grouping  |
|                  |    |                |    |________________|    |  //them all here      | 
|  #ifdef OGL/DX   |    |  #ifdef iOS/Win|                          |  - GameTime           |
|  .gfxAPI         |    |  .osAPI        |                          |  - GameConfig         |
|__________________|    |________________|                          |  - GameScene          |
                                                                    |  - GameState          |
                                                                    |  - GameErrors         |
                                                                    |_______________________|
 
                                   *And the Game Class*
                                  ______________________
                                 |         Game         |
                                 |  .GameEngine         |
                                 |  .Init()             |
                                 |  .Uninit()           |
                                 |______________________|

 

The GameEngine class have methods such as SetKey and Update so the Game class call them and don't have to worry about using the inputModule/graphicModule and figuring how it works, the class just focus on populating the data to the scene and updating the objects, and the engine deals with updating the states and rendering.

 

The Game Time and Main Game Loop

 

I've read about time in various sources and I think I got the concept of how I should implement it, but I'm not really sure if it's correctly implemented because I that got me really confused.

#include <chrono>
class GameTime
{  
    public:
        GameTime();
        void ResetTimers();
        bool UpdateGame();
        float GetInterpolation();
        __int64 GetRealTime(); 
    private:
        __int64 _currentRealTime;
        __int64 _currentGameTime;
        __int64 _nextGameUpdateTime;
  
        int _updatesPerSecond, _maxFrameSkip, _timeResolution;
        int _currentFrameSkipCounter;
        float _interpolation;
};
 
GameTime::GameTime()
{
    //Defining default values
    _updatesPerSecond = 25;
    _timeResolution = 1000 / _updatesPerSecond;
    _maxFrameSkip = 5;
 
    _nextGameUpdateTime = GetRealTime();
    _currentRealTime = _currentGameTime = 0;
    _currentFrameSkipCounter = 0;
}
//This is called once per logical update
void GameTime::ResetTimers()
{
   _currentRealTime = GetRealTime();
   _currentFrameSkipCounter = 0;
}
//This returns if the game logic should be updated or not, it's called right after ResetTimers()
bool GameTime::UpdateGame()
{
    if( (_currentRealTime - _nextGameUpdateTime > _timeResolution) && (_currentFrameSkipCounter < _maxFrameSkip) )
    {
        _currentGameTime += _timeResolution;
        _nextGameUpdate += _timeResolution;
        _currentFrameSkipCounter++;
        return true;   
    }
    else
    {
        _interpolation = (_currentRealTime + _timeResolution - _nextGameUpdate) / (float)_timeResolution;
        return false;    
    }
}
__int64 GameTime::GetRealTime()
{
    return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
 
//And my main game loop is this:
void GameEngine::Update()
{
    _gameTime.Reset();
    while(_gameTime.UpdateGame()) //Update the game until _nextGameUpdateTime > _currentRealTime
    {
       //Get the inputState from the last request to this current one
       InputState inputState = inputModule.GetInputState(_gameTime.GetCurrentGameTime());
       gameState.Update(inputState);
    }
 
    //Get the "left over time" and send to render
    Render(_gameTime.GetInterpolation());
}
void GameEngine::Render(float interpolation)
{
    //render here
}

 

Did I got the concept right?

After reading this post (http://lspiroengine.com/?p=378) I think I got the concept, but I'm not sure if it's correctly implemented.

 

The Input State

 

class InputModule
{
    public:
        void SetKey(int keyCode, bool keyStatus);
        void SetButton(int buttonCode, bool buttonStatus);
        void SetMouseMove(int positionX, int positionY);
        void SetWheel(int wheelMove);
        //Those functions above will fill the _inputQueue vector with timestamps
 
        InputState* GetInputState(__int64 currentRequest);
    private:
        InputState _inputState;
        vector<Input*> _inputQueue;
 
        __int64 previousRequest;
};
 
void InputModule::SetKey(int keyCode, bool keyStatus)
{
    Input* newInput = new Input();
    newInput->inputType = InputType::Keyboard;
    newInput->inputCode = keyCode;
    newInput->inputStatus = keyStatus;
    newInput->inputTime = Engine::_time->GetRealTime();
 
    _inputQueue.push_back(newInput);
}
//Same for all other inputs
InputState* GetInputState(__int64 currentRequest)
{
    ClearState();
    if(_inputQueue.size() > 0)
    {
        while(_inputQueue[0]->inputTime <= currentRequest)
        {
            switch(_inputQueue[0]->inputType)
            {
                case InputType::Keyboard:
                {
                    _inputState.keyboard[_inputQueue[0]->inputCode].keyStatus = _inputQueue[0]->inputStatus;
                    break;
                }
                case InputType::Mouse:
                {
                    _inputState.mouse[_inputQueue[0]->inputCode].buttonStatus = _inputQueue[0]->inputStatus;
                    break;
                }
                case InputType::MouseMove:
                {
                    _inputState.mouse.positionX = _inputQueue[0]->positionX;
                    _inputState.mouse.positionY = _inputQueue[0]->positionY;
                    break;
                }
            }
 
            //Input processed, removing it from the queue
            delete _inputQueue[0]; //memory
            _inputQueue.erase(_inputQueue.begin()); //queue
            if(_inputQueue.size() == 0) { break; }; //breaking if it's empty
        } 
    }
 
    return &_inputState;
}

 

Keyboard is an array of 256 elements with a single bool variable for the key status, same for mouse, but mouse is only 16 elements.

So I return an InputState with the bools true/false depending on whether the key were pressed until the requestedTime. I'm not sure if I need to keep track of the lastRequestedTime, so the variable is still there until I figure it out.

 

ClearState() iterates over the 256 keyboard array and 16 mouse array and sets all the bool values to false. I think there should be a better way to clear (maybe deleting the state and creating a new one is faster?) but I still can't test it.

 

The GameScene

 

class SceneObject
{
    public:
        GameObject* objectPointer; 
        //The game object also has a pointer to it's own SceneObject
        //So if it gets a child, it's easy to find and add
        SceneObject* previousObject;
        SceneObject* nextObject;
        SceneObject* parentObject;
};
class GameScene
{
     public:
         //functions to add and remove sceneObjects, same as linked lists
         void Add(SceneObject* sceneObject);
         void Remove(SceneObject* sceneObject);
     private:
         SceneObject* sceneObjectList;
         SceneObject* currentObject;
}
 
void GameScene::Add(SceneObject* sceneObject)
{
    currentObject = sceneObjectList;
    while(currentObject->nextObject != 0)
    { currentObject = currentObject->nextObject; }
 
    currentObject->nextObject = sceneObject;
    sceneObject->previousObject = currentObject;
}
void GameScene::Remove(SceneObject* sceneObject)
{
    if(sceneObject->nextObject == 0 && sceneObject->previousObject != 0)
    {
        sceneObject->previousObject->nextObject = 0;
    }
    else if(sceneObject->nextObject != 0 && sceneObject->previousObject == 0)
    {
        sceneObject->nextObject->previousObject = 0;
    }
    else if(sceneObject->nextObject != 0 && sceneObject->previousObject != 0)
    {
        sceneObject->previousObject->nextObject = sceneObject->nextObject;
        sceneObject->nextObject->previousObject = sceneObject->previousObject;
    }
 
    delete sceneObject;
}

 

To add an object as child of another, I do this:

 

GameObject* myParentObject = new GameObject();
GameObject* myChildObject = new GameObject();
 
myChildObject->sceneObject->parentObject = myParentObject->sceneObject;

 

And for transformations I do this:

 

SceneObject* myCurrentSceneObjectTransformation = myCurrentObject->sceneObject;
while(myCurrentSceneObjectTransformation->parentObject != 0)
{
    //get the transformation mat4 from the object->gameObject and multiply to my currentObj
    myCurrentSceneObjectTransformation = myCurrentSceneObjectTransformation->parentObject;
}

 

I think this is a bad way to do it... anything recursive like this during the game loop looks like a bad idea to me, but I'm not sure and I couldn't think of other alternatives.

 

The Game Model

 

class BaseModel
{
    public:
        void Load(char* assetName);
        
        static ResourceModule* resourceModule;
        static GraphicModule* graphicModule;
};
class GameModel : public BaseModel
{
};
 
void GameEngine::Initialize()
{
    //Setting the addresses that the model will use
    BaseModel::_resourceModule = &_resourceModule;
    BaseModel::_graphicModule = &_graphicModule;
}
 
void BaseModel::Load(char* assetName)
{
    ResourceObject* resObject;
    if(resourceModule->IsLoaded(assetName)) //if the data is already in the vector<Objects> in the resourceModule
    {
        resObject = resourceModule->GetAssetData(assetName);
    }
    else
    {
        ifstream fileReader = resourceModule->GetPointer(assetName);
        resObject = new ResourceObject();
        //fill resObject from the stream
 
        resourceModule->AddResourceObject(resObject);
    }
 
    //fill this obj according to resObject
    vertexBufferObject.vertexes = new float[x];
    //etc
 
    graphicModule->LoadVBO(&vertexBufferObject);
    graphicModule->LoadMaterial(&material);
    //model ready for rendering
 
    //Thinking if I should add an instance to the sceneGraph already
}

 

This way I want to get to this:

 

GameModel* newModel = new GameModel();
newModel->Load("assetName"); //object ready to use/display ingame

 

This is what I got so far... do you have any suggestions or optimizations, or even alternatives for what I'm doing?

I'm unexperienced with C++ so I think there should have plenty of bad practices there too, and any advice is really appreciated.

Share this post


Link to post
Share on other sites
Advertisement

that is actually not too bad, a good start. First off be aware of memory and use smart-pointers when you want to express ownership. I think you are on the right track with thinking "this is what I want my interface to look like how do I get it". You should really be using the standard library, instead of taking a char* you should take a const std::string& and instead of storing your vertex buffer in a C style array you can use a std::vector.

Share this post


Link to post
Share on other sites

Are you using some kind of memory pooling? It is an easy to implement struct that increases the performance by a lot:

http://en.wikipedia.org/wiki/Memory_pool

 

Also for the boolean arrays, you can use bit masks to solve tests faster. You may also use bitsets if you want to just reduce size:

www.cplusplus.com/reference/bitset/bitset/

 

Finally, you should be careful with vectors, sometimes it is faster to use an Map or a list instead (specially if you are going to remove a lot of elements from it).

Share this post


Link to post
Share on other sites

If you spend time, before you even have a working game, trying to optimise .ClearState() method, then your priorities are really screwed up.

 

As far as I understand it, ClearState is called at most once per logic tick. It iterates an incredibly tiny array (only a few 100 elements) of bools. Therefore you almost certainly don't care.

 

Even if ClearState *DID* become a problem, you can deal with it later. Make your game work first.

 

It seems that you might be in danger of becoming an architecture astronaut, too. Remember to do the simplest thing that could possibly work.

Share this post


Link to post
Share on other sites

Are you using some kind of memory pooling? It is an easy to implement struct that increases the performance by a lot:

http://en.wikipedia.org/wiki/Memory_pool

 

Also for the boolean arrays, you can use bit masks to solve tests faster. You may also use bitsets if you want to just reduce size:

www.cplusplus.com/reference/bitset/bitset/

 

Finally, you should be careful with vectors, sometimes it is faster to use an Map or a list instead (specially if you are going to remove a lot of elements from it).

 

As for vectors, if element order is irrelevant you can swap the element you wish to remove with the last element in the vector and then remove the last element to make element removal faster.

Share this post


Link to post
Share on other sites

If possible use object pools to short-circuit overhead of creating/destroying similar objects constantly.  Variable sized sub objects may require seperate allocation, but fixed sized (even dissimilar) objects may still be numerous  (UNION and PACKED   ?? do these even work anymore for 'smart compilers???)

 

Data size minimalizing - picking proper data size to shrink data structures  (cache misses waste huge amounts of time for games which often have large randomly/irrefularly  accessed data sets)

Edited by wodinoneeye

Share this post


Link to post
Share on other sites

Are you using some kind of memory pooling? It is an easy to implement struct that increases the performance by a lot:

http://en.wikipedia.org/wiki/Memory_pool

 

Also for the boolean arrays, you can use bit masks to solve tests faster. You may also use bitsets if you want to just reduce size:

www.cplusplus.com/reference/bitset/bitset/

 

Finally, you should be careful with vectors, sometimes it is faster to use an Map or a list instead (specially if you are going to remove a lot of elements from it).

 

I'll add these two for the "enhancing" part, I'm still trying to get what's currently not working before optimizing what already is, thanks!

 

If you spend time, before you even have a working game, trying to optimise .ClearState() method, then your priorities are really screwed up.

 

As far as I understand it, ClearState is called at most once per logic tick. It iterates an incredibly tiny array (only a few 100 elements) of bools. Therefore you almost certainly don't care.

 

Even if ClearState *DID* become a problem, you can deal with it later. Make your game work first.

 

It seems that you might be in danger of becoming an architecture astronaut, too. Remember to do the simplest thing that could possibly work.

 

Oh I know I do that quite often, but I'm being careful with this one.

I have a very crude working project already but it's all hardcoded, nothing automated and classes are a mess.

 

For this one I'm trying to get everything well organized and hopefully better than my very basic prototype.

 

If possible use object pools to short-circuit overhead of creating/destroying similar objects constantly

 

By object pools do you mean this?

 

class ResourceModule
{
    public:
        //various Load functions
    private:
        vector<Mesh*> _loadedMeshes;
        vector<Texture2D*> _loadedTextures;
};

 

When my model object requests it's meshes/textures, the resource checks if they're loaded already and return a pointer, else I have to request the stream to load them in the vector and then return the pointer.

 

I'm not sure how this is usually done though, so I'm trying to come up with the resource format, something like this:

 

//Single File in Binary
//[byte size] #identifier
 
[int] MeshesHeader_start
[int] MeshesHeader_end
 
[int] TexturesHeader_start
[int] TexturesHeader_end
 
//MeshesHeader_start
[int] MeshID
[int] MeshFilePointer
//MeshesHeader_end
 
//MeshData Start - "MeshFilePointer" points to here
[int] vertexCount
[vertexCount * float] vertexData
[int] normalCount
[normalCount * float] normalData
[int] uvCount
[uvCount * float] uvData
[int] indexCount
[indexCount * int] indexData
//MeshData End
 
//TexturesHeader_start
[int] TextureID
[int] TextureFilePointer
//TexturesHeader_end
 
//TextureData Start - "TextureFilePointer" points to here
[int] textureWidth
[int] textureHeight
[textureWidth * textureHeight * 4 * char] textureData in char R, char G, char B, char Alpha format
//TextureData End

 

And I have another file that has a "Model" object which has a meshID and textureID, and I just ask the ResourceModule to check for these IDs and load in the vector if they're not there, or return the pointers if they are.

 

I'm unsure of how to deal with objects of multiple models, such as a sword that has two different meshes such as "Blade" and "Hilt", since my "Mesh" class has no positioning at all. Maybe a higher model that contains two+ models or something.

 

And should I keep a vector<Model*> too? It would only contain pointers or ints to the meshID and textureID, but I'm not sure if I should read from the file everytime a model is request or if I should store in memory to just loop through the vector.

They should be requested either during a loading phase if the game has levels, or during gameplay if it's open world, but I don't know if it's better to loop through a vector or read from a file.

 

Anyway, I got lots of these doubts which is probably due to my inexperience and lack of knowledge, so I really appreciate these advices, thank you very much!

Share this post


Link to post
Share on other sites

 


By object pools do you mean this?

 


 

He likely means the object pool pattern, an extremely useful software design pattern for games because you can offload the cost of allocating most things to loading screens/while you're loading files/etc, as allocating a new object is a relatively expensive operation.

You can find a nice game specific writeup about the pattern here.

Share this post


Link to post
Share on other sites

Since you're using C++11 anyway (as I can see from #include <chrono>), why not use fixed width integer types from <cstdint> instead of non-portable __int64?

See: http://en.cppreference.com/w/cpp/header/cstdint

 

You've also mentioned that you'd like to avoid sequential coupling:

For example, I don't want to tell the game programmer "to create an object, you need to first load the object from the resource manager, call the object manager, load it to the graphic card, position it's orientation, then add it to the sceneGraph".

 

However, you're using raw pointers with manual memory management, which introduces sequential coupling on its own (need to allocate before use, need to check against nullptr during use, need to delete after use). I think you should get rid of all of the resource-owning raw pointers: http://klmr.me/slides/modern-cpp/

Instead, by default prefer std::unique_ptr (to express unique ownership; note that it has no run-time overhead compared to raw pointers), and then (only if you absolutely have to share) std::shared_ptr.

 

See:

http://www.informit.com/articles/article.aspx?p=1944072

http://herbsutter.com/elements-of-modern-c-style/

http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/

Edited by Matt-D

Share this post


Link to post
Share on other sites

If you spend time, before you even have a working game, trying to optimise .ClearState() method, then your priorities are really screwed up.

 

As far as I understand it, ClearState is called at most once per logic tick. It iterates an incredibly tiny array (only a few 100 elements) of bools. Therefore you almost certainly don't care.

 

Even if ClearState *DID* become a problem, you can deal with it later. Make your game work first.

 

It seems that you might be in danger of becoming an architecture astronaut, too. Remember to do the simplest thing that could possibly work.

 

 

Unfortunately like most conflicting advice paradoxes   doing 'the simplest; thing' doesnt always result in success when a bad but easy solution becoming so imbedded that it cant practically be removed  (within resource budgets) when it is found to be a unworkable solution..

 

Design with performance consideration should be made but in proper moderation 

 

'work' doesnt equate to 'work to the specs and real final requirements of the project'  especially when prototyping just to 'get the damned thing working'  is often done.  (Ive seen enough 'prototypes' become THE product resulting in company killing delays)

Share this post


Link to post
Share on other sites

This has nothing to deal with optimizations. It is architecture. What's really worrying me is that nothing here really maps directly to real world usage. We have some high-level (supposed to be UML?) 10,000ft architecture pictures... awesome. And some nitty gritty details about how the engine manages its input and loop.

Wait, there's something directly mapping to engine usage, it is

//Game Programming
GameStaticObject* playerHouse = new GameStaticObject();
playerHouse->Load("House1"); //done!

There's a lot of trouble here. It's skipping a lot of details... but more importantly is: what about a dynamic object? What about a dynamic object with no collision? The "GameObject" is probably one of the worst inventions in game architectures ever... it's close to be usable when using a component model but this does not appear to be in this mindset. Anyway, at the very last, it should be like

GameStaticObject* playerHouse = new GameStaticObject("House1");

Because this means it would be RAII, which is good. But I am still scared about what you're going to write on the documentation... all functions taking an object will have to be documented with a game object sub-type because of course if GameStaticObject is "everything" then I'd expect to be able to do

GameStaticObject* emitter = new GameStaticObject("crackle.ogg");

As for the performance considerations: I strongly suggest to screw all that!

Share this post


Link to post
Share on other sites

The architecture was there because I thought it was relevant to the optimizations I was looking for... I had another topic about layout/architecture and I don't intend on changing it for now.

 

I was actually looking for optimizations and enhancements on the methods I described, since they're the ones I've finished so far, and some insight and tips on the rest.

For example, I got the input part after reading a post here in these forums, but it hadn't a single line of code, so I just did what I thought it was best. There's probably some other ways to deal with it that is more efficient, and that's what I was looking for.

 

My first input management didn't deal with time and was just a pair of boolean arrays for keyboard and mouse, it also had a bunch of design flaws.

 

 

So I was really looking for something like "this method would be better if instead of X, you did Y instead" and "using this is wrong, you should use that instead" etc

But I really appreciate the help so far, there's a bunch of things I didn't know and updating my code (I still use char* often and other C style functions that I'm trying to update) is one of the things I want to do.

 

As for the Objects, I divided them in 3 types: GameStaticObject (no animation), GameDynamicObject (animated), GameTerrain, and collision is a flag that I set to any of these objects.

 

I'm not sure how I'm going to deal with the name of objects, I'm thinking in using IDs instead of strings depending on how I do my map editor, so documentation might not be an issue since I'm the only one coding it and it'll be for maintenance purposes only.

Share this post


Link to post
Share on other sites


I'm not sure how I'm going to deal with the name of objects, I'm thinking in using IDs instead of strings depending on how I do my map editor, so documentation might not be an issue since I'm the only one coding it and it'll be for maintenance purposes only.
I strongly suggest to rethink your mindset. I don't see the connection between memory addresses, ids, object names and documentation - documentation for who by the way? For artists? Or programmers?

Share this post


Link to post
Share on other sites

Firstly, as was mentioned, you are asking about architecture, not optimizations.  Optimizations are about making things faster, not easier to use.

If you want people to be able to easily load things, you work in layers.

First you have the low-level method where everything is done manually.

Then you make file formats and editors that allow people to place buildings and things in a game world and save that to your own custom file.  It contains the models and where they go in the world.

Then you add a high-level layer over your engine that makes things easy to use by adding support for those file formats.

 

 

As for your architecture, as was also mention, not bad for your first go.

The most serious problem is related to how you handle input.  You never poll the current state of the keyboard.  You always buffer all input events and make sure all of them are handled in proper sequence.  If you poll the current state of the keyboard you can miss a key-press just because it was released again before it was polled.  Mouse events are even more prone to this problem.

 

I already outlined in detail how input should be implemented for smooth and reliable input while never missing a keystroke or mouse event:

http://www.gamedev.net/topic/641102-frame-independent-movement-and-slowdowns/#entry5049391

 

 

L. Spiro

Share this post


Link to post
Share on other sites

The most serious problem is related to how you handle input.  You never poll the current state of the keyboard.  You always buffer all input events and make sure all of them are handled in proper sequence.  If you poll the current state of the keyboard you can miss a key-press just because it was released again before it was polled.  Mouse events are even more prone to this problem.

 

I already outlined in detail how input should be implemented for smooth and reliable input while never missing a keystroke or mouse event:

http://www.gamedev.net/topic/641102-frame-independent-movement-and-slowdowns/#entry5049391

 

I followed that exact topic and got that input class like that from it, could you point me what is it missing?

 

What I understood about it was:

- I need to get all the input, and put them on a vector with timestamps. (vector<Input*> _inputQueue)

- SetKey, SetButton and other inputs received from the system are added to this vector (even mouse move, though I had to make a separate vector for it)

- At each logical update, the game state can call Input::GetState(__int64 currentTime) and it'll return a class with a keyboard[] and mouse[] (and other inputs I add later) with flags for pressed or released until the time requested. The inputs in _inputQueue until _currentTime are removed from the vector (and deleted).

- The next update will be from the last call until the requested time.

 

This way, also with fixed-time step implementation (that I read in your blog along with the 2 links there, very helpful!), if the game lags, each logical update should request the InputState from lastCall to currentTime, but I don't know if I implemented it correctly.

 

I'm still working on how I should deal with sequences such as "DOWN, RIGHT, PUNCH" for a fighting game for example, I'm thinking in adding a higher layer of input with another inputSequence vector for the last inputs, and/or use the same input class already and add another InputState for the previousInput so I can compare with the currentInput and fill this new inputSequence vector.

 

I think I'm too much concerned with performance, since I never developed a game before and have no experience at all, some things seems just too much.

I thought at first that multiplying a bunch of matrices for each frame for each object would totally kill the CPU. That's how I thought things were...

So I still worry that the functions/classes I make might be really inefficient so that's why I ask for "optimization" tips.

Share this post


Link to post
Share on other sites

I strongly suggest to rethink your mindset. I don't see the connection between memory addresses, ids, object names and documentation - documentation for who by the way? For artists? Or programmers?

 
 

Hmm actually I wasn't going to do any documentation at all, or very little if I thought I needed.

I tried to simplify and divide tasks so nothing gets big enough that needs documentation, except the flow order.

 

Something like:

class SomeObject
{
    public:
        void Load(string modelName)
        {
            _thisModel = GameEngine::resourceModule->GetModel(modelName);            
        }
};
 
class ResourceModule
{
    public:
        Model* GetModel(string modelName)
        {
            Model* returnModel = GetFromLoadedModels(modelName);
            if(returnModel == 0)
            {
                SearchResourceFileHeaderByName(ResourceType::Model, modelName);
               
                returnModel = new Model();
                returnModel->LoadModelDataFromStream(&_fileReader);
                
                _loadedModels.push_back(returnModel);
            }
            return returnModel;
        }
        Model* GetFromLoadedModels(string modelName); //loops through the _loadedModels vector and returns if it finds the requested model
};

So all functions are very short currently and I didn't even think of documenting how they work since it seems simple.

The complication is beginning to show though because of the numbers of layers I'm adding so the call flow looks messy...

 

When I want to create a new object for example:

GameObject* newObject = new GameObject();
newObject->Load(1);

This will look for the resourceID 1, will figure what it is (static or animated model, 2D or 3D), call it's subfunctions for loading, which will call the resourceModule's load resource functions, which (if they're not loaded already) will call each object's (mesh, material, texture) load function, and finally there'll be the loading code for each...

 

With this, the only documentation I want to is the "How to create the game" after every object's been created and all that's left is the game itself.

 

In the case of IDs I mentioned, I was thinking in leaving everything to the MapEditor, so even if the game developer named some resource as "X", the game engine would read it as 1, 2, etc

That's because I'm worried about performance and want to squeeze every drop of variable I can, but I'm trying to change this until I can get this working first and then I work on making it better.

Edited by Danicco

Share this post


Link to post
Share on other sites

What I understood about it was:
- I need to get all the input, and put them on a vector with timestamps. (vector<Input*> _inputQueue)
- SetKey, SetButton and other inputs received from the system are added to this vector (even mouse move, though I had to make a separate vector for it)
- At each logical update, the game state can call Input::GetState(__int64 currentTime) and it'll return a class with a keyboard[] and mouse[] (and other inputs I add later) with flags for pressed or released until the time requested. The inputs in _inputQueue until _currentTime are removed from the vector (and deleted).
- The next update will be from the last call until the requested time.

If that is what you implemented, perhaps I missed some details.
Except that they should not be deleted from the buffer that is exposed to the client. They are deleted from the original buffer that the client polls once they are polled.
That solves the problem you are having here:

I'm still working on how I should deal with sequences such as "DOWN, RIGHT, PUNCH" for a fighting game for example, I'm thinking in adding a higher layer of input with another inputSequence vector for the last inputs

The history of input up to either the last X events or last Y time should already be exposed to the client (the game loop). It’s just that that set of data is extended incrementally by the requested time on each update, as you described.

Then you can always look at the history and do a greedy parse for input commands. By “greedy”, imagine you have a game where ? ? does a move but ? ? ? ? does another. You match the longest input command you can.


L. Spiro

Share this post


Link to post
Share on other sites

Even if you dont have statewise input  (where buttons are held across significant time and the state from the previous cycle needs to be retained - and the event handler has to be modal to affect the correct handling of the current state  ex- drag-n-drop type inputs)  you still cutoff taking in new input  for each 'batch' at some point to do processing.  What happens to the inputs the player was already partweay thru a sequence of presses andit was cutoff by the timer?   (clinet can check for completeness before forwarding  but it then has to deal with the partial input situation)

Share this post


Link to post
Share on other sites

Even if you dont have statewise input  (where buttons are held across significant time and the state from the previous cycle needs to be retained - and the event handler has to be modal to affect the correct handling of the current state  ex- drag-n-drop type inputs)  you still cutoff taking in new input  for each 'batch' at some point to do processing.  What happens to the inputs the player was already partweay thru a sequence of presses andit was cutoff by the timer?   (clinet can check for completeness before forwarding  but it then has to deal with the partial input situation)

 

I didn't really understand it, but are you talking about how to manage events where the previous input is relevant to the current one (such as keeping the "drag" state)?

 

For drag and drop style, I'm going to keep 2 states of inputs, the current and the previous, so it's easy to check "if current key is pressed AND the previous was also pressed" means it's still dragging. I was going for one at first but then I noticed I might need to check the previous at some point.

 

By cutoff you mean the inputs that aren't being processed because it fell off the input sequence from the input timer? If that's it, I think they shouldn't be processed at all.

If my move command is DOWN, FORWARD, PUNCH, I'm going to set a timer threshold that the inputs stay in the sequence before being discarded, so the player can't press DOWN, go out come back a min later, FORWARD, PUNCH and the game still processes that.

Share this post


Link to post
Share on other sites

ClearState() iterates over the 256 keyboard array and 16 mouse array and sets all the bool values to false. I think there should be a better way to clear (maybe deleting the state and creating a new one is faster?) but I still can't test it.

 

memset will the be fastest way physically possible.

 

 

 

 


I'm unsure of how to deal with objects of multiple models, such as a sword that has two different meshes such as "Blade" and "Hilt", since my "Mesh" class has no positioning at all. Maybe a higher model that contains two+ models or something.

 

yep. that's a real model: two or more MESHES (not models). your "model" right now isn't a real multi-mesh model,  is just a mesh with texture (a simple mesh/texture primitive) .

 

 

 


And should I keep a vector too?

 

 

yes. load once, read many.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement