Saturday, June 28, 2014

Making of a Simple Roguelike - Part 2 Screens

Untitled Document

Wherein I Admit to My Failure

   First of my failures is not getting another post up. Sorry about that as I just started my internship and it is a bit of a sharp turn about for my mind. Anyway my lack of posting is only partly having to learn another language in only a couple days. Honestly at this point I have the basics so its only learning the specific keywords for it.

   The big problem and failure on my part is not being able to pick something and stick with it. I have in my spare time coded and recoded my method of storing the three default screens. There is no way to get around the fact that it is just a few giant blobs of characters. So yeah, at the point where I am typing this I am going to stick with just hardcoding them in.

Now for the Screens Themselves

   The game screen where you will spend most of your time is important and thus I will begin with it. It was the easiest and probably hardest screen to design. Even now the design isn't complete. I was doing some tests on my map gen and figured out that a short map made with it feels best for this. This means that I actually ended up with too much room.


 ┌----------------------------------------------------┐ ┌---------------------┐ 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 |####################################################| |                     | 
 └----------------------------------------------------┘ |                     | 
                                                        |                     | 
 ┌------------------------------------------------------┘                     | 
 |                                                                            | 
 | HP:05                            a                                         | 
 |                                                                            | 
 | MoveRate: 5                      b                                         | 
 |                                                                            | 
 | DR Chance:                    c                                         | 
 |                                                                            | 
 | Block Chance:                 d                                         | 
 |                                                                            | 
 | Dodge Chance:                 e                                         | 
 |                                                                            | 
 └----------------------------------------------------------------------------┘

   The field of walls is actually the size of the map. It makes for a compact and relatively lineir experaince when I must admit my map gen wallows in dead ends and confusion normally. Letters are where equipment will be listed as I want it to be descriptive since there are only five spots. Now I just have to figure out what to do with the side. This doesn't matter though and it could just end up blank so onwards.

Start up and what does the screen you see look like?


 ┌----------------------------------------------------------------------------┐ 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 |   Welcome to a challenge. If you are                                       | 
 |   here it means you want to join the             ▓     ▓▓▓▓  ▓▓▓▓  ▓▓▓     | 
 |   Adventurers guild but not just                 ▓     ▓  ▓  ▓  ▓  ▓  ▓    | 
 |   anyone can do that. You will need              ▓     ▓  ▓  ▓▓▓▓  ▓  ▓    | 
 |   your wits about you for this. I                ▓     ▓  ▓  ▓  ▓  ▓  ▓    | 
 |   have crafted a dungeon to test                 ▓▓▓▓  ▓▓▓▓  ▓  ▓  ▓▓▓     | 
 |   your mind more than your brawn we                                        | 
 |   can make you stronger but the mind                                       | 
 |   is a bit harder to work with.                                            | 
 |   Anyway if your sure you want to                                          | 
 |   take the challenge sign here                   ▓   ▓  ▓▓▓▓  ▓       ▓    | 
 |   here and here. Okay proceed do                 ▓▓  ▓  ▓     ▓   ▓   ▓    | 
 |   the stairs and remember   Your                 ▓ ▓ ▓  ▓▓    ▓   ▓   ▓    | 
 |   death isn't our fault and be                   ▓  ▓▓  ▓      ▓ ▓ ▓ ▓     | 
 |   careful as there are rumors of                 ▓   ▓  ▓▓▓▓    ▓   ▓      | 
 |   some breaks in the magic and more                                        | 
 |   powerful monsters showing up.                                            | 
 |                                                                            | 
 |                                                                            | 
 |                                                   ▓▓▓    ▓  ▓  ▓▓▓  ▓▓▓    | 
 |                                                  ▓   ▓   ▓  ▓   ▓    ▓     | 
 |                                                  ▓   ▓   ▓  ▓   ▓    ▓     | 
 |                                                  ▓   ▓   ▓  ▓   ▓    ▓     | 
 |                                                   ▓▓▓ ▓   ▓▓   ▓▓▓   ▓     | 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 |                                                                            | 
 └----------------------------------------------------------------------------┘

   Once again I am dealing with too much space. Anyway it doesn't look too bad. In game there will be a frame around whichever option is selected at the moment. But all that aside we now have all the screens we need. Yes there is a pause screen but I am going to take a shortcut with that. Libtcod can make a frame which overwrites what it is drawn over so I will simply be drawing that and then the options.

   With that out of the way though I now have to put it into the code. I am thinking a simple class that just has getStartScreen and getGameScreen which takes the argument of a char**. It will actually be somewhat easier then it could be because of something I learned recently. Techincally it is something I should have known already but sometimes you just need stuff pointed out to you. The important thing I learned was that in notepad++ you can search and replace with regular expressions. I knew this but it took someone else pointing out that it means I can search for (.) and replace \1',' which will put that between each character.

   The problem that remains is caused by how I store the map I would have to rotate it because I go map[width][height]. I do this because it means when I am doing stuff in the code any array dealing with the map can be accessed with x,y. The problem in the past that I had was I would do it the other way and then get it all mixed up. So now I could just do the work, on the other hand I think just making a couple of private arrays going the other way will work fine. Once I put that down in code it seemed to work so I should be done with this.

Saturday, June 7, 2014

Making of a Simple Roguelike - Part 1 Setting Up

Untitled Document

So Libtcod

   As I noted in the planning post and in the heading just above here, I am going to use Libtcod for my roguelike. If you don't know of Libtcod it is a free API for roguelike development and provides many useful things like advanced true color console and input. Now that we have that out of the way we need a project to work on and add it too. I start with a blank project called JoiningTheAdventurersGuild but if your following along you can call yours whatever you want.

   To get Libtcod up and running in our project we need to grab it first. I am using the 1.5.2 Windows version to do this. In it there are a number of files but the ones we need to stick in our project folder are as follows:

  • libtcod-mingw.dll
  • libtcod-mingw-debug.dll
  • SDL.dll
  • terminal.png

   The png is very important as without it your program won't run. I had a good bit of trouble when I was working with the C# binding of libtcod as it didn't have it include at the time for some reason. The next step is to make sure the project knows what to do with those files so into the build options. Under debug in linker settings add a link to the libtcod\lib\libtcod-mingw-debug.a and then switch over to release and link to libtcod\lib\libtcod-mingw.a. Of course doing all this wont put anything on the screen and this isn't quite the point to go into how to use libtcod so I will just note that I copied the main.cpp from my default setup of libtcod to test it. If you want to take a look at it the first commit for the project on github has the code. With that I built and subsequently ran the project and everything worked. Now if you had problems there are some really good tutorials so take a look around or you can ask on the Libtcod forum. My suggestion for a C++ roguelike tutorial using Libtcod is here at Code::Umbra

