Sunday, February 8, 2015

Background drawing with parallax sprites.

With the introduction of a background image, I wanted to make it more robust than simply drawing a background sprite. I also wanted the user to be able to define multiple background layers. Why stop at just backgrouds? I wanted to give the user a method of drawing a foreground as well. The answer to this design comes in the form of Parallax Drawing. In a nut shell it means that a sprite moves in relation to the camera or in our case, viewport. 

Instead of giving the game engine an api to make backgrounds or foregrounds, I wanted to let the game engine's base sprite management handle the drawing of these layers like there were just another sprite. The controlling application wouldn't want to use these as collidables as it would create adverse affects and hinder character movement. The parallax sprites move in opposite relationship to the viewport. We must, however, provide a speed. We want foreground images to move faster past our view than the background.

See the video below for a demonstration of both parallax drawing and binding our viewport to the world.



You can see that the foreground moves faster than the background. When link hits the right edge of the world, the viewport stops. Obviously in a real game, the viewport would be locked to link. Usually placing him in the center of the screen or some buffer from the left and right edges. This is simply a test.


Saturday, February 7, 2015

Graphics Rendering and Management

A lot of development has gone into the graphic rendering of the engine and has caused the delay in blog posts. I don't claim this is the best design but I tried several others that weren't complex enough to do everything I imagine this engine should do. The engine should be designed to support the following game modes:

  • Top Down
  • Side Scroller
  • Dungeon Scroller

Viewport


The viewport is the visible area of your game. For some types of games, this viewport may be movable or even scale-able. The viewport can be the entire drawn area or just a portion. 


Tilemap


The tilemap consists of any number of layers. The layer maximum must be provided to the game engine upon initialization. Layer are arranged from 0 to the maximum. 0 being the lowest layer and drawn first. Memory is allocated per layer based on the number of rows and columns in the tilemap. Only tiles with a grid if of non zero are drawn. Tilesets for the tilemap are aligned in a grid with the first grid id being 1. The sequence follows the rows, not columns, meaning in a 10x10 grid, grid ids 1-10 are on the first row.


Sprites


Sprites can be drawn at any z depth. If a sprite is drawn on the same level as a tilemap layer, it is drawn after that tilemap layer.


Z Depth


Z depth determines the draw order. This is essentially the third dimension in our 2d game engine. Each layer of a tilemap should be a different z depth as two tilemap layers will overwrite each other.




In the above picture the cave is drawn at a higher z depth so it is drawn over the player character.


In the above picture the cave front is drawn at a lower z depth than the player character. Also, the path to the entrance is drawn at a lower level so that it looks like the player character is walking on the gravel tiles and standing in front of the cave.



Background


I debated having anything to do with a background. A background could easily be implemented as a single sprite that was as big as the viewport. I decided that managing this within the engine would be beneficial. I wanted to reduce the amount of code that a game developer would have to use and adding small things like this would help in faster deployment of new games. The engine will decide what bounds of the sprite to draw based on where the viewport is. This does, however, introduce the idea of a world into the engine design. Since the background I wish to provide is one where the viewport can show all of it or part of it, the engine must know the size of the world in pixels. 

I hope to upload some code in the next blog entry.

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.