Saturday, January 17, 2015

Expanding the Animation Class

The first thing I noticed after creating the animated sprite is that I had the need to allow a single sprite to have multiple animations. This was apparent when I wanted to have a player controlled sprite move around the screen in two dimensions. Instead of creating a new rendering component as I initially used, I chose to create an animated sprite and use the same rendering component. This means that the user can once again extend this animated class to do whatever they require. 

There are many corner cases that need to be analyzed later to determine how to make the animation transitions seamless.



Above is a small video of the character moving around the screen. I show some movement while holding the arrow keys down, as well as showing some intermittent key presses that make the sprite look like it's in slow motion.

Below is the controlling application example.


 #include "precomp.hpp"  
 #include "Engine.hpp"  
 #include "Animation.hpp"  
 #include "AnimatedSprite.hpp"  
 #include <stdlib.h>  
 #include <iostream>  
 Engine* engine;  
 AnimatedSprite* link;  
 std::string forward("f");  
 std::string back("b");  
 std::string left("l");  
 std::string right("r");  
 ge::Key lastKey = ge::Unknown;  
 int deltaMove = 10;  
 void doKeyRelease(int key)  
 {  
   static bool fs = false;  
   switch(key)  
   {  
     case ge::Escape:  
       engine->close();  
       break;  
     case ge::Return:  
       engine->setFullScreen(!fs);  
       fs = !fs;  
       break;  
     case ge::Left:  
     case ge::Right:  
     case ge::Down:  
     case ge::Up:  
       link->pause();  
     default:  
       break;  
   }  
 };  
 void doKeyPress(int key)  
 {  
   switch(key)  
   {  
     case ge::Left:  
       if(lastKey != ge::Left)  
       {  
         lastKey = ge::Left;  
         link->setAnimation(left);  
         link->goToFirstFrame();  
       }  
       else  
       {  
         link->play();  
       }  
       link->move(deltaMove * -1,0);  
       break;  
     case ge::Right:  
       if(lastKey != ge::Right)  
       {  
         lastKey = ge::Right;  
         link->setAnimation(right);  
         link->goToFirstFrame();  
       }  
       else  
       {  
         link->play();  
       }  
       link->move(deltaMove,0);  
       break;  
     case ge::Up:  
       if(lastKey != ge::Up)  
       {  
         lastKey = ge::Up;  
         link->setAnimation(back);  
         link->goToFirstFrame();  
       }  
       else  
       {  
         link->play();  
       }  
       link->move(0,deltaMove * -1);  
       break;  
     case ge::Down:  
       if(lastKey != ge::Down)  
       {  
         lastKey = ge::Down;  
         link->setAnimation(forward);  
         link->goToFirstFrame();  
       }  
       else  
       {  
         link->play();  
       }  
       link->move(0,deltaMove);  
       break;  
     default:  
       break;  
   }  
 };  
 int main(int aargc, const char* argv[])  
 {  
   // Setup the window  
   engine = new Engine("Game Engine Sprite Test", 800,600);  
   engine->setFrameRate(60);  
   engine->registerKeyPressedCallback(doKeyPress);  
   engine->registerKeyReleasedCallback(doKeyRelease);  
   // Load a texture  
   int t0 = engine->addTexture("link.png");  
   // Create collision area  
   std::vector<ge::fVertex> square;  
   square.push_back(ge::fVertex(0.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,64.0f));  
   square.push_back(ge::fVertex(0.0f,64.0f));  
   // Create a collider sprite  
   link = new AnimatedSprite();  
   link->setScale(.5f,.5f);  
   link->setTexture(t0);  
   link->setSpeed(100);  
   // Set link as a collider  
   link->setCollision(&square, ge::Collision_SOLID);  
   Animation* linkForward = new Animation();  
   Animation* linkBack = new Animation();  
   Animation* linkLeft = new Animation();  
   Animation* linkRight = new Animation();  
   // StartX, StartY, Width, Height  
   // create link forward movement  
   linkForward->addFrame(0,0,120,130);  
   linkForward->addFrame(0,520,120,130);  
   linkForward->addFrame(120,520,120,130);  
   linkForward->addFrame(240,520,120,130);  
   linkForward->addFrame(360,520,120,130);  
   linkForward->addFrame(480,520,120,130);  
   linkForward->addFrame(600,520,120,130);  
   linkForward->addFrame(720,520,120,130);  
   linkForward->addFrame(840,520,120,130);  
   linkForward->addFrame(960,520,120,130);  
   linkForward->addFrame(1080,520,120,130);  
   // create link left movement  
   linkLeft->addFrame(0,130,120,130);  
   linkLeft->addFrame(0,650,120,130);  
   linkLeft->addFrame(120,650,120,130);  
   linkLeft->addFrame(240,650,120,130);  
   linkLeft->addFrame(360,650,120,130);  
   linkLeft->addFrame(480,650,120,130);  
   linkLeft->addFrame(600,650,120,130);  
   linkLeft->addFrame(720,650,120,130);  
   linkLeft->addFrame(840,650,120,130);  
   linkLeft->addFrame(960,650,120,130);  
   linkLeft->addFrame(1080,650,120,130);  
   // create link back movement  
   linkBack->addFrame(0,260,120,130);  
   linkBack->addFrame(0,780,120,130);  
   linkBack->addFrame(120,780,120,130);  
   linkBack->addFrame(240,780,120,130);  
   linkBack->addFrame(360,780,120,130);  
   linkBack->addFrame(480,780,120,130);  
   linkBack->addFrame(600,780,120,130);  
   linkBack->addFrame(720,780,120,130);  
   linkBack->addFrame(840,780,120,130);  
   linkBack->addFrame(960,780,120,130);  
   linkBack->addFrame(1080,780,120,130);  
   // create link right movement  
   linkRight->addFrame(0,390,120,130);  
   linkRight->addFrame(0,910,120,130);  
   linkRight->addFrame(120,910,120,130);  
   linkRight->addFrame(240,910,120,130);  
   linkRight->addFrame(360,910,120,130);  
   linkRight->addFrame(480,910,120,130);  
   linkRight->addFrame(600,910,120,130);  
   linkRight->addFrame(720,910,120,130);  
   linkRight->addFrame(840,910,120,130);  
   linkRight->addFrame(960,910,120,130);  
   linkRight->addFrame(1080,910,120,130);  
   // Add the animation to the sprite  
   link->addAnimation(forward, linkForward);  
   link->addAnimation(back, linkBack);  
   link->addAnimation(left, linkLeft);  
   link->addAnimation(right, linkRight);  
   // initialize the link sprite  
   link->setAnimation(forward);  
   link->pause();         // we don't want him moving  
   link->goToFirstFrame();     // not strictly neccessary  
   // Add our link as a sprite  
   engine->addSprite(link);  
   //std::vector<Entity*>::iterator it;  
   while(engine->isRunning())  
   {  
     engine->run();  
   }  
   delete engine;  
   engine = NULL;  
 }  