Foundations

   Now to get some basic things hashed out. I looked into various ways of managing the game state and have decided I don't want any of the more complex stuff. There will be a start/home screen, the in-game stuff, and a menu for saving and quitting. They all stack one right on top of another and I will be dealing with this using some functions each with a while loop in it. Mind you the proper way would be to make an actual game state manager but that can be put in later (If you haven't been programming for long this is like saying I will clean my room tomorrow. You almost never go back and do stuff you put off if what you have works). Anyway I want to do some coding so I am going to try and mock up what I want with this and put what I end up with below.

#include "libtcod.hpp"
int MainScreen() {
    bool done = false;
    while (!done) {
        handleInput();
        //MainScreen Logic
        int gameoutput;
        if (gamestart) {
            gameoutput = GameScreen(randseed);
        }
        if (gameload) {
            gameoutput = GameScreen(levelseed);
        }
        if (gameoutput > 0) {
            setLoadSlot(gameoutput);
        }
        if (quit || gameoutput == -1) {
            done = true;
        }
        drawScreen(&mainscreen);
    }
    return 1;
}

int GameScreen(int seed) {
    bool ingame = true;
    int gameoutput = 0;
    while (ingame) {
        handleInput();
        //GameScreen Logic
        int pauseoutput
        if (paused) {
            pauseoutput = PauseScreen(&gamescreen);
        }
        if (pauseoutput == 1) {
            gameoutput = level;
            ingame = false;
        }
        if (gameover || pauseoutput == -1) {
            gameoutput = -1;
            ingame = false;
        }
        drawScreen(&gamescreen);
    }
    return gameoutput;
}

int PauseScreen(const /*some map or custom struct array*/ &gamescreen) {
    bool paused = true;
    int pauseoutput = 0;
    while (paused) {
        handleInput();
        //PauseScreen Logic
        if (quitgame) {
            pauseoutput = -1;
            paused = false;
        }
        if (savegame) {
            pauseoutput = 1;
            paused = false;
        }
        drawScreen(&pausescreen);
    }
    return pauseoutput;
}

int main() {
    TCODConsole::initRoot(80,50,"Joining The Adventurers Guild",false);
    return MainScreen();
}

   The above code wont work but it should show what I intend. I am basically stacking the states one on top of another with the only complicated bit being going to the pause screen. What is happening there is that I want the game screen to be in the background and maybe grayed out so I pass the current game screen to it. It is being passed as a const because I don't want it to be changed and this enforces that. Anyway this was the easy setup bit, all the fun and hard stuff goes in those comments mentioning logic and possibly whatever happens in handleInput.

   Speaking of handleInput I have figured out how I want to do it. Really when it comes down to it there is two control schemes only. A menu control scheme and the ingame control scheme. Since two of three are the menu version I can use a bool as the argument and the menu version will be true. Also lets rename it to getInput as I have decided to make it just return an int depending on what key is pressed and results that don't do something returning -1. The actual function code will be a simple switch statement. Code for both the change to the call and the implementation are just below.

int inputcode = getInput(true);
int getInput(bool menuscheme) {
    TCOD_key_t key = TCODConsole::checkForKeypress();
    if (menuscheme){
        if (key.vk == TCODK_ENTER || key.vk == TCODK_KPENTER) {
            return 0;
        }
        else if (key.vk == TCODK_ESCAPE || TCODK_BACKSPACE) {
            return 1;
        }
        else if (key.vk == TCODK_DOWN || key.vk == TCODK_KP2) {
            return 2;
        }
        else if (key.vk == TCODK_UP || key.vk == TCODK_KP8) {
            return 3;
        }
        else {
            return -1;
        }
    }
    else {
        switch(key.vk) {
            case TCODK_ESCAPE:
                return 0;
                break;
            case TCODK_CHAR:
                switch (key.c) {
                    default:
                        return -1;
                        break;
                }
                break;
            default:
                return -1;
                break;
        }
    }
}

   Do note that I have only filled out the menu keys. I only put how to deal with characters and the escape key in the game keys as I wanted to show how to do it. With that in place I now need to figure out how I will handle what to draw. It could be easy if I only wanted to use characters and everything was the same color. That is not the case though as I want colored enemies. This means a struct which contains the character and the color as follows:

struct Tile {
    char Symbol = ' ';
    TCODColor foreColor = TCODColor.lightestGrey;
    TCODColor backColor = TCODColor(15,15,15);
};

   Take note of the default backColor. While not important programmatically it is for the color scheme as a pure black can be too severe a contrast. Now to decide how the screen data is stored. Because the screen is always the same size I am thinking a simple 2d array of Tiles. With this decided I can write the drawScreen function.

void drawScreen(const Tile screen[][WINDOW_HEIGHT]) {
    TCODConsole::root->clear();
    for (int column = 0; column < WINDOW_HEIGHT; ++column){
        for (int row = 0; row < WINDOW_WIDTH; ++row){
            TCODConsole::root->putCharEx(row, column, screen[row][column].Symbol, screen[row][column].foreColor, screen[row][column].backColor);
        }
    }
    TCODConsole::flush();
}

   And that along with a couple of updates to the code all the remaining errors end up being about things not existing. More importantly those things don't exist because they are to be implemented in the logic. I now have most of the framework needed to get it running in some form. About all I can see needing to have the basic start up screen working is said start up screen. That will require what ends up being me just hardcoding the menu screens.

Small Addon

   As a bit of a fun in addition to the above I will layout how I plan to make dogs act. They will be the new monster on the 4th dungeon level and represented by a brown 'd'. Anyway I figured out a simple state machine setup for them that will make them hunt in a pack or so I hope. A dog will have three states depending on whether it can see other dogs or the player. The one they start in is alone and would be something like the following:

If see_player
   move_away
Else If saw_player
   reverse_of_last_move
Else
   rand_walk
If see_dog
   leave alone enter inpack

Next as you might intuit from the above will be the state of inpack

If see_player
   leave inpack enter hunting
If see_hunting_dog
   move_closer
else
   If distance_to_nearest_dog < 3
      move_away
   If !_see_dog
      leave inpack enter alone
   If distance_to_nearest_dog > 6
      move_closer
   else
      rand_walk

Finally of course is the state called hunting

If next_to_player
   attack_player
Else If see_player
   If other_dog_closer_to_player
      move_closer
   Else
      keep_distance_but_move
If saw_player
   move_towards_player
Else
   leave hunting enter alone

   And with that I hope to make an interesting pack behavior. If the dog is alone just wander or avoid the player but only just. When he finds another dog they will try to stay together but not too close. Finally if a dog that is currently in a pack sees the player other dogs that can see it will go to it. All dogs that can see the player will try to be just as close to the player as any other dog. In the end no dog will actually try to attack the player directly but if the player approaches a dog they will all get closer. I am basically trying to get the dogs to stay on the edge until the player either purposefully approaches them or ends up having to do so.

   Really it will be an interesting choice for the player. Do you deal with the dog now even though it will run away till it has a friend? How many do you let follow you before it is too many? And finally now that you let them follow you so far and you see a dog at the other end of the room how will you survive?

At least that is my hope for them

Wednesday, May 28, 2014

