lørdag 5. juli 2014

[ SDL2 - Part 9 ] No more delays!

Getting rid of SDL_Delay


Up until now, we've been regulating movement by just setting a delay of 16 milliseconds at the end of each frame like so : SDL_Delay( 16 )


This is bad because sometimes a frame takes a bit longer. In our code, there is so little going on, so the frames usually take less than on MS. So we've added a delay of 16 ms. But things may happen, and suddenly a frame takes 5 ms. What happens then? Well, your animations will jump. And if this happen regularly, your animation will be choppy. We don't want choppy animations, we want smooth animations.How do we achieve this?

Method 1 - our current solution


We've set an aim for 60 frames per second. Since there is 1000 millisconds ( ms ) in a second, each frame should take 1000 ms / 60 frames = 16 ms.

Since our code is very light, we've just assumed that each frame takes less than 1 ms, so we've added a delay.

Problem : what if one or more frames uses more than 1 ms to run? Our animations will get choppy and we don't want that

Method 2 - using delta


Another, better way we could limit framrate is to calculate delta time and use that to set the delay, of. Delta time is the time between each frame ( or update. ) If a frame takes 3 ms, delta will be 3 ms. In method 1 we had a 16 ms delay and assumed the frame took 0 ms to run. But if the frame took 3 ms, we "loose" 3 ms of the delay time and end up with a delay tie of 16ms - 3ms = 13 ms.

This is a valid solution, and your game will run relatively smoothly in 60 fps.

Method 3 - using delta, no delay


Method 1 and 2 has worked by limiting frame rate. But why limit it? We can just skip the delay altogether and just use the delta time to adjust animation. Most modern games works this way. Since we adjust movement with delta time, it doesn't matter if we run in 60 fps or 6000 fps. For smooth animation, this method is superior to the ones above. Waiting 16 ms between frame can make objects jump a tiny bit. Better to eliminate this delay at all.

It's also very simple, just multiply the movement with the delta time.

Implementation


Implementing frame independent movement is quite simple, but there are a few things you need to be aware of in order to get it right.

Delta time


Probably the most important point. In order to get animations right, the delta tie needs to be accurat. Very accurate, preferably with microsecond ( µs ), or nanoseconds ( ns )

There are various ways to do this.
  • The C way</li>
    • - Confusing ( functions takes a struct and fills it with time / date )
    • - Not always accurate
    • - Hard to use correctly
    • - Platform dependent ( no one way to do it in Linux and Winows ) 
    • + No library needed,
    • + Doesn't need C++11 support
  • Boost
    • - You'd need to download, and link with boost
    • - Adds and a dependency, just for time
    • + Cross-platform
    • + Doesn't need C++11 support
  • C++11
    • - Syntax can be hard to understnad
    • + Does everything you need easily
    • + Cross platform
    • + Well documented
Both boost and C++11 are good alternatives. In this tutorial, we'll cover the C++11 way. The main reason for this is that it means we don't have to install boost. And the C++11 way is well documented, though lacking of examples.

std::chrono


Chrono is the C++11 way for everything related to time and timing. It's quite large and complex, but I'll try to give a short explanation of how we get the delta tie using chrono. std::chrono::time_point is the basic C++11 time structure. It contains std::chrno::duration, which contains information about a point in time. More specific it contains a tick variable that contains the number of time units since 1.1.1970, and a std::ration which says what unit of time the ticks are in ( secnds, monutes, milliseconds, etc... )

This might be kinda confusing, but all you need to remember is that std::chrono::time_point represent a point in time. So let's implement our delta function. First of all, we're gonna make things a lot easier for ourselves by adding using namespace std::chrono;. This is not necessary, but it means we don't have to add std::chrono:: in front of everything.

So now we need to get the point in time, this can be done using high_resolution_clock::now(); which returns a std::chrono::time_point

time_point timeCurrent = high_resolution_clock::now();