Thursday, January 15, 2015

Animated Sprite

Developing the capability for animated sprites caused me to re-design how an entity is created. Before, you'd ask the engine to create one and return it to you. Well since you already have to have the header file for this and I want to provide the capability of custom updates to the entities, I have to allow the user to subclass the entities. This means the user is responsible for cleaning up it's entities. 

Below is the code for adding a sprite. Each sprite is a frame in a sprite sheet and is looped based on the speed the user wants.


 #include "precomp.hpp"  
 #include "Engine.hpp"  
 #include "Animation.hpp"  
 #include <stdlib.h>  
 int main(int aargc, const char* argv[])  
 {  
   // Setup the window  
   Engine* engine = new Engine("Game Engine Sprite Test", 800,600);  
   // Load a texture  
   int t0 = engine->addTexture("link.png");  
   // Create collision area  
   std::vector<ge::fVertex> square;  
   square.push_back(ge::fVertex(0.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,64.0f));  
   square.push_back(ge::fVertex(0.0f,64.0f));  
   // Create a collider sprite  
   Animation* link = new Animation();  
   link->setScale(.5f,.5f);  
   link->setTexture(t0);  
   link->setSpeed(200);  
   link->setLoop(true);  
   // StartX, StartY, Width, Height  
   link->addFrame(0,0,120,130);  
   link->addFrame(0,520,120,130);  
   link->addFrame(120,520,120,130);  
   link->addFrame(240,520,120,130);  
   link->addFrame(360,520,120,130);  
   link->addFrame(480,520,120,130);  
   link->addFrame(600,520,120,130);  
   link->addFrame(720,520,120,130);  
   link->addFrame(840,520,120,130);  
   link->addFrame(960,520,120,130);  
   link->addFrame(1080,520,120,130);  
   link->play();  
   // Set link as a collider  
   //link->setCollision(&square, ge::Collision_SOLID);  
   // Add our link as a sprite  
   engine->addSprite(link);  
   //std::vector<Entity*>::iterator it;  
   while(engine->isRunning())  
   {  
     engine->run();  
   }  
   delete engine;  
   engine = NULL;  
 }  