Making of a Simple Roguelike - Part 0 The Designing

Untitled Document

Design It First

   I have been on the cusp of making a roguelike for a long time now. As of Late I have mostly been stuck at redesigning my map generator over and over. My most complete attempt so far was a couple years ago for a 7drl and it failed because of early design choices. This basically means I could have avoided it if I had planned ahead and so this time around I will do so.

   To start let's layout what the game will be like overall. I want a simple twelve level dungeon with only a dash of flavor. Each level introduces a new enemy type with a few types from previous levels thrown in. There will be a "boss" on each level but you are not required to fight them ever. The combat will be low hp and probably the most complex bit because of time management. With the high level view layed out lets take a closer look at each bit.

   The number of levels was chosen at random. I actually had originally planned for 26 levels with each one devoted to a letter. That was a bit overboard and I decided to limit it for this. Each of the levels will be generated with my simple map gen so that is actually taken care of though I will probably go over that at a future date. As for the story there won't be much of it. Something really short about wanting to join the adventurers guild and this being the entrance exam. Probably will fit it on the start screen.

   As for the enemies as I stated each level will introduce a new type. The original plan was for me to use the whole alphabet but my requirement for enemies made that not very practical. What I required was each enemy type being in some way unique. There is actually a little notebook I keep on me and it has a lot of notes in it. What my general consensus over a few months of design in that notebook is that 26 is too many for something as simple as I want. So yeah, with only 12 things I need to design this should be a lot easier for me to handle.

   Having a boss on each level was originally going to be a secret feature. Since I am posting about it that is no longer the case so the second purpose now will take the forefront. Basically the plan for the bosses is that they only show up if you defeat all enemies on a level. They also are the only form of advancement of your character besides hp. If you defeat a boss then when you go down the stairs you will be given an option of 3 power ups to choose from. The catch with the bosses is that they break the rules. Of course you don't have to fight them as the only goal on a level is to proceed to the next.

   The interesting bit where I will be dabbling the most with is combat. I want a bit more positioning and tactics without resorting quite to everything only having a single hitpoint. The player character will have a max hp of the level times 5 and it is restored when you go down to the next level. Monsters on the other hand will only have 1 hit point or if armored 2. Combat itself is where the most complicated bits will probably reside. There are pages of notes in my notebook on the time system I want to use and it is much too complicated for this. My basic plan to simplify it is reduce the numbers. As it is written I have all kinds of interesting mechanics but for this game I can't expect people to learn it. By reducing the numbers down to a range of 1 to 9 then even without understanding of the system people can comprehend it. Though I will still keep some of the interesting things such as damage happening at the end of the attack.

   The above gives a decent outline though it probably is missing some things. While randomly chosen the number of levels feels right. I believe I can manage 12 unique feeling enemies. The concept behind the bosses has been in my head for a while so should work out. With the combat it might take some jury rigging of my original design but that happens. So yeah overall it should all fit together but I still need to set this all into a nice bullet pointed list of goals.

The Bullet Pointing

   Now above I have a lot words but very few talk about concrete requirements. Below I will be fixing this by taking what I talked about and some that I didn't and turning it into a nice little list. It will of course be based upon the above but go into more detail about the specifics. While I do reserve the right to change any of the following I wont do so without going into specifically why I am doing so. Anyway here is the list:

  • 12 dungeon levels
    • Generated with already existing dungeon generation code
    • Up and down stairs never generate next to each other
  • Story fits on start screen
  • 12 unique enemy types
    • Unique means they visibly act differently
    • Bosses are extensions of the current levels enemy type
  • Bosses appear after clearing a level
    • They break the rules in some way
    • Defeating a boss gives a choice of 3 powerups
  • Each powerup is a part of a category [ex: weapon]
    • There is a limit to one powerup per a category
    • Game warns you if you are picking a powerup that will replace a current one
  • The way to advance is go down
    • HP is restored when you proceed to the next dungeon level
    • You can go down stairs at any point but you can't go back up
  • Hp will stay relatively low
    • Monsters will have 1 or 2 hp
    • The player has 5 times the level he is on hp
  • Every action takes a specific amount of time
    • The amount goes from 1 to 9
    • A basic attack or move takes 5
  • Displaying it will be using Libtcod and not SDL
  • I will not be touching the dungeon generation code

   And that is the most I can plan out right now without getting to far into it. As a bit of a look into what I have been planning though below I will transcribe some of what I have about the time system. Of course this is also partly so that its backed up and not stuck in a fragile paper form.

My Notes

Basic move - 100

Basic attack - 100

1-hand small - 70

2-hand attack - 140 X

Sword and board - 120 X

Weapon speed mod - -10 to 10

2 weapon & shield should balance

sword and board - the weapon has normal attack and what the shield does is cost a certain number of action points each time it blocks and can only block one attack at a time. Chance to block goes down if also attacking at the same time.

Where does the damage happen? The end is best of the simple but the middle or somewhere in that area would be more realistic. Damage happens at the end of the base attack time then the weapon speed mod happens. Speed mod from 0 to 20 instead of -10 to 10 and maybe take 10 off basic attack. Could still put weapon mod at -10 to 10 and have it 0 to 20 behind scene. No just 0 to 20.

Shields have base 50% block and down to 25% during the time when you are attacking. The shields mod adds 4% a point (+2 is 8%) with +10 max for 90%/65%. Change block 1 attack at a time to one enemies attack at a time so if

How should speed work? Magical haste is simply half costs. Maybe in percentages? Lower is better and strictly a percentage. Basic is 100%. Physical improvement or worsening of speed is additive so -5 to speed causes 95% and always stacks with itself to min of -30 and is added first. Static magical affects stacks with itself Static magical affects partially stacks. It can't cause below -80 after physical is counted in. The lowest is counted first and each successive gives a percentage which is equal to the current speed of the static magical.

(-10 -5 5 10) 100 - 10 = 90 so 90% of -5 is -4.5 round up 90 - 5 = 85 so 85% of 5 is 4.25 round down 85 + 4 = 89 so 89% of 10 is 8.9 round up 89 + 9 = 98

-20 -5 +2 +13 = 90
100 - 20 = 80 - 4 = 76 + 2 = 78 + 10 = 88

-5 -5 -5 +10 = 95
100 - 5 = 95 - 5 = 90 - 5 = 85 + 9 = 94

-10 -10 -5 +25 = 100
100 - 10 = 90 - 9 = 81 - 4 = 77 + 19 = 96

-25 +5 +10 +10 = 100
100 - 25 = 75 + 4 = 79 + 8 = 87 + 9 = 96

-25 +10 +10 +5 = 100
100 - 25 = 75 + 8 = 83 + 8 = 95 + 5 = 100

+10 +5 +5 = 120
100 + 10 = 110 + 6 = 116 + 6 = 122

-25 -12 -5 +5 +12 +25 = 100
100 - 25 = 75 - 9 = 64 - 3 = 61 + 3 = 64 + 8 = 72 + 18 = 90

