-
Notifications
You must be signed in to change notification settings - Fork 2
Main.cpp and game loop
- Overview of game loop
- Swarm-object
- Setting particles colors and elapsed-variable
- Updating the particle objects
- After effects via blurring and updating the screen
- Checking for user input and limiting the performance
Here is the main.cpp-file 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 executeduntil 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 has a game loop, be it a simple tic-tac-toe 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. As an example tic-tac-toe game loop goes on as long as there are empty squares where player can insert their symbol or if one 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 program’s game loop is all about. This Particle Fire Simulation program does not have win or 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.
Before the game loop starts srand-function is called. 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 numbers 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 improve randomness, 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 from ctime library to srand as a seed. Time provides number in seconds since 00:00 hours, Jan 1, 1970, so every time the program is run, different seed is provided and only way to make two same number sequence is to start two instances of the program at the same time.
Before the game loop begins, an 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 position of the brush and where to move it and finally Swarm is the painter who makes sure that the everything happens according the vision and every line is drawn where it is needed.
When swarm-object is declared two constructors are also executed. First the base class Screen’s constructor 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 reading and setting screen resolution and framerate from a configuration file. Finally, after loading the values from a file, or if file is missing, using default values, constructor also sets m_buffer pointer 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 of an array for each pixel on the screen.
Second constructor is the Swarm’s which is also used to call ReadSettings data method for reading another configuration file where user can set number of particles on the screen and how fast the color of particles change. 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. Finally m_lastTime is initialized to value zero to that it can be compared to how long program has been running.
The New-keyword is needed here on each constructor because pointers to those locations are used throughout the program. Because of that they need larger scope than local and negates the need to use global variables. It should be also noted when array of Particle objects is created each object calls Particles’s constructor, 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 all those construction calls, the program moves to call init method of Screen class using swarm object. This method opens the video output, creates a window and 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. Continuing the painting 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 video output is not supported by the system (for instance operating systems 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, it basically 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 frame rate, which concept is fully explained on this wiki-page.
Another use for uptime is at SetParticleColorValue 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 color 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.
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. Ensuring 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 update 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 color applied to them. 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.
Almost at the end of the game loop. 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 user presses esc-key , event gets sent to queue and when the SDL_PollEvent is called, it notices this key press and with simple if-statement is can be decided 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. Final data method called 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 in a second image is drawn on the screen. This is 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 of this wiki.
If the user hasn’t stopped the execution of the program, this same loop is executed again. This same loop is executed multiple times per second, ensuring smooth transition between frames and make movement of particles look animated. If this would be a game, let’s say like basic Pong, there would also be some kind of data methods for checking if the ball would have hit the either of the paddles or if it would have exited on either side of the screen resulting a point to player, the game loop would be also be used to calculate these events and change behavior of the game accordingly.