-
Notifications
You must be signed in to change notification settings - Fork 2
Main.cpp and game loop
Here is the main program that has the game loop of the program.
//Include C++ system libraries
#include <ctime>
//Include local headers
#include "Swarm.h"
int main(int argc, char *args[]) {
//Provide seed to the rand-function later used in the particle class
srand((unsigned int)time(NULL));
//Create object from Swarm-class with default constructor
particlefire::Swarm swarm;
//Use the object to call the init from inherited Screen-class to initialize SDL-window. If it fails returns 1 from main function.
if (swarm.Init() == false) {
return 1;
}
//A boolean variable to test the game loop (while) below.
bool quit = false;
//"Game loop" for program so that the window stays open and called methods are executed until certain event,
//in case this program is closed by user via either esc-key or closing of the window.
//Game loop runs as long as quit is false.
while (!quit) {
//This function is used to get the number of milliseconds since the SDL library initialization.
Uint32 elapsed(SDL_GetTicks());
//Set color values of the particles
swarm.SetParticleColorValue(elapsed);
//Update position of particles
swarm.Update(elapsed);
//Apply blur effect to particles
swarm.ApplyBoxBlur();
//Update the screen to show particles
swarm.UpdateScreen();
//Check for events if user wants to quit
if (swarm.ProcessEvents() == false) {
quit = true;
}
//Set limit for how many times in a second this "game loop" can execute
swarm.LimitFPS(elapsed);
}
return 0;
}Purpose of the game loop is to keep the program running as long as it is necessary. Every game you play has a game loop, be it a simple tic-tac-toe played on a paper on pencil or multi million video game product that is sold to consumers and require some kind of device to be played on. For example tic-tac-toe game loop goes on as long as there are empty squares where player can insert their symbol (doesn’t matter if the game is a win or a draw because the game still ends), or if either of players win the game during their turn. Game loop can be also over, if one of the players decides to give up and not play the game anymore, and this is something that this particular game loop is all about. This Particle Fire Simulation program does not have winning / draw game over state like tic-tac-toe, so the point of game loop is to keep program running as long as end user decides to close it by selecting the close button on the window or pushing ESC-key on their keyboard. As can be seen, this game loop is implemented as a while loop, which runs as long as variable “quit” is false. Finally on this section I will address the srand-function call. C++ doesn’t have “true” random number generator, which means when using rand-function, one only gets pseudo-random number generated. Meaning that even that number are in random order they are always the same number in the same “random” order. In this program it would mean that every time user starts up the program particles would be in the same place and traverse the same axis without fail. To change this to more random srand-function can be used to provide a seed that changes the random numbers. However if the value would be something like “15742” the rand-function would work the same as before except the string of numbers would be different, meaning that something different is needed to input to function every time the program is run. Usual way to make numbers random enough is to provide the result of time-function to srand. Time provides number in seconds since 00:00 hours, Jan 1, 1970 so every time the program is run, new different seed is provided.
Before the game loop begins the object swarm is declared from Swarm class. Since class Swarm is derived from base Screen class it can access all the public and protected data members and methods of Screen. Swarm also has one private data member; an object derived from Particle class. Swarm is a class that brings together graphical window initialization and pixel access of Screen class, and the position and speed alternation setting and alternation of Particle class. It could be said that Screen is an empty canvas, while Particle is an painter that decided where it wants to position the brush and where to move it while Swarm makes sure that the everything happens according the vision of the painter.
When swarm-object is declared two constructors are also executed. First the base class constructor Screen::Screen() is called. This constructor is used to initialize Screen-classes data members m_window, m_renderer, m_texture, and m_buffer to NULL value and also calling ReadConfig() data method for setting screen resolution and framerate from a configuration file. Finally, after loading the screen resolution settings from a file, or if file is missing, using default values, constructor also sets m_buffer to point at to an array saved to heap memory with new-syntax. Arrays size is value in variable screen_width times value in variable screen_height so one element for each pixel on the screen. Second constructor is the constructor Swarm::Swarm() which is also used to call ReadSettings() data method for reading a another configuration file where user can set number of particles on the screen and how fast the color of particles change. Swarm constructor is also used to initialize two data members, m_particles-pointer and m_lastTime-variable, of Swarm class. Pointer is is initialized with an array of Particle-objects saved to heap memory with new-syntax. Size of an array is nparticles_ -variable that is either loaded from file or default value stored in the code. m_lastTime is initialized to value zero. New-keyword is needed here on both constructor to because both pointers are needed throughout the program so that they need larger scope than local, without needing to use global variables. It should be also noted when array of Particle objects is created each object calls constructor Particle::Particle(), which in turn calls Particle-class’s data method Init(). This method is used to determine each particle object’s fixed position on the middle of the screen. Along with direction and travelling speed randomly using following algorithms:
m_direction = (2 * M_PI * rand()) / RAND_MAX;//Direction in radians where 2 * PI is 360 degrees
m_speed = (0.0004 * rand()) / RAND_MAX;To understand the mathematics behind this check out Position and movement on concepts explanation pages. After constructors have been called the program moves to call init method of Screen class using swarm object. This method opens the video output, creates a window, renderer to creates context a for a window and finally texture that is put on that rendering context. Program also checks if initialization process of each is done correctly and creates an error message using SDL_Log-function. If we compare this to painter example above video output is the ability to draw itself, meaning that if there wasn’t even possibility to draw painter could not paint works of art, in the same way if SDL video output is not supported by the system (for example operating system environment where there is only text input/ouput) creating graphical windows is not possible. Window is the same as surface where the canvas is placed, like for example easel. Renderer is the canvas and texture is the type of color used to paint, for example oil or ink.
At the start of each run of the game loop, value from SDL_GetTicks-function is stored to variable elapsed. Value is the number of milliseconds since the SDL library initialization and because this is done almost at the beginning of the program basically it means how long the program has run so far, or simply the uptime of program. This data is useful in number of places of this program, for example making sure that the game loop runs only set amount of times per second and not as fast as possible, making the program allocate too much resources on displaying this one program. This is also known as limiting framerate and it you can read more about it here.
Another use for uptime comes from [SetParticleColorValue][swarm_setpcolorv] data method of Swarm-class. In this case the elapsed time is used to make color change consistent and not depended on the speed of the computer hardware. For example with color channel red algorithm:
unsigned char red((unsigned char)((1 + sin(elapsed * red_speed_)) * 128));For more information about the color changing algorithm of each channel, check out particle color entry on this wiki. Moving on the SetParticleColorValue data method and how each of the particle object on the screen is set to certain color. Inside the method for-loop is run which goes through each of the particle object and turns each object’s coordinate to position on the screen width(x-axis) and height(y-axis) pixel, again please refer to Position and movement for more detailed information how this is done. At the end of the for-loop Screen class’s data method SetPixel is called and color of each channel and pixel values of each particle are passed as parameters. SetPixel is rather simple data method, which could be summed up in one sentence; store each color channel’s value in to one variable and set corresponding pixel to that value. For more detailed information please consult particle color page.
Moving on the game loop next up is data method for updating every particle object’s position found from Swarm class called Update. This data method is used to call another data method from Particle class called UpdatePosition for each and every particle object of the program. Again, like previously, for-loop is used for the task of calling every particles own UpdatePosition method, and to ensure constant movement that is not depending on the speed of the hardware, variable interval is the passed along as a parameter too. UpdatePosition then handles the moving of every particles. Firstly by making the particles rotate by updating the direction of particle:
m_direction += interval * -0.0001;Calculating the position where particle will travel next on a biaxial-plane:
double xspeed = m_speed * cos(m_direction);
double yspeed = m_speed * sin(m_direction);And finally setting the new position according the speed.
m_xpos += xspeed * interval;
m_ypos += yspeed * interval;
Since every particle object has it own direction and speed stored in their data members after this data method is run each of the particle object is going to travel to varying locations with varying speed. Since all the particle objects are initialized in the middle at the beginning of the program, the first second seems that particles explode from the middle of the screen to outwards in all direction before setting up as a fiercely burning fire.
For finishing visualizing touch, a type of blurring effect is applied to make particles leave a vanishing trail behind them so that they don’t fill their travelling path with their color. Data method for this is found Screen class and it is known as ApplyBoxBlur. How it works is documented here. Now that all the calculations are done, positions changed and effects applied it is finally time to update the screen the reflect these changes by calling the UpdateScreen data method. This method updates the texture previously created on screen initialization. Before drawing the newly updated texture new pixel information needs to be loaded from buffer to texture and then the old texture erased from renderer. After all that is done finally new texture may be placed to renderer and the image on the screen is updated to highlight all the changes done in the previous data methods. All this happens via SDL-function calls SDL_UpdateTexture, SDL_RenderClear, SDL_RenderCopy and SDL_RenderPresent in this order.
We are at the end of the game loop here. First up is Screen class’s ProcessEvents data method which is used to call SDL_PollEvent, which job is to detect and examine events that are about to happen. If end user presses esc-key it sends event to queue and when the SDL_PollEvent is called it notices this key press and with simple if-statement decide what happens after that. In this case data method ProcessEvents returns false to main program, which in turn sets variable quit to true, thus exiting the game loop and shutting down the program. Same kind of returning of value false happens when users presses the close button on their window. When that happens SDL_Quit event gets send to event queue and when queue is processed by SDL_PollEvent an if-statement catches it and rest is the same as before. Final data method call is LimitFPS of the Screen class, which is used to limit how many times in a second the game loop can execute, which in graphical programs like this means how many times a second image is drawn on the screen also known as frames per seconds. This, like making sure that movement of particles is constant, is a way to make sure that program run at fixed speed and not as fast as computer hardware is able to perform. SDL-library provides function called SDL_Delay that waits a number of milliseconds that are sent to it as parameters. For better understanding how frame rate limiting works please check [this entry][limitfps] of this wiki.
So now you understand how one loop of the game loop is executed. This same loop is executed multiple times per second to ensure smooth transition between frames and make movement of particles look animated and not choppy.