-25 -12 -5 +25 +12 +5 = 100
100 - 25 = 75 - 9 = 64 - 3 = 61 + 15 = 76 + 9 = 85 + 4 = 89

-25 +25 -12 +12 -5 +5 = 100
100 - 25 = 75 + 19 = 94 - 11 = 105 + 13 = 118 - 6 = 112 + 6 = 118

+25 -25 +12 -12 +5 -5 = 100
100 + 25 = 125 - 31 = 94 + 11 = 105 - 13 = 92 + 5 = 97 - 5 = 92

Need to make program to do more tests. Temp affects do not stack but apply last and are multiplicative. While they do not stack they don't cancel out so a 50% haste for 100 and a 10% for 1000 means after the 100 at 50% you still have 900 at 10%. The difference from static and active is static is something you don't have a choice about. Toggle affects or ones that cost something are active. Artifacts break the rules. The only thing for certain is min 1% total so it doesn't break the game.

-10 -5 +15
100 - 10 = 90 - 4.5 = 86.5 + 12.975 = 99.475
100 - 10 = 90 - 5 = 85 + 13 = 98

-15 +5 +10
100 - 15 = 85 + 4.25 = 89.75 + 8.975 = 98.725
100 - 15 = 85 + 4 = 89 + 9 = 98

And then I complain about not being home and wanting to write a program to help test various ways of rounding the numbers. I also have a little drawing where I figure out various item slots I want. There is also a bit where I start figuring out what each letter will end up being. The one that I will probably keep is having 'a' being an ant and 'A' the ant queen.

Wednesday, May 14, 2014

SDL2 - That Which I Learned

Exported HTML

Lets wrap my crash course in SDL2 up

   By which I mean to say lets go through the steps of creating a simple wrapper for SDL2. I will be using C++11, SDL2, SDL2_image, SDL2_ttf, and the IDE CodeBlocks with MinGW on a Windows 7 to do so. There are a couple important features I want so lets document them here so we don't end up over complicating things later.

  • Most important is that Nothing public should take nor return anything specifically SDL
  • Display a window
  • Display an image
  • Handle the break down of said image into tiles (basically make tilesets work)
  • Display text
  • Change the font and size of displayed text

   The first one is so any code I write with this isn't chained to SDL2. I could go and write a wrapper for another similar thing and use it in place of this. Mostly I am just paranoid here but since it is a learning experience its okay. this will show how you can make code safe from changes in an external library you use. The rest are easy to understand so I don't have to say much about them. Though I will note that Display text is its own bullet point because of it is separate from images.

Actually getting started

   I already have something setup for making SDL2 projects in my programming folder. Having said that I won't be using it as I want to take us through all the steps as with a story the best place to start is the beginning. Anyway to get started just open CodeBlocks and create a new project that is an "Empty Project":

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Next name it what you will. I will be calling mine DragonWrapper_SDL2 because it stands out and gives me a naming convention for future wrappers. You will note that I am creating it at C:\Programming\ on my computer. Having such a folder makes organization much easier and helps with those things that are picky about placement. You might even want to consider breaking it down even further if you program in multiple languages by having sub folders for each.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Now for a bit which really isn't important but more of a personal preference for me. Basically if you can't tell what is different here I have the debug and release output set to the base directory rather then having separate folders for them. I generally do this just to make placement of dll's easier on me. While you don't have to do this if you don't then later on when I put the various things in the base folder you might have to place them elsewhere.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Now we need to grab all the SDL2 bits we want for this project. First off is from here get the mingw dev version of SDL2. Next is the mingw dev version of SDL2_image found here. Finally we will want SDL2_ttf that is here and once again get the mingw dev version. Now with all those files unzip them with something like 7zip. Then unzip what came out once more and you should have ended up with the following stuff:

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Now that we have that all done there is a decision to make. More to the point I have already made the decision but you should know that I did. Basically do we want to use the 32 bit or 64 bit versions. Now if you are on a 32bit computer the decision is already made for you. My decision was made because I even had to state that. I have slight tendencies to want to actually distribute my programs to the world at large so if I even have to consider about a programmer using a 32bit computer I better go with that.

   Now that is out of the way let's make our lives easier. We could leave everything where it is but let's not. In each of the folders is another folder called i686-w64-mingw32. Then in that folder is bin, include, and lib which are the important ones for us. Throw them all into one folder (I called mine "SDL2 32" because it is SDL2 and 32bit). This will let us link to just one place when it comes time for that. Also all the dll's you will need are in one bin folder which is convenient.

   Now that we did that let's hook them up to our project. Right click on the project and select build options. From there select the top level so we can affect both Debug and Release builds. As I am using some random things from C++11 let's just check that off right away.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Okay now let's select the Linker settings tab and add a few things to the "Other linker options" box. We want to put -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf -static-libgcc -static-libstdc++ in there so when we use the stuff it will be available right away. The window should now look like this (I put line breaks in there so you can see it all, it doesn't do anything):

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Now select the Search directories tab and then the sub tab called Compiler. Click add and then browse to wherever you stored your SDL2 stuff. In there select the include folder and inside it is a folder called SDL2 which is what we want so select it and click ok. It will then ask whether you want to keep it a relative path. I generally go with saying no and you will see I did so though the choice is up to you as each case has its own reasons for choosing it.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Now let's head onto the next sub tab which is called Linker. Here we will be doing a similar thing but instead of the include\SDL2 folder you just want the lib folder.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Finally (for this bit) head back over to the Compiler settings tab and change over to Debug. It will ask if you want to save what you have done when you do the change and just say yes. Now we change something for a little quality of life. In the options enable "Enable all compiler warnings" and "Enable extra compiler warnings". CodeBlocks by default uses a low level of warnings and you really want more of them then the default because they can help later on.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Now to finish the basic setup so let's grab the dlls we want. For this just head to where ever you stored all the stuff and crack open the bin folder. As I mentioned above all the dlls for SDL2 are contained here, or at least for our needs. In this case what we need is as follows, so just select them and copy them into the project folder.

  • SDL2.dll
  • SDL2_image.dll
  • SDL2_ttf.dll
  • libfreetype-6.dll

    This one is for doing text

  • libpng16-16.dll

    we need this because I am going to use png images. If you are using jpegs for instance you would want libjpeg-9.dll

  • zlib.dll

