Translation statement:
This series of tutorials comes from Dev hub and all the right to interpretation belongs to the original author. I translated this series of tutorials only from my hobbies. Because I am also a beginner and have a limited level of English, it is inevitable to make mistakes.
Address: http://www.sdltutorials.com/sdl-tutorial-tic-tac-toe/
At this time, we have laid a good foundation for developing a game. So far, we have established a basic framework to process general rules, a special class to process messages, and a class to process some surface functions. In this lesson, we will use all these things to combine them and then create a well-known game. Don't worry. This is simple. On the basis of the previous lesson.
The first thing we need to do is to plan our games. Based on experience, we know that the word game has 3*3 grids. You can place X or O in these grids. Then we know that we need three images, one acting as a grid, one acting as X, and the other acting as O. We do not need to repeat multiple X or O, because we can draw the same image multiple times in the program. Let's take the first step. Our grid is 600*600, and X and O are both 200*200.
Now that we have images, we need to use a method to load them into our program. Open the CAPP. h file and make some modifications. Delete the surfaces for testing and add three new surfaces.
- # Ifndef _ capp_h _
- # DEFINE _ capp_h _
- # Include <SDL. h>
- # Include "cevent. H"
- # Include "csurface. H"
- Class CAPP: Public cevent {
- PRIVATE:
- Bool running;
- Sdl_surface * surf_display;
- PRIVATE:
- Sdl_surface * surf_grid;
- Sdl_surface * surf_x;
- Sdl_surface * surf_o;
- Public:
- CAPP ();
- Int onexecute ();
- Public:
- Bool oninit ();
- Void onevent (sdl_event * event );
- Void onexit ();
- Void onloop ();
- Void onrender ();
- Void oncleanup ();
- };
- # Endif
In the same way, enable the CAPP. cpp and make corresponding modifications. Delete the test surface and add three new ones again.
- # Include "Capp. H"
- Capp: CAPP (){
- Surf_grid = NULL;
- Surf_x = NULL;
- Surf_o = NULL;
- Surf_display = NULL;
- Running = true;
- }
- Int CAPP: onexecute (){
- If (oninit () = false ){
- Return-1;
- }
- Sdl_event event;
- While (running ){
- While (sdl_pollevent (& event )){
- Onevent (& event );
- }
- Onloop ();
- Onrender ();
- }
- Oncleanup ();
- Return 0;
- }
- Int main (INT argc, char * argv []) {
- Capp theapp;
- Return theapp. onexecute ();
- }
Oh, you guessed it. Open capp_oncleanup.cpp and make the same changes. As before, remove the test surface and add three new ones.
- # Include "Capp. H"
- Void CAPP: oncleanup (){
- Sdl_freesurface (surf_grid );
- Sdl_freesurface (surf_x );
- Sdl_freesurface (surf_o );
- Sdl_freesurface (surf_display );
- Sdl_quit ();
- }
Now we have completed the surface settings and loaded them into the memory. Open capp_oninit.cpp and modify it. Remove the test surface and load three new ones here. Make sure that the file name is correct. Change the window size to 600*600, that is, the grid size. In this way, the entire window can be filled without leaving any blank space around the window.
- # Include "Capp. H"
- Bool CAPP: oninit (){
- If (sdl_init (sdl_init_everything) <0 ){
- Return false;
- }
- If (surf_display = sdl_setvideomode (600,600, 32, sdl_hwsurface | sdl_doublebuf) = NULL ){
- Return false;
- }
- If (surf_grid = csurface: onload ("./GFX/grid.bmp") = NULL ){
- Return false;
- }
- If (surf_x = csurface: onload ("./GFX/x.bmp") = NULL ){
- Return false;
- }
- If (surf_o = csurface: onload ("./GFX/o.bmp") = NULL ){
- Return false;
- }
- Return true;
- }
You may have noticed that I have made some modifications to the file name. I added./GFX/before the file name to specify the folder where the image is located. Before the game grows, make sure that a folder contains these files. For this reason, all images will be placed in the GFX folder.
Now, we will display the grid to the screen. Open capp_onrender.cpp and change the rendering of the test table to a rendering grid.
- # Include "Capp. H"
- Void CAPP: onrender (){
- Csurface: ondraw (surf_display, surf_grid, 0, 0 );
- Sdl_flip (surf_display );
- }
Try compiling your program. If it succeeds, you can see that the grid is displayed. Remember to use a surface in five steps: declare, set to null, load, draw, and then clean up. It is best to keep these five steps in mind now, because if you forget one step, there will be problems. For example, if you ignore setting a surface to null, it will lead to undefined. If you ignore the release of a surface, it will cause memory leakage.
Maybe you will feel that our image is a bit strange. How can X and o be purple backgrounds. Of course there is a reason. We need to make these surfaces transparent. In fact, as long as the purple is displayed, we will make the purple transparent. SDL provides a simple function to achieve this effect, sdl_setcolorkey. To implement it, open csurface. h to add a new function.
- # Ifndef _ csurface_h _
- # DEFINE _ csurface_h _
- # Include <SDL. h>
- Class csurface {
- Public:
- Csurface ();
- Public:
- Static sdl_surface * onload (char * file );
- Static bool ondraw (sdl_surface * surf_dest, sdl_surface * surf_src, int X, int y );
- Static bool ondraw (sdl_surface * surf_dest, sdl_surface * surf_src, int X, int y, int X2, int Y2, int W, int H );
- Static bool transparent (sdl_surface * surf_dest, int R, int g, int B );
- };
- # Endif
Now implement this function, open csurface. cpp and add it:
- Bool csurface: transparent (sdl_surface * surf_dest, int R, int g, int B ){
- If (surf_dest = NULL ){
- Return false;
- }
- Sdl_setcolorkey (surf_dest, sdl_srccolorkey | sdl_rleaccel, sdl_maprgb (surf_dest-> Format, R, G, B ));
- Return true;
- }
Note the three parameters passed except the surface. We have three transparent color values, not just purple. For example, if we want to make the red transparent, it is, 0, 0.
This function first checks whether we have passed a valid surface. If yes, we set a Color Key (transparent) for a color ). The first parameter is the surface where the Color Key is applied. The second parameter is a sign that tells SDL how to operate. The third parameter is a transparent color. These logos are very basic. The first one tells SDL to apply the color key on a source (that is, the surface passed in, the second tells SDL to try to use RLE acceleration (in fact, to draw faster ). The third parameter is a bit complicated. To create a color, we use sdl_maprgb. This function uses a surface, you also need to specify the color value (R, G, B), and then try to find the nearest color value on the surface to match it. You may be wondering what it is. Not all surfaces have the same palette. Do you still remember that there were only a few color values available in the early nes? Similarly, sdl_maprgb uses a color value and finds the nearest value in a surface palette.
Now let's apply this new function to our surface, open capp_oninit.cpp, and then make the following changes:
- # Include "Capp. H"
- Bool CAPP: oninit (){
- If (sdl_init (sdl_init_everything) <0 ){
- Return false;
- }
- If (surf_display = sdl_setvideomode (600,600, 32, sdl_hwsurface | sdl_doublebuf) = NULL ){
- Return false;
- }
- If (surf_grid = csurface: onload ("./GFX/grid.bmp") = NULL ){
- Return false;
- }
- If (surf_x = csurface: onload ("./GFX/x.bmp") = NULL ){
- Return false;
- }
- If (surf_o = csurface: onload ("./GFX/o.bmp") = NULL ){
- Return false;
- }
- Csurface: transparent (surf_x, 255, 0,255 );
- Csurface: transparent (surf_o, 255, 0,255 );
- Return true;
- }
Everything on the surface has been done. What we need to do below is to draw X and O in one way. We can draw them anywhere in the grid because they are not always at the same point. Next, we need to create an array of nine containers. The data in the array tells us the value of each cell on the grid. So is the upper left, 1 is the upper middle, 2 is the upper right, and so on. Add the created array to CAPP. h:
- # Ifndef _ capp_h _
- # DEFINE _ capp_h _
- # Include <SDL. h>
- # Include "cevent. H"
- # Include "csurface. H"
- Class CAPP: Public cevent {
- PRIVATE:
- Bool running;
- Sdl_surface * surf_display;
- PRIVATE:
- Sdl_surface * surf_grid;
- Sdl_surface * surf_x;
- Sdl_surface * surf_o;
- PRIVATE:
- Int grid [9];
- Public:
- CAPP ();
- Int onexecute ();
- Public:
- Bool oninit ();
- Void onevent (sdl_event * event );
- Void onexit ();
- Void onloop ();
- Void onrender ();
- Void oncleanup ();
- };
- # Endif
We know that each cell has optional values: NULL, X, and O. They tell us what is in a unit currently. We use an Enum here to make it more clean than 0, 1, and 2. If you do not understand what Enum is, you can go to a quick look at this aspect. You only need to know That grid_type_none = 0, grid_type_x = 1, and grid_type_o = 2. Return to CAPP. h and add the following content to the grid array:
Enum {
Grid_type_none = 0,
Grid_type_x,
Grid_type_o
};
Note: Once I mention that all the code in different files has been pasted up so far. From now on, I hope you can clear the code. Most of the time I will tell you where to add it, and sometimes I will display it all.
Now we have a method to implement a unit, and we need a method to reset the panel. Let's create a new function reset at the bottom of the CAPP. h.
Public:
Void reset ();
Open the CAPP. cpp and add the following functions before Main:
Void CAPP: reset (){
For (INT I = 0; I <9; I ++ ){
Grid [I] = grid_type_none;
}
}
This cycle sets all cells in the grid to grid_type_none, meaning that all units are empty. We need to do this at the beginning of each program load. All calls to this function in capp_oninit.cpp:
//...
Csurface: transparent (surf_x, 255, 0,255 );
Csurface: transparent (surf_o, 255, 0,255 );
Reset ();
Now everything is normal. Next we need to place X and O on the screen. Create a new function. Turn on the CAPP. h and add it after the Reset:
Void setcell (int id, int type );
Now, open Capp. cpp and add this function:
Void CAPP: setcell (int id, int type ){
If (ID <0 | ID> = 9) return;
If (type <0 | type> grid_type_o) return;
Grid [ID] = type;
}
This function has two parameters: the first is the ID of the unit to be changed, and the second is the type to be changed. We need to limit it here. First, make sure that the access array cannot be out of bounds (otherwise our program will crash), and then ensure that the input type is suitable. That's simple.
Now, let's implement a method for Drawing X and O. Open capp_onrender and add the following code after the grid:
For (INT I = 0; I <9; I ++ ){
Int x = (I % 3) * 200;
Int y = (I/3) * 200;
If (grid [I] = grid_type_x ){
Csurface: ondraw (surf_display, surf_x, x, y );
} Else
If (grid [I] = grid_type_o ){
Csurface: ondraw (surf_display, surf_o, x, y );
}
}
This is a little complicated than usual. First, we traverse every unit in the grid. Then we translate the ID of the grid into X and Y coordinates. We use two different methods to accomplish this. In order to get X, we let I get the remainder of 3. So when I is 0, we get 0, I is 1, we get 1, I is 2, we get 2, I is 3, we get 0, and so on. Since every one is 200*200, We Have To multiply it by 200. To obtain y, we divide it by 3, so that when I is 0, 1, 2, Y is 0, I is 3, 4, 5, Y is 1, and so on. Multiply the value by 200. I suggest you better figure out what is going on here, because this type of method is very common for brick-based games.
Next, we need to check the unit type and draw the above surface correctly.
Now we have drawn the surface, and we still need a human-computer interaction method. Here we use the mouse message. When you click a unit, the corresponding settings are made. We need to rewrite a function of cevent to implement this. Open the CAPP. h and add the following function after onevent and onexit:
Void onlbuttondown (int mx, int my );
Now open capp_onevent.cpp and add this function:
Void CAPP: onlbuttondown (int mx, int my ){
Int id = mx/200;
Id = ID + (my/200) * 3 );
If (grid [ID]! = Grid_type_none ){
Return;
}
If (currentplayer = 0 ){
Setcell (ID, grid_type_x );
Currentplayer = 1;
} Else {
Setcell (ID, grid_type_o );
Currentplayer = 0;
}
}
First, what we need to do is the opposite of converting X and Y into ID. This is to convert to ID. Then, make sure that the unit is not occupied. Otherwise, the function returns. Next, we will check which player is the turn, modify the unit accordingly, and take turns the players. Currentplayer is a new variable to specify who is in turn. We need to add it. Open the CAPP. h and add this variable under the grid array:
Int currentplayer;
Similarly, set the default value of this variable in the CAPP. cpp file:
Capp: CAPP (){
Currentplayer = 0;
Surf_grid = NULL;
Surf_x = NULL;
Surf_o = NULL;
Surf_display = NULL;
Running = true;
}
Compile it and you should be able to get a game that is basically running. Congratulations!
The next thing is yours. We already have a fairly stable foundation for developing our own games, and most of the work has been completed. I hope you can go further. Try to add "x wins", "O wins", and "Draw" them at the end of each game (additional images are needed here ). Think about how you can check who wins (is a function suitable to accomplish this purpose )? Try to reset the game after it is complete. If you have a good idea, you can try to add some common AI to compete with players. If you are brave enough, you can try to add a player to combat a player or a player to play a computer.
If you are ready and have mastered this lesson, continue to the next lesson to see frame animation.
SDL tutorial tic tac toe -- course file:
Win32: Zip, rar
Linux: Tar (thanks gaten)