This will be done at the beginning of our GetDelta() function. We also need a variable to store the previous point in time ( the last tie GetDelta() goy called. We'll set this at the end of our GetDelta() like this ( timePrev is just a time_point )

timePrev = timeCurrent;

Now that we have our two time_points, we can calculate the time between them ( the delta time. ) time_point supports arithmetic operations such as - and + so we can easily find the time difference between them by doing time_point timeDiff = ( timeCurrent - timePrev ).

auto timeDiff = timeCurrent - timePrev;

So now we have a third time_point that contains the difference between the two frames. This is essentially the delta time represented as a time_point. But there is anther problem : we have no idea what time unit the duration object uses. So to be certain we get the time as nanoseconds, we need to cast the duration inside the time_point to be using nanoseconds. To do this, we use a duration_cast<> we can simplify it a little to calculate timeDiff and cast it in the same line :

auto timeDiff = duration_cast< nanoseconds >( timeCurrent - timePrev );

To get the actual number of time unites, we use the count() member function :

double delta = duration_cast< nanoseconds >( timeDiff ).count();

And finally we have the delta. But since it's in nanoseconds, we need to convert it into seconds :
delta /= 1000000000;

There! It's a bit complicated with all the different variables types, but we now have a function for getting delta time. And it will work on any platform as long as it supports C++11, not extra libraries needed. Here is the finished function :


Using the delta time


Now that the worst part is over, it's time for some minor details on how to implement frame independent movement.

First of all, the animated objects needs to have speed. And for better precision, we use doubles. Let's make a simple struct for holding the speed. Our speed will have values between -1.0 and 1.0.
struct Speed
{
     double x;
     double y;
};
We also need an Update( double delta ) functions that's used to update the position of the object.

rect.x += speed.x * delta;
rect.y += speed.y * delta;

There's a few things wrong with this, though. First of all, delta values will be really small, and our speed is between-1.0, and 1.0 so the resulting movement will be really small. We can solve this by multiplying with a constant.

rect.x += speed.x * delta * 300;
rect.y += speed.y * delta * 300;

But there is a second issue here : both speed and delta are likely to do decimal numbers. So the result of the multiplication will almost always be a decimal number as well. But we just put it into an int and this means we'll loose the decimal precision. So if the result is 0.8, we'll end up adding 0 to x or y. And if the same things happen in the next frame, we've "lost" 1.6 worth of movement. A while pixel! It might seem like a small thing, but it would make the movement really choppy. So instead we introduce two new member variables to our Texture struct to hold the result of the multiplication. So here's our final Update function:

double x;
double y;

// .......

void Update( double delta )
{
     x += speed.x * delta * 100;
     y += speed.y * delta * 100;
  
     rect.x = x;
     rect.y = y;
}

Conclusion


That concludes the tutorial on delta timers. I've made a few updates, put things into different classes and other minor improvements. The code is too big to include in this blog post, but you can find a full zip of it here.

 I've added a new .cpp file, so you need to add that to the compilation string if you're using clang or gcc

clang++ main.cpp Texture.cpp -std=c++11 -lSDL2 -lSDL2_image -o Game

As always, feel free to post a comment or message me if you have a question.

onsdag 25. juni 2014

[ Data structures - Part 2 ] Stack

Stacks


Last time, we learnt about vectors. This time we're gonna learn about stacks. Stacks are extremely important in understanding how your code runs. hIt's used both for storing y our local variables and for making sure your functions run in the right order. Recursive method heavily rely on stacks.

The stack ADT


Stacks asr ad ADTs this basically says it's a data type with certain operations you can perform on it. But ADTs does not have any specific implementation, you can implement it any way you like.


Supported operations


The following operations are typical for stacks
  • Push
    • Pushes and element on the top of a stack
  • Pop
  • Top
    • Returns the top element of the stack.

An example


Imagine you have a stack of plates. The plates are all of the same plates, but some have different motives. It is your jobs to sort them into new stacks where depending on the motives of the plates. So you look at the top plate ( top() ) and decide which of the sorted stacks it belongs onto. When you have decided it belongs in stack 1 ( as an example ), you remove it from the stack ( pop() ) and put it onto stack 1 ( push() ). You look at the next one ( top() ) it belongs in stack 2 so you remove it ( pop() ) from the unsorted stack and place it on stack 2 ( push() ).

This goes on and on. Always looking ( top() ) and remove ( pop() ) the top element and placing it ( push() ) on the top of the correct stack. You can't look at or remove any of the elements below the top one, so you're always working with the top one. This is how a stack works. Always insert and remove at the top. The elements below the top are irrelevant until they are the top elements themselves.

Usage


Stacks have a lot of usage in the world of computers. Every time you write a function, you are dealing with stack. Every time you do an if or loop ( for, while ) you are dealing with a stack. So let's take a look at how that works.


void Function()
{
    int value = 5;
    int someValue = 19375;

    if ( value == 5 )
    {
        int otherValue = 3;]
    }

    while ( value !=10 )
    {
        int someValue = value;
        int something1 = 0;
        int something2 = 0;
       value = someValue + 1;
    }
}
Here's what happen when you enter Function().