Start of Actual Code

   Now let's get some actual code on a page. Well actually, let's get an actual page on the project. To start just add a new class file, I will be calling mine DrW_SDL2. Mostly because the Dr makes you think Doctor W which is cool, that and the three letters are relatively unique to help with auto-complete.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   Now we have two files. [name].cpp and [name].h which is fine but since I aim to have a library at the end of this we should add one more file to test with. I just called it libtest.cpp as that is exactly what it will do. With that though I must admit I lied about being done with setup. Go back to Project and select properties. Now select the build target tab and then select the release. You will note the Type is set to something like Console application. Change that to Dynamic library and then in the Build target files uncheck the libtest file. Now when you build the release you will get a .dll file.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   With this I can say we are truly done with setup but there is one thing to bring up. Basically all of my code for this and even this post itself will be on Github. I use Git for source control even when I am not publishing it to Github. So with that said it was at this point where I did the first commit on the project. You can see the code here. Also of interest, as I mentioned, I have my blog posts up on my Github so you can see my posts here and even see what is in the workings. So with that all out of the way time to put code on the page.

   Now there are many ways to start on a project but for this there are a few things I think are important to get out of the way. The very first of which is putting all the includes into the .h file. Below is just a quick run down of what I will start with (I might add more later but this is what I know of now). Also note I am just putting what goes in the angle brackets below so the full form is #include <[whats below]>

  • SDL.h
  • SDL_image.h
  • SDL_ttf.h
  • iostream
  • vector
  • map

   With that it is time for some private variables. This will be somewhat forward looking as I am including some stuff before it is needed but they where easy to see coming. Anyway we want an SDL_Window, an SDL_Render for it, the TTF_Font, and a vector of SDL_Texture.

SDL_Window* _window;
SDL_Renderer* _renderer;
TTF_Font* _font = NULL;
std::vector<SDL_Texture*> _textures;

   You will note that I have a bit of a naming convention for private names. Mainly I start them with an underscore '_' and I use it on all private names. They also tend to all be lowercase. This is just personal choice for me so do as you will but I like be able to tell at glance that something is private. With this let me explain the one slightly different bit here which is the vector. The wrapper needs to be able to hold many textures to be useful and a vector was the simplest and quickest way for me to store them all. It also lets me refer to all textures by the int for where they are in the vector. Mind you this is only possible if you don't remove from the vector but I don't intend to. The other thing I considered was using a map so I could for instance use a string as the key. In the end though I found that a vector was the easiest way to make sure I had as much space as I needed without over-complicating it.

   Now with that done let's do a little more work in the header file before we move onto implementing it all. First we want a way to log an error with SDL which is simple enough. I actually just implemented this in the header when I tried this before but let's leave implementation of things in the .cpp files. After that we need something that will actually return a SDL_Texture that we want to load and a similar thing for text. Because the first deals with actual errors from SDL and the others return an SDL2 object these will be private as well.

void _logerror (const std::string &message);
SDL_Texture* _loadtexture (const std::string &file);
SDL_Texture* _rendertextastexture (const std::string &message, SDL_Color color);

   Now let's head over to the .cpp file and get some of this stuff working. We want to first initiate SDL itself so in the constructor we want to put the following:

DrW_SDL2::DrW_SDL2()
{
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
       _logerror("SDL_Init");
    }
    if (TTF_Init() != 0){
       _logerror("TTF_Init");
    }
}

   You will note that I just init everything. I don't know specifically what I need so I don't really feel like holding back on this. You will also notice of course that I am using the _logerror before I have actually shown it. This is partly because I already know how it will be coded, but mostly because this is how it will be used. I don't need to know the specifics if I know this is the data it will accept. Anyway let's do the actual code for SDL error handling.

void DrW_SDL2::_logerror(const std::string &message){
    std::cout << message << " Error: " << SDL_GetError() << std::endl;
}

   And it is really that simple. Next we can do texture loading. I will include loading text as a texture here as well. Rendering text as a texture is a bit more complex but that is fine.

SDL_Texture* DrW_SDL2::_loadtexture(const std::string &file){
    SDL_Texture* outputTexture = IMG_LoadTexture(_renderer, file.c_str());
    if (outputTexture == nullptr){
        _logerror("IMG_LoadTexture");
    }
    return outputTexture;
}

SDL_Texture* DrW_SDL2::_rendertextastexture(const std::string &message, SDL_Color color){
    if (_font == nullptr){
        return nullptr;
    }
    SDL_Surface* tempSurface = TTF_RenderText_Blended(_font, message.c_str(), color);
    if (tempSurface == nullptr){
        _logerror("TTF_RenderText_Blended");
        return nullptr;
    }
    SDL_Texture* outputTexture = SDL_CreateTextureFromSurface(_renderer, tempSurface);
    if (outputTexture == nullptr){
        _logerror("SDL_CreateTextureFromSurface");
    }
    SDL_FreeSurface(tempSurface);
    return outputTexture;
}

   I will note at this point that I am not really handling the errors so much as just noting when they happen. Later I might come back and replace cout with writing the errors to a text file. As it is I would not really call it complete but it is good enough for this. Also of note is that for the text I am using Blended mode. That would be the fancy way of rendering text and depending on how it performs I may add in the ability to choose Shaded or even Solid as needed. Anyway we have finished all the private functions we need at the moment. Now I know I have been doing a lot of setup here with this code but that is because I know what I want it to do. If you where to start from scratch this would probably be a little more haphazard. With this out of the way though I will be stepping into more fluid territory. The first step in this is simple enough though, let's write something to create a window.

void createWindow(const std::string &windowtitle, int x, int y, int width, int height, bool resizable);

   This could be enough but I want to add one thing. While it is nice to specify exactly the x and y of your window but sometimes you just want it to open wherever. We will now add another createWindow which doesn't have the x and y arguments.

void createWindow(const std::string &windowtitle, int width, int height, bool resizable);

   With that let's write the actual code but first I will be explaining something slightly tricky I plan to use. Seeing as it is something a little beyond the basics I will explain it first. I am going to use an "inline if" in the code. What happens is exactly the same as a normal if but as noted inline. The way it works is [argument]?[true]:[false] so you could say x = (a > b)?y:z. If 'a' is greater than 'b' then 'x' is equal to 'y' else it is equal to 'z'. Anyway here is the code for creating a window.

void DrW_SDL2::createWindow(const std::string windowtitle, int x, int y, int width, int height, bool resizable){
    _window = SDL_CreateWindow(windowtitle.c_str(), x >= 0 ? x : SDL_WINDOWPOS_UNDEFINED, y >= 0 ? y : SDL_WINDOWPOS_UNDEFINED, width, height, resizable ? SDL_WINDOW_RESIZABLE : (SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE));
    if (_window == nullptr){
        _logerror("SDL_CreateWindow");
    }
    _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (_renderer == nullptr){
        _logerror("SDL_CreateRenderer");
    }
}

void DrW_SDL2::createWindow(const std::string windowtitle, int width, int height, bool resizable){
    createWindow(windowtitle, -1, -1, width, height, resizable);
}

   You will note I used the inline if three times and to be honest I didn't need to. It is mostly just my own aesthetics when it comes to coding. I could have created int X = x; and then wrote if(x < 0) {X = SDL_WINDOWPOS_UNDEFINED;} and do the same for y and the flags. Anyway you will have noticed that I also set the renderer here and applied some flags to it as well. The renderer is set here because it is basically attached to the window and I don't plan to do anything fancy with it. If you wanted to do something more with your renderer you may want to separate that out. As for the flags the accelerated one is basically just telling it to use hardware acceleration. The present vsync flag means that the render limits itself to your monitors refresh rate so you don't end up with insanely high but unneeded fps.

   With this we have reached an important point. Basically we can finally test that this whole mess is working though we probably want to added a couple things first. So let's add the following to the deconstructor:

DrW_SDL2::~DrW_SDL2(){
    for (size_t iter = 0; iter < _textures.size(); ++iter){
        SDL_DestroyTexture(_textures[iter]);
    }
    if (_font != NULL){
        TTF_CloseFont(_font);
    }
    SDL_DestroyRenderer(_renderer);
    SDL_DestroyWindow(_window);
    IMG_Quit();
    TTF_Quit();
    SDL_Quit();
}

   Those lines will take care of getting rid of what we will be making in this next step. Which of course means we will be doing something with the libtest file. I will be using an SDL command in the test at this time as there isn't a need for it in the wrapper yet.

#include "drw_sdl2.h"

const int SCREEN_WIDTH = 640, SCREEN_HEIGHT = 480;

int main(int argc, char **argv){
    DrW_SDL2 sdl;
    sdl.createWindow("test window", SCREEN_WIDTH, SCREEN_HEIGHT, true);
    SDL_Delay(2000);
    return 0;
}

   With that in, running the project should open up a blank window for a couple seconds and there should be a couple warnings in the build messages. The warnings are about argc and argv not being used. They from what I can tell don't matter and are in because SDL requires them for compatibility reasons. As for it staying up for those couple seconds this is because of the SDL_Delay command. It accepts an int representing the number of milliseconds to delay. Of course this causes the window to be unresponsive but its just for testing at this point so that is okay. Now to actually load a texture.

int DrW_SDL2::createText(const std::string &message){
    SDL_Color color = {0, 0, 0, 255};
    _textures.push_back(_rendertextastexture(message, color));
    return _textures.size() - 1;
}

int DrW_SDL2::createTexture(const std::string &file){
    _textures.push_back(_loadtexture(file));
    return _textures.size() - 1;
}

   They both return an int which is the location the texture is at. This means that on the libtest side of things you just keep track of textures by an int which is simple and straightforward. With the createText code I am just having black as the color of the text. I will probably at a later date add the ability to change the color, mostly likely with an enum for various colors. Anyway with this we are halfway to the goal of displaying an image.

   We do have a bit of a thing now to decide. Rendering a texture has a few options and half require the use of SDL_Rect. When I did some work while learning SDL I just used and int array to represent this but I want a better option. From what I can tell there are about 4 of ways to do this. Just use the int array as it is simple and works, Make my own struct Rect, use a function that hides the use of SDL_Rect, and finally make a class Rect. Because of some stuff I looked into we will be using the last option. But let's write the class and then I will explain what I did. Do note I will be putting the class in its own files named drw_sdl2_rect while the class itself is just called Rect.

drw_sdl2_rect.h

#include <SDL.h>

class Rect{
    public:
        Rect(int x, int y, int w, int h);
        Rect(int x, int y);
        int X, Y, W, H;
    private:
        SDL_Rect _getsdlrect();
    friend class DrW_SDL2;
};

drw_sdl2_rect.cpp

Rect::Rect(int x, int y, int w, int h){
    X = x;
    Y = y;
    W = w;
    H = h;
}

Rect::Rect(int x, int y){
    X = x;
    Y = y;
    W = -1;
    H = -1;
}

SDL_Rect* Rect::_getsdlrect(){
    SDL_Rect* output = new SDL_Rect;
    output->x = X;
    output->y = Y;
    output->w = W;
    output->h = H;
    return output;
}

   With this we can continue but first let's look at what I did with the Rect class. In the constructor that only takes x and y I set the width and height to -1. This will tell me I need to set those later on and if I forget definitely throw an error. If I used a 0 or some other way to show it wasn't set I might end up not setting them somewhere and wondering why the texture isn't rendering. The big thing is that I declare my base class DrW_SDL2 as a friend. This means that even though it is private the wrapper class will be able to use the function. Now this doesn't seem much but it does means I get to use this class as SDL_Rect without exposing it outside the wrapper.

   Now that we have the Rect squared away we need to write the functions that will actually render the textures. When I learned this I had a bunch of replication in these functions as there are 4 options for rendering textures and they are quite similar. I have since figured a better way of doing it so I can reduce the unneeded duplication of code. It will start with a private function.

void DrW_SDL2::_rendertexture(const int textureid, const SDL_Rect* source, const SDL_Rect* destination){
    SDL_RenderCopy(_renderer, _textures[textureid], source, destination);
}

   And now for four more public functions and one private helper. The four can be public as they won't be accepting SDL_Rect but rather our own Rect class.

Rect DrW_SDL2::_gettexturesize(const int textureid, int x, int y){
    Rect output(x, y);
    SDL_QueryTexture(_textures[textureid], NULL, NULL, &output.W, &output.H);
    return output;
}

void DrW_SDL2::renderTexture(const int textureid, Rect destination){
    _rendertexture(textureid, NULL, destination._getsdlrect());
}

void DrW_SDL2::renderTexture(const int textureid, int x, int y){
    renderTexture(textureid, _gettexturesize(textureid, x, y));
}

void DrW_SDL2::renderTexture(const int textureid, Rect source, Rect destination){
    _rendertexture(textureid, source._getsdlrect(), destination._getsdlrect());
}

void DrW_SDL2::renderTexture(const int textureid, Rect source, int x, int y){
    renderTexture(textureid, source, _gettexturesize(textureid, x, y));
}

   I definitely prefer this to how my original try at it worked out. Even only counting the internal bits and not including any bracket only lines I used 34 lines of code previously. Here even with 2 extra functions and counting all lines the total comes out to only 20. While shorter is not always better in this case it definitely is. A couple more functions are needed before we can render a texture but these are basically just the thinnest of wrappings.

void DrW_SDL2::renderclear(){
    SDL_RenderClear(_renderer);
}

void DrW_SDL2::renderpresent(){
    SDL_RenderPresent(_renderer);
}

   Very straightforward with these as they just letting us access the clear and render functions without actually touching SDL. Anyway to test this we need an image and I have prepared one for us. I have made a wonderful 64x22 png in the colors of Dwarf Fortress microline and a wonderful eye burn magenta.

While I did have a picture here it apparently failed. Inform me and I will try to fix it.

   With this let's render it to the screen. I placed the image in the base folder for the project and we just need to add the following lines right before SDL_Delay in libtest.cpp

int test = sdl.createTexture("TestTexture.png");
sdl.renderclear();
sdl.renderTexture(test, 10, 10);
sdl.renderpresent();

   With that the screen should pop up as all black but with our little image at 10,10 on the screen. But of course you could probably tell by the test image it is setup so we can use it as a mini tileset. So let's add that functionality in now. First to add a private Map with int as the key and a Vector of Rect. Next we will also want a couple public functions.