I will have another post explaining the entity class in detail and it's components.

Tuesday, January 13, 2015

Expanding the Entity Class

The core of a good 2d game engine is the entity support. I didn't want a bulky entity that tried to implement everything on it's own and force the game developer to implement everything on a child class. The design approach I have taken is a component system. The first component that I will be developing is the rendering component. 

The render component will provide all of the graphic display of the entity. This can be a static sprite or an animation. The static sprite can be an entire texture, or a portion of a texture. For example, you may load an entire texture into the video card and simply use a portion of it. The animation may load a texture of frames into the video card and then display the appropriate frame or sub-image of the texture as needed.

First we can see the graphics engine loading a sprite with a texture. This texture is a sprite sheet with multiple different views for the sprite's graphical representation.

Sprite Sheet Full Texture
 #include "precomp.hpp"  
 #include "Engine.hpp"  
 #include <vector>  
 #include <stdlib.h>  
 int main(int aargc, const char* argv[])  
 {  
   // Setup the window  
   Engine* engine = new Engine("Game Engine Sprite Test", 800,600);  
   // Load a texture  
   int t0 = engine->addTexture("link_64px.png");  
   // Create collision area  
   std::vector<ge::fVertex> square;  
   square.push_back(ge::fVertex(0.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,64.0f));  
   square.push_back(ge::fVertex(0.0f,64.0f));  
   // Create a collider sprite  
   Entity* link = engine->addColliderSprite(&square, ge::Collision_SOLID, t0);  
   //std::vector<Entity*>::iterator it;  
   while(engine->isRunning())  
   {  
     engine->run();  
   }  
   delete engine;  
   engine = NULL;  
 }  

Note: These may become integer based sizes as images are in whole pixels, but I am testing whether keeping everything in float is better than converting between int and float repeatedly.

Now you can see in the above picture and code that loading a texture into the sprite causes the entire texture to be drawn but we don't want that in the case of sprite sheets.

Single Frame Sprite

 #include "precomp.hpp"  
 #include "Engine.hpp"  
 #include <vector>  
 #include <stdlib.h>  
 int main(int aargc, const char* argv[])  
 {  
   // Setup the window  
   Engine* engine = new Engine("Game Engine Sprite Test", 800,600);  
   // Load a texture  
   int t0 = engine->addTexture("link_64px.png");  
   // Create collision area  
   std::vector<ge::fVertex> square;  
   square.push_back(ge::fVertex(0.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,0.0f));  
   square.push_back(ge::fVertex(64.0f,64.0f));  
   square.push_back(ge::fVertex(0.0f,64.0f));  
   // Create a collider sprite  
   Entity* link = engine->addColliderSprite(&square, ge::Collision_SOLID, t0);
   // Set the sprite to a single frame
   link->setTextureRect(0.0f,64.0f,0.0f,59.1f);  
   //std::vector<Entity*>::iterator it;  
   while(engine->isRunning())  
   {  
     engine->run();  
   }  
   delete engine;  
   engine = NULL;  
 }  


Here you can see that with a single line change in the source, we now have just the forward facing Link. Now let's turn this into an animation and see how the sprite performs ... to be continued.

Wednesday, January 7, 2015

Entity (Sprite) Management

This evening, I have started work on the Entity Management system. I call it an entity management system because sprites are traditionally graphical entities and if there were any reason to have a non graphical entity or various types of graphical entities, this would provide a common base. The management system currently uses a std::set in order to keep track of the entities. A pointer to an entity is stored in the set and is used to delete and access the entity. This recommendation from a friend allows the access/deletion of the entities without iterating through the list and reducing performance.

The controlling application must load the texture into the engine before using it in a sprite. Below is an example of a main that would produce a single sprite with a texture. The deletion of the engine will call clear on the texture manager so there is on code visible for that. It will also remove sprites but I provided an example of how to remove just one sprite if needed.




 #include "Engine.hpp"  
 int main(int aargc, const char* argv[])  
 {  
   Engine* engine = new Engine("Game Engine Test", 800, 600);  
   int t0 = engine->addTexture("mario.jpg");  
   Entity* entity = engine->addSprite(t0);  
   engine->run();  
   engine->removeSprite(entity);  
   delete engine;  
   engine = NULL;  
 }  