Step 1:

The variable value and someValue will be added to the stack.

The stack is now
  • someValue
  • value
Step 2:

We enter a new block { so we add a mark to show this

The stack is now
  • {
  • someValue
  • value
Step 3:

We find a new variable, otherValue, and add it t the stack

The stack is now
  • otherValue
  • someValue
  • value
Step 4:

So now we are leaving the current block. The current block was the one that started in step 2.
So now we need to delete the variables we added in the current block ( they are now out of scope )

We do this by doing the following look at the top element :
  • The element is not a {
    • Pop it and go check all the next element.
  • If the element is a {
    • We have deleted ( popped ) all elements that belonged to this scope so we pop the { and return. Now we have deleted all the elements and can continue.
 So let's do this step by step
  • } - The end of the scope, pop it and continue
      • otherValue1 - A variable in the scope. Pop it and continue
    • { - The { signaling the beginning of the scope. Pop it and return
      • someValue
      • value
       In the end, our stack looks like this :
      • someValue
      • value

      Step 5:

      Now we have come to the loop in our function. Here we declare the variable someValue. Wait? Isn't that what the other variable is called? Yes, it is. But it's in a different scope so the compiler knows that when you someValue within this scope, you are referring to the variable declared in this scope. Let's add the { and someValue to the stack.

      This is what we end up with :
      • }
        • something2 
        • something1
        • someValue - The one in the loop, value is same as value
      • {
      • someValue - The original one, declared at the top. Its value is 19375
      • value

      Step 6:

      The loop goes out of scope, and someValue is deleted. Only to be recreated as we re-enter the loop. This time value is one more than last time.

      The order in which the items will be deleted is the opposite of the order they were inserted. So : something2, then something1 and finally someValue.

      Next we go to 5, value will be one more, but otherwise exactly the same will happen all the way until value is 10, in which case we continue to the last step.

      Step 7:

      Now the entire function is done. All that is left of the stack is

      • someValue
      • value
      So we pop of the top variable, someValue. Now we're left with the first variable we created, value. We pop that of too. Now the entire stack is empty and we return from the function.

      Conclusion


      Stacks are limited in what they can do. But they are also very easy to implement and understand. They are also extremely useful in some cases ( as we have just seen. )

      I hope to update this post soon with more information about the implementation of the object in STL, std::stack.



      Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.

      For a full list of my tutorials / posts, click here.

      fredag 20. juni 2014

      [ SDL2 - Part 8 ] It's TIME for another update!

      Introduction


      In the last tutorial we learnt how to load and render png files with alpha layers. This time we're gonna dive into rotating textures. Or more presicely ; rendering them rotated.

      Today e'll be making a simple, but working, analog clock.  This will show how to rotate textures in SDL2. We'll also improve how we represent textures

      SDL_RenderCopyEx


      Until now, we have been rendering using SDL_RencerCopy. But in order to rotate, we need a different function ;  int SDL_RenderCopyEx it takes a couple more arguments. I'll try to explain all of them.

      SDL_RenderCopyEx
      (
         SDL_Renderer* renderer,
         SDL_Texture* texture,
         const SDL_Rect* srcrect,
         const SDL_Rect* dstrect
         const double angle,
         const SDL_Point* center,
         const SDL_RendererFlip flip
      )

      • renderer - the SDL_Renderer we always use for rendering
      • texture  - the SDL_Texture we want to render
      • srcrect  - which part of the SDL_Texture to render. null for everything.
      • dstrect  - where to render it. Used exactly like in SDL_RenderFillRect)
      • angle     - angle of rotation
      • center    - the center of rotation
      • flip      - flipping of texture ( vertical, horizontal, no flip )
      Return value
      • 0 on success

      As you can see, the first four parameters are identical to SDL_RenderCopy you can read more about them in part 6.

      Texture flip


      Texture flip is represented as an SDL_TextureFlip

      It is simply an enum with the following three values :
      • SDL_FLIP_NONE              - don't flip at all
      • SDL_FLIP_HORIZONTAL   - flip horizontaly
      • SDL_FLIP_VERTICAL         - flip vertically

      For now, we will just be using SDL_FLIP_NONE.

      Center of rotation


      The center of rotation is given as a position seen from the top-left of the texture ( remember; in SDL 0,0 is the top-left of a texture. ) So a center of 0,0 will mean you rotate it from the top-left corner.

      Finding the center of rotation is usualy quite simple, it'll usually just be the center of the texture you are using. For a circle, like a spinning wheel, the center of rotation will simply be the center of the wheel. Or for a Tetris piece it will be the center of the retris piece. The center of a texture is easy to find. Since a center of any rectangle will be halfway along its x axis and halfway along its y axis, the position can be calculate as follows ;


      // The rect of the texure.
      // Assume it is filled out with pos(x, y) and size( w, h )
      SDL_Rect textureRect;
      SDL_Point center;

      // Calculate center pos by setting
      // ->x pos to half of witdh
      // ->y pos to half of height
      center.x = textureRect.w / 2;
      center.y = textureRect.h / 2;

      The type of center is one we haven't used before, SDL_Point. But an SDL_Point is simply just a struct with an x and y value.

      In our case we are going to make a working analog wall clock. So we can't rotate it around the middle ( that would be a weird clock! ) What we need to do, is find the base of the hands.


      Here is a picture of on our clock hands. The white hole in the middle is where we want the center of rotation to be. All three hands look very similar to this, all hands have a hole in the base, and all of them rotate around that point.

      If you look closely at the green dots, you'll see that the distance to the hole from either of the sides and the bottom is all the same ( three green dots. ) The green dots span the entire width of the base. So to find the center x value, we simply take half of the width. Looking at the picture, you'll see that the distance from the bottom to the hole is the same as the distance from the sides subtract half of the width to find the y position of the hole.

      So the code for finding the center of rotation will be something like this :


      // Assume textureRect is filled out with pos and size
      SDL_Rect textureRect;
      SDL_Point textureCenter;

      int halfWidth = textureRect.w / 2;
      textureCenter.x = halfWidth;

      // Center.y  = half of the width above texture bottom.
      textureCenter.y = textureRect.h - halfWidth;

      Putting things together


      Previously we have worked with simply storing the SDL_Texture and SDL_Rect as separate objects in our main.cpp. But now we also need to store the center of rotation and the angle. So that's four variables for each texture. With so many variables, it's a good idea to split it into a separate struct :



      We also need to change main to make the clock work



      As you can see, there is very little code needed in main since a lot of it is moved into Texture.h


      Conclusion


      The full code can be downloaded here.


      Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.

      For a full list of my tutorials / posts, click here.

      onsdag 18. juni 2014

      [Data structures - Part 1] Vector

      Introduction


      In this series I will be talking about the major data structures, what operations they support and how to implement it. It is intended mainly as a way for me to learn these things better, but I also hope it will be of use to someone.

      At the same time I will try to describe the functionality of the various classes in the STL.

      Let's start with one of the most basic one, vector.

      ADTs


      Vectors are ADTs ( Abstract Data Type ) This means it's not a concrete data type like ints, pointers, arrays, etc... An ADT is just a data structure with no specific implementation. It could be implemented as an array, but it could also be implemented as a linked list ( I'll write a post on this later. In simplicity, it's just a container where each element has a pointer to the next one. )

      An ADT has a specific set of operations that can be performed on it. On a vector, you can add an item to the back( push ), remove the element in the back ( pop ) or read a random element. In most implementations, vectors are implemented as an array because it's way simpler and more efficient. But you could implement it as a linked list if you wanted to.

      The Vector ADT


      Vectors are one of the most important structures. According to Bjarne Stroustrup
      If you know vector and int, you know C++

      While this is not entirely true, vectors are very useful, and very fast.
      Vector is an array-like container. It's very useful for storing data, and it's particularly focused on adding/removing elements from the back.

      Vectors are focused around adding and removing elements to the end of the vector. But it also supports random access, so it will work as a regular array in many cases.

      Vector is a good choice if you need to store a list of object and you intend to add items as you go along. As long as you add/delete at the end, vectors are very fast. Adding and removing at the end is O( 1 )

      Supported operations


      The following functions are typical to a vector:
      • Push Back
        • Insert at the end
      • Pop
        • Remove last element
      • Back
        • Access last element
      • operator[ ]
        • Access a specified element


      Implementation


      The random access functionality of a vector is why it's usually implemented using an array. And when using an array, the items are laid out consecutively in memory. This means that, if element 0 is at pos x, then element 1 is at position x + size, where size is the size of one element. And the CPU works VERY efficiently when the memory is laid out like this. And that is one of the major reasons it's so widely used.

      And this is one of vectors shortcomings. Because when extending a vector, you usually need to move all elements to a different place in memory. And since your vector can potentially hold millions of elements, this might take some time.

      Luckily though, this is one of the areas the CPU excels. It's incredible effective when it comes to working on consecutive blocks of memory. So in a lot of cases, the overhead of increasing the capacity of a vector is not so bad.

      Data members


      The following are the data members a standard vector needs to have. Note: these are intended to show the basic member of a vector. The members below are not the same as the ones in std::vector, they're just intended used as an example.

      Type[] data


      The array that holds all the data. It contains all elements in the vector. The memory for the object is dynamically allocated with new, since a vector needs to be able to grow.

      unsigned int size


      The number of elements in the vector. Note: this is not the same as capacity of the vector. The size of a vector is just how many elements are in the vector right now. It is equivalent to the length of a string.

      unsigned int capacity


      How many items the vector can currently store. This number will always be as large, or larger, than the size of a vector.

      Size vs capacity


      Every time you insert an item, the vector needs to check whether it is equal to the capacity of the vector. If it is, the vector is full and the capacity needs to be increased. This also means new space have to be allocated and the elements has to be moved. See reserve further down for details.

      Inserting


      When it comes to insertions, there are a few cases to consider

      Inserting at the end ( index = size of vector )


      Say you have a vector of 11 elements. The first element is at index 0, the las one at index 10. Inserting an element to the back ( index 11 ) means you have to do the following:
      1. Insert the item at the next empty position ( 11 )
      2. Increase the size by 1
      Done!

      Inserting in a differend position


      Now let's assume you have the same vector as before ( 11 elements ) and you want to add an element at index 5 :
      1. Take all elements from index 5 to 10 ( the last element ) and move them back one position ( increasing index by 1 ). The last item will now be at index 11 and the position 5 will be empty
      2. Now you can insert the item into position 5
      3. Increase the size  by one
      The function might have to move a lot of elements. But this isn't a huge worry since CPU's work very efficiently on consecutive blocks of memory like arrays and vectors.

      Functions in std::vector


      These are all the supported functions of std::vector you find in the C++ STL ( Standard Template Library. ) This section does not contain the algorithms like std::find(), I will cover these later.

      Push Back


      The basic insertion algorithm, it adds an element to the end of the vector. It will also increment the size by one. If the current capacity of the vector is too small to fit this element, the capacity will be extended. See the point Reserve( int ) for more info.

      Reserve


      This function increases the capacity of the vector. The function will do the following :
      1. Allocate a new, larger, chunk of memory.
      2. Copy all elements to this chunk of memory
      3. Deallocate the old chunk of memory

      If you try to reserve more than the theoretical limit of the vector ( see Max Size ), and exception of the type std::length_error will be thrown.

      A few notes :
      • The function might have to move a lot of elements.
        • In most cases this isn't a huge concert since CPU's are very efficient at this.
      • If you are constantly inserting a lot of elements, this function might get called automatically a lot. 
        • To prevent this, you can call Reserve() with a large value so that the size never grows beyond the capacity so that the capacity won't have to be extended.
      • You can't use this function to shrink the capacity.
        • If you call this function with a value smaller than the current capacity nothing will happen.
        • You can use ShrinkToFit() to decrease the capacity ( see below. )

      Shrink To Fit


      Will reduce memory usage by freeing unused memory. Since it deallocates all unused memory ( all elements after size - 1 ), capacity will be set to the same as size. If you try to PushBack or Insert an item after calling this method, Reserve() will be called.

      Back


      Simply returns the last element.

      Front


      Simply returns the first element.

      operator [int]


      Returns the element specified the brackets[]. Does NOT perform any bounds checking. If you specify a number below 0, or equal to or more than size. The function returns a reference, so this function can be used to change the element in the vector.

      At( int )


      Just like the [], this returns the elements specified in the function argument. But unlike the [] operator this function performs bounds checking. If you specify an index out of bonds, an exception of the type std::out_of_range is thrown.Just like [], function returns a reference, so this function can be used to change the element in the vector.

      So why use the brackets[]?


      While at() is technically safer, there is also a little overhead in performing the bounds checking. [] can potentially be run in just in one clock cycle.

      My general rule of thumb is to use [] in your code. If you check and test your code properly, stepping out of bounds should not be a risk.

      Empty


      Returns true if the vector is empty, otherwise false.

      Size


      Returns the number of elements currently in the vector.

      Capacity


      The number of items the vector has allocated space for. If this is equal to the sixe, the vector needs to be expanded ( see Reserve() above. )

      Data


      Returns the underlying data of the vector.

      Max Size


      Returns the the maximum theoretical capacity of the vector, meaning the maximum number of elements the vector can hold using all avaialbe memory.Note : this is not the capacity of the vector.

      When size reaches capacity


      So when the size reaches the same value as capacity, the capacity of the vector needs to be extended. Usually this is checked whenever an element is inserted and the function Reserve( int ) will be called.


      Illustration


      Here is a simple illustration of a vector.













      It demonstrates insert ( both at the back and any other position, ) and remove. It also shows back element ( last element in the vector, ) size ( how many items that are in the vector ) and capacity ( how many items is there room for. )

      This code is meant as a base for later parts where I will show various sorting algorithms. You can find the code here.


      =========================

      A container we'll look at later, linked list solves the problem of capacity limitations.



      Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.

      For a full list of my tutorials / posts, click here.

      lørdag 14. juni 2014

      [ SDL 2 - Part 7 ] Using PNG files

      Using PNG


      In the previous post we learned how to load .bmp files. Which is not optimal. The .bmp format means the files grow large and there is no alpha layer ( no transparency. ) Which means you end up with ugly single-colored edges instead of transparency

      Original with .bmp images


      SDL2_image


      Up until now, we've only been using the standard SDL2.0. But the standard SDL2.0 doesn't support loading of .png files. But fortunately, there is a second SDL library that does this. And since it's still SDL, it will work perfectly with or other SDL code!

      Installing is almost exactly the same as installing SDL2 itself; you just follow the instructions in part 1 of my tutorial. But instead of installing SDL2, you install SDL2_image.

      New version with png files.
      Note: I apologize for vague and not very thorough descriptions in the below points. I will update them when I have access to Windows and Mac. I also apologize for the horrible background and sprites.

      Linux


      For Linux you can use need to install -lSDL2_image- or -libSDL2_image- ( the actual name might be different in different distributions. )

      The linker flag is -lSDL2_image

      The process is more or less identical to that of setting up SDL2 base. For more information, see my blog post about setting up SDL2.

      Windows


      Similar to setting up SDL2 base.

      The difference is that you have to download the development files for SDL2_image

      And similarly add SDL2_image.lib to library includes and add SDL2_image.lib to the library flags ( where you previusly added SDL2.lb )

      And with that, it should work.

      Mac


      See the first part of my tutorial. Just install SDL2_image instead of SDL2

      Again; I apologize for my poor description on this. I don't run Mac and have no experience with it. I hope my short descriptions is of any hope, though.

      Loading PNGs using SDL2_image


      In order to use SDL2_image, you need to add the header file, like so :

      #include

      The actual loading of .png files is just as easy as loading .bmp files. But since it uses an extension library, we have to use a different function :

      SDL_Texture* IMG_LoadTexture
      (
           SDL_Renderer* renderer,
           const char *file
      );


      This function will load a file and return it as a SDL_Texture*. This means we don't have to convert it, so we can just do:



      Note that this function, unlike the one for loading bmps, needs a pointer to the SDL_Renderer this is because it returns an SDL_Texture SDL_Textures are hardware optimized, and SDL2 needs the SDL_RendererIMG_LoadTexture() can handle several types of images. And because I'm lazy and didn't change the .bmp files that don't need transparency.

      Running it


      Just like last time, I'm not gonna show the entire code as one long gist ( too much code, and there are images too. )

      Here is a link to the code in .zip form

      Linux / Max


      If you are compiling using the compiler, you have to add -lSDL2_image to the compile string like so :
      clang++ main.cpp -std=c++11 -o Game -lSDL2 -lSDL2_image

      If you want to run it, you simply do
      ./Game

      Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.

      For a full list of my tutorials / posts, click here.

      tirsdag 6. mai 2014

      [ SDL2 - Part 6 ] Loading textures

      Loading textures


      In this tutorial you will learn how to load images ( just bmp for now, the next part wil cover .png ). First of all, a little info about the two new structs we'll be using

      SDL_Surface


      This is part of the old SDL ( < 2.0. ) It was used for just about everything related to rendering.

      SDL_Surface is used for holding the raw texture data. Basially it says what the color for every pixels is. Sort of like this :
      • pixel( 0, 0 ) = 128, 64, 255, 255 ( 128 red, 64 green, 255 blue, 255 alpha )
      • pixel( 1, 0 ) = 255, 32, 128, 128 ( 255 red, 32 green, 128 blue, 128 alpha )
      ..etc

      ( This  might not be 100% true for all cases, but it's generally how SDL_Surface stores the pixels. )

      The SDL_Surface structure also holds other data about the texture.
      • width/height ( these are read-only )
      • the pixel format, stores as an SDL_PixelFormat
      • clip_rec, a SDL_Rect used for bliting ( similar to rendering )
      d
      These values are useful to know about if you want to create your own SDL_Surfaces using code or want to use the old form of rendering ( bliting ). I do, however, not recommend learning about it. Bliting is less intuitive and it uses only software rendering. This basically means that the CPU will be doing the rendering, somthing CPUs are really bad at. We want optimized hardware rendering on the GPU. And that's where the second struct comes in.

      SDL_Texture


      Like SDL_Surface, SDL_Texture holds texture data. But unlike SDL_Surface, SDL_Texture holds an optimized, driver specific representation of the pixel data. This means rendering will be executed on the GPU, where it belongs. And THAT means it is really, really fast!

      So what's the point of SDL_Textures?

      Well, as I said, the pixel data in SDL_Texture is optimized. And what's more, it can be very different depending on your driver ( nVidia vs ATI. ) Basically this means that you can't use the pixel data for anything other than rendering. A second reason will come up when we dive into another new part of SDL, namely image loading.

      Loading images


      Loading a BMP image in SDL is very easy. It can be done using the following function
      SDL_Surface* SDL_LoadBMP
      (
          const char* file
      )
      Parameters
      • file - the file we want to load
      Return value
      • SDL_Surface* - the loaded image as a SDL_Surface. null if failed. 

      But we're not gonna use the SDL_Surface*, we want a SDL_Texture* instead. To get it, we need to use a second function :
      SDL_Texture* SDL_CreateTextureFromSurface
      (
         SDL_Renderer* renderer,
         SDL_Surface* surface
      )
      Parameters
      • renderer - our SDL_Renderer
      • surface - the surface we want to make a SDL_Texture from
      Return value
      • SDL_Texture* - The resulting SDL_Texture. null if failed. 

      Since we won't be using the SDL_Surface, we can delete it and free the memory :
      void SDL_FreeSurface
      (
         SDL_Surface* surface
      )

      Parameters
      • surface - the SDL_Surface to delete
      Return value
      • none

      Now that all of this is in place, we can wrap it all up in a nice little function :



      Getting the images on screen



      Now that we have loaded the textures, we need to render then on screen. That means no more SDL_RenderFillRect() and THAT means no more rectangles and squares. Finally! To render them, we just need a simple function :


      int SDL_RenderCopy
      (
         SDL_Renderer* renderer,
         SDL_Texture* texture,
         const SDL_Rect* srcrect,
         const SDL_Rect* dstrect
      )


      Parameters
      • renderer - the SDL_Renderer we always use for rendering
      • texture - the SDL_Texture we want to render
      • srcrect - which part of the SDL_Texture to render. null for everything.
      • dstrect - where to render it. Used exactly like in SDL_RenderFillRect)
      Return value
      • 0 on success

      Even though this function has a few parameters, it's really quite simple. We can use null for srcrect and the SDL_Rects we used for SDL_RenderFillRect() as dstrect. And then we can remove the SDL_SetRenderDrawColor and end up with a LOT shorter Render() function.



      Putting it all together


      I'm not gonna put the entire code on the blog anymore since this creates a whole wall of text to scroll through. And now there is added images too. But you can download the images and the source code here.



      Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.

      For a full list of my tutorials / posts, click here.

      tirsdag 29. april 2014

      [ SDL2 - Part 5 ] Collision detection and our first game!

      Collision detection


      Collision detection is one of the key aspects of game programming. For all non-rectangular objects it gets harder and harder the more accurate you want it to be. But today we're gonna continue just using rectangles. This part describes a simple algorithm to check for intersections.


      After we've created a functions that does rectangle-rectangle collision detections, we'll use all of what we've learned so far and make a simple game!

      Rectangle - rectangle collisions


      Instead of checking if two rectangles are inside or touching each other, we're gonna check for the opposite; if they are outside of each other. The algorithm to do so consists of two steps.

      1. Find the x coordinate of the left and right and the y coordinate for top and bottom.


        Say we have two rectangles, rect1, and rect2.
        SDL_Rect rect1;
        SDL_Rect rect2;
        Finding the x/y of left/right/top/bottom is very easy.
        int left1 = rect1.x;
        int right1 = rect1.x + rect2.w;
        int top1 = rect1.y;
        int bottom1 = rect1.y + rect2.h;
        The process is exactly the same for the second rect.

      2. Use the 8 edges to check if they are not colliding


        Take a look at the below drawing

        As you can see, the red rectangle is farther to the right than the blue one. But how do can we easily check that? Simply by checking if redLeft is further to the right than blueRight. If it is ( like it is in our case ) there is no collision. Then it's just a simple matter of repeating it for the other edges. So we end up with something like this :

        SDL_Rect rect1;
        SDL_Rect rect2;

        // Find edges of rect1
        int left1 = rect1.x;
        int right1 = rect1.x + rect1.w;
        int top1 = rect1.y;
        int bottom1 = rect1.y + rect1.h;

        // Find edges of rect2
        int left2 = rect2.x;
        int right2 = rect2.x + rect2.w;
        int top2 = rect2.y;
        int bottom2 = rect2.y + rect2.h;

        // Check edges
        if ( left1 > right2 )// Left 1 is right of right 2
            return false; // No collision

        if ( right1 < left2 ) // Right 1 is left of left 2
            return false; // No collision

        if ( top1 > bottom2 ) // Top 1 is below bottom 2
            return false; // No collision

        if ( bottom1 < top2 ) // Bottom 1 is above top 2
            return false; // No collision

        // None of the above test were true, collision! return true;

      The resulting function


      So here's the final function for you to copy and test out.

      If you want to shorten the code, you can remove the comments and replace the variables with the rect x / y / w / h values like so :

      if ( rect1.x > ( rect2.x + rect2.w ) )// Left 1 is right of right 2
          return false; // No collision

      I chose to not do this as the longer version is a bit easier to read and understand.

      Our first game!


      Our first game is of the "lead the chicken safely across the road" kind of things. Or, as in our case, "lead the square from the rectangle, past all the other square and to the other rectangle." You control the square with the arrow keys. If you hit a red square, you'll end up where you started. If you make it to the other side, you'll also end up where you started, but at least you have that tiny sense of victory for helping our poor little square!


      The code just uses the different things we've learned so far, so I won't explain it other than in code comments and ( hopefully ) descriptive names.

      So without further ado, here's the code :




      Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.

      For a full list of my tutorials / posts, click here.