std::map<int, std::vector<Rect>> _tilesetdefinition;
int DrW_SDL2::setupTileset(const int textureid, Rect source){
    _tilesetdefinition[textureid].push_back(source);
    return _tilesetdefinition[textureid].size() - 1;
}

Rect DrW_SDL2::getSourceRect (const int textureid, const int tileid){
    return _tilesetdefinition[textureid][tileid];
}

   This setup can only set one tile of a tileset at a time but any automation should really be elsewhere. Basically I would be wanting to import the tile info from a file or something similar. Anyway let's break the picture up and paste it all over the place in our libtest. Because of needing to replace some lines I will be replacing everything between int test and sdl.renderpresent()

int solidPink = sdl.setupTileset(test, Rect(1, 1, 20, 20));
int halfandhalf = sdl.setupTileset(test, Rect(22, 1, 20, 20));
int solidBlue = sdl.setupTileset(test, Rect(43, 1, 20, 20));
sdl.renderclear();
sdl.renderTexture(test, sdl.getSourceRect(test, solidPink), Rect(20, 20, 20, 20));
sdl.renderTexture(test, sdl.getSourceRect(test, solidBlue), Rect(40, 40, 20, 20));
sdl.renderTexture(test, sdl.getSourceRect(test, halfandhalf), Rect(40, 20, 20, 20));
sdl.renderTexture(test, sdl.getSourceRect(test, halfandhalf), Rect(20, 40, 20, 20));

   These lines will make something that looks very much like the middle image of the png appear. Of course it is actually a pink square, a blue square, and a couple half and half squares. Seeing as that works we should probably get it so we can print text on the screen. First we need to be able to set the font.

void DrW_SDL2::setFont(const std::string &font, int fontsize){
    _font = TTF_OpenFont(font.c_str(), fontsize);
}

   And that is it basically. Because of a lot of the setup that was previously done we can just plug in some stuff and have it work. Sadly I can't just have it work because we don't have an actual font to use yet. Luckily for us everyone tends to have some ttf files lying around but just in case here are some free fonts. I just grabbed Mono, Sans, and Serif from there and threw them in a fonts folder. Now we can toss the following code just above the renderpresent and get it running.

sdl.setFont("fonts/freeserif.ttf", 16);
int text = sdl.createText("test");
sdl.renderTexture(text, 20, 20);

   Now when you run it there will be the word test in your square. Of course with this done we have finished with implementing what I outlined way at the top. There are a lot of stuff that could still be done but that is for later. You can now take the code and switch it the build target to release and get a dll which you can use or even just take the files and use them as is. At the very bottom I have the final code as done in this post as I will definitely be adding to the wrapper in the future as I need to. This is the barest boned structure and if you can follow it up to here and want to use it I advise you not wait for me to do so. The best way to learn this stuff is by doing and I might not need the features you want. Also if there is any errors in the code or I did something that while technically correct isn't proper please tell me. It is much better for me to be told I am wrong so I can fix it rather having it stay wrong and misinform people with this post.

drw_sdl2.h

#ifndef DRW_SDL2_H
#define DRW_SDL2_H

#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <iostream>
#include <map>
#include <vector>
#include "drw_sdl2_rect.h"

class DrW_SDL2
{
    public:
        DrW_SDL2();
        void createWindow(const std::string windowtitle, int x, int y, int width, int height, bool resizable);
        void createWindow(const std::string windowtitle, int width, int height, bool resizable);
        int createText(const std::string &message);
        int createTexture(const std::string &file);
        void renderTexture(const int textureid, Rect destination);
        void renderTexture(const int textureid, int x, int y);
        void renderTexture(const int textureid, Rect source, Rect destination);
        void renderTexture(const int textureid, Rect source, int x, int y);
        void renderclear();
        void renderpresent();
        int setupTileset(const int textureid, Rect source);
        Rect getSourceRect (const int textureid, const int tileid);
        void setFont(const std::string &font, int fontsize);
        virtual ~DrW_SDL2();
    private:
        SDL_Window* _window;
        SDL_Renderer* _renderer;
        TTF_Font* _font = NULL;
        std::vector<SDL_Texture*> _textures;
        void _logerror (const std::string &message);
        SDL_Texture* _loadtexture (const std::string &file);
        SDL_Texture* _rendertextastexture (const std::string &message, SDL_Color color);
        void _rendertexture(const int textureid, const SDL_Rect* source, const SDL_Rect* destination);
        Rect _gettexturesize(const int textureid, int x, int y);
        std::map<int, std::vector<Rect>> _tilesetdefinition;
};

#endif // DRW_SDL2_H

drw_sdl2.cpp

#include "drw_sdl2.h"

DrW_SDL2::DrW_SDL2(){
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
       _logerror("SDL_Init");
    }
    if (TTF_Init() != 0){
       _logerror("TTF_Init");
    }
}

void DrW_SDL2::_logerror(const std::string &message){
    std::cout << message << " Error: " << SDL_GetError() << std::endl;
}

SDL_Texture* DrW_SDL2::_loadtexture(const std::string &file){
    SDL_Texture* outputTexture = IMG_LoadTexture(_renderer, file.c_str());
    if (outputTexture == nullptr){
        _logerror("IMG_LoadTexture");
    }
    return outputTexture;
}

SDL_Texture* DrW_SDL2::_rendertextastexture(const std::string &message, SDL_Color color){
    if (_font == nullptr){
        return nullptr;
    }
    SDL_Surface* tempSurface = TTF_RenderText_Blended(_font, message.c_str(), color);
    if (tempSurface == nullptr){
        _logerror("TTF_RenderText_Blended");
        return nullptr;
    }
    SDL_Texture* outputTexture = SDL_CreateTextureFromSurface(_renderer, tempSurface);
    if (outputTexture == nullptr){
        _logerror("SDL_CreateTextureFromSurface");
    }
    SDL_FreeSurface(tempSurface);
    return outputTexture;
}

void DrW_SDL2::createWindow(const std::string windowtitle, int x, int y, int width, int height, bool resizable){
    _window = SDL_CreateWindow(windowtitle.c_str(), x >= 0 ? x : SDL_WINDOWPOS_UNDEFINED, y >= 0 ? y : SDL_WINDOWPOS_UNDEFINED, width, height, resizable ? SDL_WINDOW_RESIZABLE : (SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE));
    if (_window == nullptr){
        _logerror("SDL_CreateWindow");
    }
    _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (_renderer == nullptr){
        _logerror("SDL_CreateRenderer");
    }
}

void DrW_SDL2::createWindow(const std::string windowtitle, int width, int height, bool resizable){
    createWindow(windowtitle, -1, -1, width, height, resizable);
}

int DrW_SDL2::createText(const std::string &message){
    SDL_Color color = {0, 0, 0, 255};
    _textures.push_back(_rendertextastexture(message, color));
    return _textures.size() - 1;
}

int DrW_SDL2::createTexture(const std::string &file){
    _textures.push_back(_loadtexture(file));
    return _textures.size() - 1;
}