Below is some scale, rotation and translation of the sprite. This is seamlessly handled by SFML.



Tuesday, January 6, 2015

Texture Management

The texture management is fairly simplistic. The management simply stores the texture and it's unique id in a map to be referenced when creating sprites. Since the textures are stored on the video card, they should be managed by the application as to when textures need loaded or removed from the graphics memory. The unique identifiers are limited to 65535. 

Concern 1: Current management does not reallocate ids of removed textures. I must weigh the pros and cons of simply adding more identifiers or reallocating ids when one is removed.

Concern 2: Using integer ids means that the controlling application must remember the ids. Perhaps I should store them by name or path. 

Design decisions may change how this is done entirely so I'm not too worried about it at the start. My starting goal was simply to store the texture which I accomplished.

I did use a singleton class. Whether this is really needed is debatable.

 // System Includes  
 #include <map>     //map  
 #include <iostream>   //cout  
 // SFML Includes  
 #include <SFML/Graphics.hpp>  
 // Engine Includes  
 #include "TextureManager.hpp"  
 TextureManager::TextureManager(void)  
 {  
   m_NumTextures = 0;  
 };  
 // Add an Texture to the Texture manager from a sprite sheet  
 int TextureManager::addTexture(const char* iTexturePath)  
 {  
   sf::Texture texture;  
   int retVal = m_NumTextures;  
   if(!texture.loadFromFile(iTexturePath))  
   {  
     std::cout << "Unable to load texture " << iTexturePath << std::endl;  
     retVal = -1;  
   }  
   else  
   {  
     std::cout << "Loaded texture " << iTexturePath << std::endl;  
     // TODO: Possible Latency issue with copying the texture  
     m_Textures.insert(std::pair<int, sf::Texture>(m_NumTextures, texture));  
     m_NumTextures++;  
   }  
   return retVal;  
 };  
 // Returns a reference to the Texture at the index to be used by the game engine  
 sf::Texture& TextureManager::getTexture(int iIndex)  
 {  
   return m_Textures[iIndex];  
 };  
 // Removes all Textures and resets ids  
 void TextureManager::clear(void)  
 {  
   m_NumTextures = 0;  
   m_Textures.clear();  
 };  
 // Removes a specific Texture id  
 void TextureManager::remove(int iTextureId)  
 {  
   // Don't reset the number of textures as we need to keep providing unique identifiers  
   m_Textures.erase(iTextureId);  
 };  

The above code is the source for the singleton texture manager as of now.

Getting Started with SFML and Linux.

This blog is meant to be a design journal as a 2d game engine evolves. It will be used as a record of design changes as this game engine is developed. The purpose of this game engine is not to re-invent the wheel for low level hardware interfaces. For this reason, the game engine will rely on the Simple and Fast Multimedia Library (SFML). This low level may be abstracted out later to allow for other low level libraries on different platforms.

This game engine will be developed on Linux and ported to other platforms. The game engine will be used to develop 2d platformers. I intend to have the engine support side scrollers and top down 2d games. 

There are plenty of tutorials regarding SFML out there so I will not be discussing the functionality provided by SFML unless it specifically drives design changes to the engine.

The primary functionality I wish to provide in the engine is to manage the entities or sprites that a game will need in order to remove that logic from any games that are developed with the library.

 // Initializes the engine  
 //   
 // This function sets up the rendering window for the engine.  
 // The input parameter iWindowName will be the title given to the window.  
 bool Engine::init(const char* iWindowName)  
 {  
   m_Window = new sf::RenderWindow(sf::VideoMode(m_Width,m_Height), iWindowName);  
   if(m_Window != NULL)  
   {  
     return false;  
   }  
   m_EntityManager = &EntityManager::getInstance();  
   m_TextureManager = &TextureManager::getInstance();  
   return true;  
 };  

The above code provides a basis for what we'll be doing. Initialization of the engine will setup the application window using SFML. The initialization will also record the singleton classes for the entity and texture managers.

The texture manager will ensure that any sprite needing drawn will use the appropriate texture and prevent the same texture from being created multiple times and thus slowing down the video card.

The entity manager will record all entities on the screen such as enemies, tiles, bullets, etc.
 
 // Cyclical loop for the engine  
 void Engine::loop()  
 {  
   while(m_Window->isOpen())  
   {  
     // Check for events like closures  
     handleInput();  
     // Update  
     update();  
     // Render  
     render();  
   }  
 };  

The above code shows the basis for the main engine loop. This should be done in its own thread. These will change as development continues.