| Games and SDL SDL Installation SDL for Embedded SDL API SDL Events | SDL Graphics SDL Threads Thread Example SDL Animation SDL Sound | Raw Video Player Video Formats Video Compression | Game Trees About The Author |
| 1 | 2 |
We have discussed in the previous sections how to do simple animations with a pre-created image that contains sequences of bitmaps; a few simple classes and a thread are used to achieve the effects. Here, we show you the complete code which is ready to compile and execute. The program "jewels.cpp" shown below is the complete code you need. You can copy-and-paste it into a file named "jewels.cpp" and compile it by the command:
To run it, you can simply execute
However, before you run the program, you have to create a subdirectory "images" and put the image files "jboard.jpg" and "jewels.png" in it.
/*
jewels.cpp : Demonstration of Animation using SDL
Compile: g++ -o jewels jewels.cpp -lSDL -lSDL_image
Execute: ./jewels
See http://www.webkinesia.com/games/sdl-animate.php
@Author: Fore June
*/
#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <deque>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <SDL/SDL_image.h>
using namespace std;
#define Location Point
#define JWIDTH 52
#define JHEIGHT 52
bool quit = false;
class Point {
public:
short x;
short y;
Point () { x = y = 0; }
Point ( short x0, short y0 ) { x = x0; y = y0; }
};
Point point_1 ( -1, -1 );
class Jewel {
public:
short jType; //type of jewel
Point dest; //position to display jewel
};
//The Jewel Board
class JBoard {
public:
Jewel jewels[8][8]; //jewels on the board
Point jSource[7]; //source locations of various jewels
SDL_Surface *image; //source image of all jewels
SDL_Surface *imageMain; //source image of main board
SDL_Surface *screen; //destination for displaying images
short width; //size of jewel ( all the same )
short height;
JBoard( SDL_Surface *scr ); //constructor
bool init(); //initialize the board
short x2Col ( short x ); //find col # corresponding to x
short y2Row ( short y ); //find row # corresponding to y
Location jewelLocation ( Point p ); //find ( col#, row# ) of Point p
short neighbour ( Location m, Location n ); //determine if m, n are neighbours
bool swapJewels ( Location a, Location b ); //swap two adjacent jewels
};
//create the board
JBoard::JBoard( SDL_Surface *scr )
{
SDL_Rect source, dest;
screen = scr;
imageMain = IMG_Load ( "images/jboard.jpg" ); //source image of main board
if ( imageMain == NULL ) {
cout << "Unable to load image\n";
return;
}
image = IMG_Load ( "images/jewels.png" ); //source image of main board
if ( image == NULL ) {
cout << "Unable to load image\n";
return;
}
/*
remember where to get the jewels
totally there are 7 different kind of jewels
*/
short x, y, i, j, k;
width = 48; //size of jewel
height = 48;
x = 3; //location of first jewel
y = 3;
for ( i = 0; i < 7; ++i ) {
jSource[i] = Point ( x, y );
y += 104; //location of next jewel, same x, different y
if ( i == 4 || i == 5 ) y -= 1; //make small adjustments in positions
}
/*
remember where to put the jewels
starting position is ( 20, 204 )
*/
y = 20; //starting y position
for ( i = 0; i < 8; ++i ) {
x = 204;
for ( j = 0; j < 8 ; ++j ) {
jewels[i][j].dest = Point ( x, y );
x += 51; //next jewel
}
y += 51; //next row
if (i == 2) y-=1; //make minor adjustment
}
} //JBoard()
bool JBoard::init()
{
short k;
SDL_Rect source, dest;
source.x = 0;
source.y = 0;
source.w = imageMain->w;
source.h = imageMain->h;
dest.x = dest.y = 0;
SDL_BlitSurface ( imageMain, &source, screen, &dest );
source.w = width; //all jewels have same size
source.h = height;
//now populate the board randomly with jewels
srand ( ( unsigned )time ( NULL ) ); //random seed
for ( short i = 0; i < 8; ++i ) {
for ( short j = 0; j < 8; ++j ) {
k = rand() % 7;
jewels[i][j].jType = k;
source.x = jSource[k].x;
source.y = jSource[k].y;
dest.x = jewels[i][j].dest.x;
dest.y = jewels[i][j].dest.y;
SDL_BlitSurface ( image, &source, screen, &dest );
}
}
if( SDL_Flip( screen ) == -1 )
return false;
return true;
}
//find the column # corresponding to x position
short JBoard::x2Col ( short x )
{
if ( x < jewels[0][0].dest.x || x > ( jewels[0][7].dest.x + JWIDTH ) )
return -1;
short i;
for ( i = 1; i < 8; ++i ) //simple linear search
if ( x < jewels[0][i].dest.x ) break;
return i - 1;
}
//find the row # corresponding to y position
short JBoard::y2Row ( short y )
{
if ( y < jewels[0][0].dest.y || y > ( jewels[7][0].dest.y + JHEIGHT ))
return -1;
short i;
for ( i = 1; i < 8; ++i ) //simple linear
if ( y < jewels[i][0].dest.y ) break;
return i - 1;
}
Location JBoard::jewelLocation ( Point p )
{
short i, j;
i = x2Col ( p.x );
if ( i < 0 ) return point_1;
j = y2Row ( p.y );
if ( j < 0 ) return point_1;
return Location ( i, j );
}
short JBoard::neighbour ( Location m, Location n )
{
if ( (m.y - n.y) == 0 ) { //same row
if ( abs ( m.x - n.x ) == 1 ) //neighbouring cols
return 1;
} else if ( ( m.x - n.x ) == 0 ) { //same col
if ( abs ( m.y - n.y ) == 1 ) //neighbouring rows
return 0;
}
return -1; //non neighbours
}
/*
Swap adjacent jewels.
*/
bool JBoard::swapJewels ( Location m, Location n )
{
short adj_col = neighbour ( m, n ); //0 => adj. rows, 1 => adj. cols, -1 => non-neighbours
if ( adj_col < 0 ) //m, n are not neighbours
return false;
SDL_Rect source_m, source_n, dest_m, dest_n, source0, source1, dest0, dest1;
short i, j, k, update_w, update_h;
if ( adj_col ) {
k = m.x - n.x;
update_w = JWIDTH * 2;
update_h = JHEIGHT;
} else {
k = m.y - n.y;
update_w = JWIDTH;
update_h = JHEIGHT * 2;
}
//Ensure m is on left or top of n
if ( k > 0 ) {
Location temp = m; //swap m, n
m = n;
n = temp;
}
dest_n.w = dest_m.w = source_n.w = source_m.w = width;
dest_n.h = dest_m.h = source_n.h = source_m.h = height;
i = m.y; j = m.x;
dest_m.x = jewels[i][j].dest.x;
dest_m.y = jewels[i][j].dest.y;
k = jewels[i][j].jType;
source_m.y = jSource[k].y;
source_m.x = jSource[k].x;
i = n.y; j = n.x;
dest_n.x = jewels[i][j].dest.x;
dest_n.y = jewels[i][j].dest.y;
k = jewels[i][j].jType;
source_n.y = jSource[k].y;
source_n.x = jSource[k].x;
short d; //distance two jewels need to travel in swapping
if ( adj_col )
d = jewels[n.y][n.x].dest.x - dest_m.x;
else
d = jewels[n.y][n.x].dest.y - dest_m.y;
source0.x = dest0.x = dest_m.x - 2; //backgrounds
source0.y = dest0.y = dest_m.y - 2;
source1.x = dest1.x = dest_n.x - 2;
source1.y = dest1.y = dest_n.y - 2;
source0.w = source1.w = JWIDTH;
source0.h = source1.h = JHEIGHT;
for ( int r = 0; r < d; ++r ) {
SDL_BlitSurface ( imageMain, &source0, screen, &dest0 ); //first jewel background
SDL_BlitSurface ( imageMain, &source1, screen, &dest1 ); //second jewel background
SDL_BlitSurface ( image, &source_m, screen, &dest_m ); //first jewel image
SDL_BlitSurface ( image, &source_n, screen, &dest_n ); //second jewel image
SDL_UpdateRect( screen, source0.x, source0.y, update_w, update_h ); //update affected region
if ( adj_col ) {
dest_m.x += 1; //first jewel goes right
dest_n.x -= 1; //second jewel goes left
} else {
dest_m.y += 1; //first jewel goes down
dest_n.y -= 1; //second jewel goes up
}
SDL_Delay ( 5 );
}
//swap the states of the jewels
k = jewels[m.y][m.x].jType;
jewels[m.y][m.x].jType = jewels[n.y][n.x].jType;
jewels[n.y][n.x].jType = k;
} //swapJewels
class JMouse {
private:
short x;
short y;
public:
JBoard *jBoard;
JMouse ( JBoard *jb );
void doJewel( Point p, short action ); //rotate or flash jewel
};
JMouse::JMouse ( JBoard *jb )
{
jBoard = jb;
x = y = 0;
}
/*
Rotate or flash the jewel located at p. If p is outside board, returns.
action = 0 => flash, action = 1 => rotate
*/
void JMouse::doJewel ( Point p, short action )
{
if ( action < 0 ) return;
Location loc = jBoard->jewelLocation ( p );
if ( loc.x < 0 ) {
cout << "outside board " << endl;
return;
}
short i, j, k;
i = loc.y; //i, j roles are reversed here
j = loc.x;
k = (jBoard->jewels[i][j]).jType;
SDL_Rect source, dest;
dest.w = source.w = jBoard->width;
dest.h = source.h = jBoard->height;
dest.x = jBoard->jewels[i][j].dest.x;
dest.y = jBoard->jewels[i][j].dest.y;
source.y = jBoard->jSource[k].y; //source points to row for rotation
if ( action == 0 ) //point to next row which is for flashing
source.y += JHEIGHT;
source.x = jBoard->jSource[k].x;
for ( int i = 0; i < 15; ++i ) {
SDL_BlitSurface ( jBoard->imageMain, &dest, jBoard->screen, &dest ); //background
SDL_BlitSurface ( jBoard->image, &source, jBoard->screen, &dest ); //image
SDL_UpdateRect( jBoard->screen, dest.x, dest.y, JWIDTH, JHEIGHT );
SDL_Delay ( 30 );
source.x += JWIDTH; //next image
}
}
deque<Point> mouse_down_queue;
int mouse_thread ( void *jmouse )
{
JMouse *jm = ( JMouse *) jmouse;
short action = -1; //no action at beginning
short nevent = 0;
Point p;
Location m, n;
while ( !quit ) {
if ( mouse_down_queue.size() > 0 ) {
m = n;
p = mouse_down_queue.front();
n = jm->jBoard->jewelLocation( p );
mouse_down_queue.pop_front();
action = rand() % 2; //randomly set for rotation (1) or flashing (0)
++nevent;
}
if ( nevent == 2 ) {
if ( jm->jBoard->neighbour ( m, n ) >= 0 ) {
jm->jBoard->swapJewels ( m, n ); //swap two jewels
action = -1;
nevent = 0;
} else
--nevent;
} else
jm->doJewel ( p, action ); //rotate or flash
}
return 0;
}
int main()
{
#ifndef ARM
const int VWIDTH = 640;
const int VHEIGHT = 480;
#else
const int VWIDTH = 320;
const int VHEIGHT = 240;
#endif
SDL_Surface *screen;
SDL_Event event;
/* Fire up SDL. */
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0) {
printf("Error initializing SDL: %s\n", SDL_GetError());
return 1;
}
atexit(SDL_Quit);
/* Set a video mode. Force 16-bit hicolor. */
if ((screen = SDL_SetVideoMode(VWIDTH, VHEIGHT, 16, 0)) == NULL) {
printf("Error setting a video, 16-bit video mode: %s\n", SDL_GetError());
return 1;
}
JBoard jb ( screen );
jb.init();
Location n ( 3, 4), m ( 4, 4 );
jb.swapJewels ( m, n );
n = Location ( 4, 3 );
m = Location ( 4, 4 );
jb.swapJewels ( m, n );
m = Location ( 4, 5 );
n = Location ( 5, 5 );
jb.swapJewels ( m, n );
m = Location ( 2, 4 );
n = Location ( 2, 5 );
jb.swapJewels ( m, n );
Point p;
JMouse jm ( &jb );
SDL_Thread *mthread;
mthread = SDL_CreateThread( mouse_thread, &jm );
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
p = Point ( event.button.x, event.button.y );
mouse_down_queue.push_back ( p );
break;
case SDL_KEYUP:
if (event.key.keysym.sym == SDLK_ESCAPE) {
SDL_Quit();
quit = true;
}
break;
}
}
SDL_WaitThread( mthread, NULL );
return 0;
}
|