void DrW_SDL2::_rendertexture(const int textureid, const SDL_Rect* source, const SDL_Rect* destination){
    SDL_RenderCopy(_renderer, _textures[textureid], source, destination);
}

Rect DrW_SDL2::_gettexturesize(const int textureid, int x, int y){
    Rect output(x, y);
    SDL_QueryTexture(_textures[textureid], NULL, NULL, &output.W, &output.H);
    return output;
}

void DrW_SDL2::renderTexture(const int textureid, Rect destination){
    _rendertexture(textureid, NULL, destination._getsdlrect());
}

void DrW_SDL2::renderTexture(const int textureid, int x, int y){
    renderTexture(textureid, _gettexturesize(textureid, x, y));
}

void DrW_SDL2::renderTexture(const int textureid, Rect source, Rect destination){
    _rendertexture(textureid, source._getsdlrect(), destination._getsdlrect());
}

void DrW_SDL2::renderTexture(const int textureid, Rect source, int x, int y){
    renderTexture(textureid, source, _gettexturesize(textureid, x, y));
}

void DrW_SDL2::renderclear(){
    SDL_RenderClear(_renderer);
}

void DrW_SDL2::renderpresent(){
    SDL_RenderPresent(_renderer);
}

int DrW_SDL2::setupTileset(const int textureid, Rect source){
    _tilesetdefinition[textureid].push_back(source);
    return _tilesetdefinition[textureid].size() - 1;
}

Rect DrW_SDL2::getSourceRect (const int textureid, const int tileid){
    return _tilesetdefinition[textureid][tileid];
}

void DrW_SDL2::setFont(const std::string &font, int fontsize){
    _font = TTF_OpenFont(font.c_str(), fontsize);
}

DrW_SDL2::~DrW_SDL2(){
    for (size_t iter = 0; iter < _textures.size(); ++iter){
        SDL_DestroyTexture(_textures[iter]);
    }
    if (_font != NULL){
        TTF_CloseFont(_font);
    }
    SDL_DestroyRenderer(_renderer);
    SDL_DestroyWindow(_window);
    IMG_Quit();
    TTF_Quit();
    SDL_Quit();
}

drw_sdl2_rect.h

#ifndef RECT_H
#define RECT_H

#include <SDL.h>

class Rect{
    public:
        Rect(int x, int y, int w, int h);
        Rect(int x, int y);
        int X, Y, W, H;
    private:
        SDL_Rect* _getsdlrect();
    friend class DrW_SDL2;
};

#endif // RECT_H

drw_sdl2_rect.cpp

#include "drw_sdl2_rect.h"

Rect::Rect(int x, int y, int w, int h){
    X = x;
    Y = y;
    W = w;
    H = h;
}

Rect::Rect(int x, int y){
    X = x;
    Y = y;
    W = -1;
    H = -1;
}

SDL_Rect* Rect::_getsdlrect(){
    SDL_Rect* output = new SDL_Rect;
    output->x = X;
    output->y = Y;
    output->w = W;
    output->h = H;
    return output;
}

libtest.cpp

#include "drw_sdl2.h"

const int SCREEN_WIDTH = 640, SCREEN_HEIGHT = 480;

int main(int argc, char **argv){
    DrW_SDL2 sdl;
    sdl.createWindow("test window", SCREEN_WIDTH, SCREEN_HEIGHT, true);
    int test = sdl.createTexture("TestTexture.png");
    int solidPink = sdl.setupTileset(test, Rect(1, 1, 20, 20));
    int halfandhalf = sdl.setupTileset(test, Rect(22, 1, 20, 20));
    int solidBlue = sdl.setupTileset(test, Rect(43, 1, 20, 20));
    sdl.renderclear();
    sdl.renderTexture(test, sdl.getSourceRect(test, solidPink), Rect(20, 20, 20, 20));
    sdl.renderTexture(test, sdl.getSourceRect(test, solidBlue), Rect(40, 40, 20, 20));
    sdl.renderTexture(test, sdl.getSourceRect(test, halfandhalf), Rect(40, 20, 20, 20));
    sdl.renderTexture(test, sdl.getSourceRect(test, halfandhalf), Rect(20, 40, 20, 20));
    sdl.setFont("fonts/freeserif.ttf", 16);
    int text = sdl.createText("test");
    sdl.renderTexture(text, 20, 20);
    sdl.renderpresent();
    SDL_Delay(2000);
    return 0;
}

Thursday, May 8, 2014

The Start of DragonheartedCode

I have failed at blogging

   Now the blog itself wasn't really a failure. ASCII, Dice, and Why was a good blog and has a number of interesting posts on it. The problem was it had three different audiences. It was about roguelikes, programming, and roleplaying which wasn't even the modern style but the old school. Roguelikes and programming had overlap as I am working on programming a roguelike but the last doesn't fit that well. I could probably pick it back up but if I did then I would like to refocus it on roleplaying. So yeah, this is my programming blog.

   I have been approaching the end of my college course for a while now. There is one thing I have been seeing and been told on numerous programming blogs, podcasts, and such. It is that the type of job I want will be a lot easier to get if I have a couple of specific things. First is of course a backlog of projects, preferably on Github, as well as with contributions to opensource projects. That is being worked on and not entirely unrelated to this blog, but then we get to the second thing. That is have a blog about what you do. It doesn't have to be popular, it just have to exist and be updated semi-regularly. Having one shows that not only do you know what your blogging about but it shows you are into to it. Have you yet to read a blog where the person hated what they where talking about? I am willing to bet that even if you did it was very obvious. This is my blog to show that I love what I do and want to share it with you.

Of course that is very selfish

   That isn't the point of this blog though. While I would like to use this to leverage myself into a good job that is not why I am doing this blog. I had a blog about all these different things I was interested in even before all that. This will probably be the least informative post on the whole blog, maybe barring my deciding to post something stupid for April fools day. Let me lay out why I am doing this for you.

   I spent the last couple weeks trying to learn SDL2 for C++. I was bashing my head against Lazy Foo's tutorials because the previous one for SDL was really good. Now mind you, the SDL2 tutorial isn't bad. There are people who will very much be able to learn what they need to learn from it. Problem was it wasn't setup right for how I needed to learn. Then I found a tutorial within the last couple days that was right for me. TwinklebearDev has a tutorial for SDL2 which just fit right with how I wanted to learn. I will be doing a post about SDL2 later as I still have something to finish. There is some self learning and modification of the code I ended up with from it that I want to do first.

   Now what does this have to do with this blog you ask? Basically I want to give back what I have gotten and more so. Someone out there is learning SDL2 and what I found would help them. I heard A really good way to look at it from a podcast I have been listening too recently. The host of Giant Robots Crashing into other Giant Robots encourages people to start their own blog about what they do. The reason for this is that while you know it now since you have learned it the you from a week ago would have loved to know what you understand now. This blog is for you out there who don't know yet.

Here is to learning because the moment I stop learning is the moment I am dead.