SoFunction
Updated on 2025-03-10

C++ uses easyx graphics library to realize creative cool run games every day

Cool run every day

This is the first time Siren has written a mini game based on the "easyx" graphics library (need to download easyx). If you want to learn how to use various graphics libraries or other engines to write your own mini-games, you can read this article carefully and learn the basics.

Development Log

1. Create a project

2. Import material

3. Create a game interface

Start with the user interface

Select a graphics library or other engine. Cool Run is based on the "easyx" graphics library.

1) Create a game window

2) Design game background

a. Triple background moves at different speeds

b. Implementation of loop scrolling background

3) Realize the game background

a. Loading background resources

b. Rendering (to realize the effect of printing pictures) Background knowledge: coordinates

Problem encountered: PNG format image in background image appears black

4. Realize player running

5. Realize player jumps

6. Implement the random turtle

7. Create structure structure type

8. Reinitialize after using the obstacle structure

9. Display of multiple obstacles after packaging

10. Realize the player's squat skills

11. Add column obstacles

Code implementation


//#undef UNICODE
//#undef _UNICODE

#include <>
#include <>
#include <>
#include <vector> //c++ Array with variable length#include ""

using namespace std; //Declare the namespace#define WIN_SCORE 10

#define WIN_WIDTH 1012 //Define macros for post-maintenance and processing#define WIN_HEIGHT 396
#define OBSTACLE_COUNT 10


IMAGE imgBgs[3];//Background picture - create an array of picturesint bgX[3];//X coordinates of background image (constantly changing)int bgSpeed[3] = { 1,2,4 };//Control 3 backgrounds with different speeds
IMAGE imgHeros[12];//The realization of the characters running continuouslyint heroX;//The player's X coordinateint heroY;//The player's Y coordinateint heroIndex;//The picture frame number of the player running
bool heroJump;// means that the player is jumpingint jumpHeightMax;//The maximum height of the jumpint heroJumpOff;//Jump offsetint update;//Indicate whether the screen needs to be refreshed immediately
//IMAGE imgTortoise; //Little turtle//int torToiseX; //The horizontal coordinates of the little turtle//int torToiseY; //The vertical coordinates of the little turtle//bool torToiseExist; //Is there a small tortoise in the current window?
int heroBlood; //Define the player's healthint score;

typedef enum {
	TORTOISE, //Turtle 0	LION, //Lion 1	HOOK1,
	HOOK2,
	HOOK3,
	HOOK4,
	OBSTACLE_TYPE_COUNT //Border 6}obstacle_type;

// Equivalent to IMAGE obstacleImgs[3][5]vector&lt;vector&lt;IMAGE&gt;&gt;obstacleImgs; //Two-dimensional array stores various pictures of all obstacles
typedef struct obstacle {
	int type; //Type of obstacle	int imgIndex; //The serial number of the currently displayed picture	int x, y; //The coordinates of the obstacle	int speed;
	int power; //Cause	bool exist;
	bool hited;  //Indicates whether there has been a collision	bool passed;// Indicates whether it has been passed}obstacle_t;

obstacle_t obstacles[OBSTACLE_COUNT];
int lastObsIndex;//Solve obstacle bugs (the pillar is with the lion)
IMAGE imgHeroDown[2];
bool heroDown; //Indicate whether the player is in a squat state
IMAGE imgSZ[10];

//Initialization of the gamevoid init() {
	//Create a game window	initgraph(WIN_WIDTH, WIN_HEIGHT, EW_SHOWCONSOLE);

	//Load background resources	char name[64];
	for (int i = 0; i &lt; 3; i++)
	{
		//Path "res/" "res/" "res/"		sprintf(name, "res/bg%",i+1);//%03d occupies 3 digits, less than 3 digits will automatically make up for 0		//#undef _UNICODE  
		loadimage(&amp;imgBgs[i], name);//Load 3 pictures into the image array
		bgX[i] = 0;
	}

	//Load Hero running picture frame material	for (int i = 0; i &lt; 12; i++) {
		sprintf(name, "res/hero%", i + 1);
		loadimage(&amp;imgHeros[i], name);
	}
	 
	//Set the initial position of the player	heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;//X coordinates: half of the screen width minus half of the character width	heroY = 345 - imgHeros[0].getheight();//Y coordinate: Sole coordinate minus the character height	heroIndex = 0;

	heroJump = false;
	jumpHeightMax = 345 - imgHeros[0].getheight() - 120;
	heroJumpOff = -4;

	update = true;

	Loading small turtle material
	//loadimage(&amp;imgTortoise, "res/");
	//torToiseExist = false;
	//torToiseY = 345 - () + 5;
	IMAGE imgTort;
	loadimage(&amp;imgTort, "res/");
	vector&lt;IMAGE&gt;imgTortArray;
	imgTortArray.push_back(imgTort);//Add pictures	obstacleImgs.push_back(imgTortArray);

	IMAGE imgLion;
	vector&lt;IMAGE&gt;imgLionArray;
	for (int i = 0; i &lt; 6; i++) {
		sprintf(name, "res/p%", i + 1);
		loadimage(&amp;imgLion, name);
		imgLionArray.push_back(imgLion);
	}
	obstacleImgs.push_back(imgLionArray);
	
	//Initialize the obstacle pool	for (int i = 0; i &lt; OBSTACLE_COUNT; i++) {
		obstacles[i].exist = false;
	}

	//Load squat material	loadimage(&amp;imgHeroDown[0], "res/");
	loadimage(&amp;imgHeroDown[1], "res/");
	heroDown = false;

	//Picture of loading column	IMAGE imgH;
	for (int i = 0; i &lt; 4; i++) {
		vector&lt;IMAGE&gt; imgHookArray;
		sprintf(name, "res/h%", i + 1);
		loadimage(&amp;imgH, name, 63, 250, true); // Shrink the picture		imgHookArray.push_back(imgH);
		obstacleImgs.push_back(imgHookArray);
	}

	heroBlood = 100;

	//Preload sound effects	preLoadSound("res/hit.mp3");
	//Background music	mciSendString("play res/bg.mp3 repeat", 0, 0, 0);

	lastObsIndex = -1;
	score = 0;

	//Load digital pictures	for (int i = 0; i &lt; 10; i++) {
		sprintf(name, "res/sz/%", i);
		loadimage(&amp;imgSZ[i], name);
	}
}

//Random creation of obstaclesvoid creatObstacle() {
	int i;
	for (i = 0; i &lt; OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist == false) {
			break;
		}
	}
	if (i &gt;= OBSTACLE_COUNT) {
		return;
	}

	obstacles[i].exist = true;
	obstacles[i].hited = false;
	obstacles[i].imgIndex = 0;
	//obstacles[i].type = (obstacle_type)(rand() % OBSTACLE_TYPE_COUNT);
	obstacles[i].type = (obstacle_type)(rand() % 3);

	//If the previous obstacle is a pillar and the next one is a lion, judge the distance. If it is very close, the lion will be replaced with a turtle	if (lastObsIndex &gt;= 0 &amp;&amp; obstacles[lastObsIndex].type &gt;= HOOK1 &amp;&amp; obstacles[lastObsIndex].type &lt;= HOOK4 &amp;&amp; obstacles[i].type == LION &amp;&amp; obstacles[lastObsIndex].x &gt; WIN_WIDTH - 500) 
	{
		obstacles[i].type == TORTOISE;
	}
	lastObsIndex = i;

	if (obstacles[i].type == HOOK1) { //Reduce the frequency of column appearance		obstacles[i].type += rand() % 4; //0-3
	}

	obstacles[i].x = WIN_WIDTH;
	obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();
	if (obstacles[i].type == TORTOISE) {
		obstacles[i].speed = 0;
		obstacles[i].power = 5; //random	}
	else if (obstacles[i].type == LION) {
		obstacles[i].speed = 1;
		obstacles[i].power = 20;
	}
	else if (obstacles[i].type &gt;= HOOK1 &amp;&amp; obstacles[i].type &lt;= HOOK4) {
		obstacles[i].speed = 0;
		obstacles[i].power = 20;
		obstacles[i].y = 0;
	}

	obstacles[i].passed = false;
}

void checkHit() {
	for(int i = 0; i &lt; OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist &amp;&amp; obstacles[i].hited == false) {
			int a1x, a1y, a2x, a2y;
			int off = 30;
			if (!heroDown) { //Non-squat Run Jump				a1x = heroX + off;
				a1y = heroY + off;
				a2x = heroX + imgHeros[heroIndex].getwidth() - off;
				a2y = heroY + imgHeros[heroIndex].getheight();
			}
			else {
				a1x = heroX + off;
				a1y = 345 - imgHeroDown[heroIndex].getheight();
				a2x = heroX + imgHeroDown[heroIndex].getwidth() - off;
				a2y = 345;
			}

			IMAGE img = obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]; //The current obstacle type (that picture)			int b1x = obstacles[i].x + off;
			int b1y = obstacles[i].y + off;
			int b2x = obstacles[i].x + () - off;
			int b2y = obstacles[i].y + () - 10;

			if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {
				heroBlood -= obstacles[i].power;
				printf("Health Remaining %d\n", heroBlood);
				playSound("res/hit.mp3");
				obstacles[i].hited = true;
			}
		}
	}
}


//Let the background movevoid run() {
	for (int i = 0; i &lt; 3; i++) {
		bgX[i] -= bgSpeed[i];//The speed of the three backgrounds is different		if (bgX[i] &lt; -WIN_WIDTH) {
			bgX[i] = 0;
		}
	}

	//Realize jump	if (heroJump) 
	{
		if (heroY &lt; jumpHeightMax) //Achieving the maximum jump height, the jump offset is positive, jump downward		{ 
			heroJumpOff = 4;
		}

		heroY += heroJumpOff;

		if (heroY &gt; 345 - imgHeros[0].getheight()) //Arriving at the ground Jumping ends		{  
			heroJump = false;
			heroJumpOff = -4; // Offset initialization		}
	}
	else if (heroDown) { //The character squats down		static int count = 0;
		int delays[2] = { 8,30 }; //Set the squat time differently		count++;
		if (count &gt;= delays[heroIndex]) {
			count = 0;
			heroIndex++;
			if (heroIndex &gt;= 2) {
				heroIndex = 0;
				heroDown = false;
			}
		}
	}
	else{ //No jump		heroIndex = (heroIndex + 1) % 12; //12 pictures are played in a loop to complete a series of actions	}

	//Create obstacles	static int frameCount = 0;
	static int enemyFre = 50;
	frameCount++;
	if (frameCount &gt; enemyFre){
		frameCount = 0;
		//if (!torToiseExist) { //Avoid multiple turtles appearing at the same time on the screen		//	torToiseExist = true;
		//	torToiseX = WIN_WIDTH;	
		// enemyFre=rand()%301+200; //A random turtle appears every 200-500 frames		//}
		enemyFre = rand() % 50 + 50;
		creatObstacle();
	}

	//if (torToiseExist) {
	// // Update the location of the little turtle	//	torToiseX -= bgSpeed[2];
	//	if (torToiseX &lt; -()) {
	//		torToiseExist = false;
	//	}
	//}
	
	//Update the coordinates of all obstacles	for (int i = 0; i &lt; OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist) {
			obstacles[i].x -= obstacles[i].speed + bgSpeed[2];
			if (obstacles[i].x &lt; -obstacleImgs[obstacles[i].type][0].getwidth() * 2) {
				obstacles[i].exist = false;
			}
			int len = obstacleImgs[obstacles[i].type].size();
			obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;
		}
	}

	// "collision detection" processing of players and obstacles	checkHit();

}

// Render "Game Background"void updateBg() {
	//Adjust the background image position	putimagePNG2(bgX[0], 0, &amp;imgBgs[0]);
	putimagePNG2(bgX[1], 119, &amp;imgBgs[1]);
	putimagePNG2(bgX[2], 330, &amp;imgBgs[2]);

}

//Realize jumpvoid jump() {
	heroJump = true;
	update = true; //You can jump when it is not refreshed}

void down() {
	update = true;
	heroDown = true;
	heroIndex = 0;
}

// Process the input of user keysvoid keyEvent() {

	//char c;
	//scanf("%c", &c); will directly block the execution of the program
	if (GetAsyncKeyState(VK_UP)){ //Virtual key		jump();
		/*
			 if(kbhit()) //kbhit() determines whether there is keyboard input.  If a key is pressed, kbhit() returns to TRUE
			 {
				 char ch = _getch();//No need to press Enter to read it directly
				 if (ch == ' ') {//Press space to jump
					 jump();
			 }
		 */
	}

	else if (GetAsyncKeyState(VK_DOWN)) {
		down();
	}
}

void updateEnemy() {
	// Render the little turtle	/*if (torToiseExist) {
		putimagePNG2(torToiseX, torToiseY, WIN_WIDTH, &amp;imgTortoise);
	}*/
	for (int i = 0; i &lt; OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist) {
			putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH, &amp;obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]);
		}
	}
}

void updateHero() {
	if (!heroDown) { //Not squat - run and jump		putimagePNG2(heroX, heroY, &amp;imgHeros[heroIndex]);
	}
	else {
		int y = 345 - imgHeroDown[heroIndex].getheight();
		putimagePNG2(heroX, y, &amp;imgHeroDown[heroIndex]);
	}
}
	
void updateBloodBar()
{
	drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBlood / 100.0);
}

void checkOver() {
	if (heroBlood &lt;= 0) {
		loadimage(0, "res/");
		FlushBatchDraw();//refresh		mciSendString("stop res / bg.mp3", 0, 0, 0);// Turn off the background music		system("pause");

		//After the pause, recharge the coin and resurrect it or start the next game directly		heroBlood = 100;
		score = 0;
		mciSendString("play res / bg.mp3 repeat", 0, 0, 0);
	}
}

void checkScore() {
	for (int i = 0; i &lt; OBSTACLE_COUNT; i++) {
		if (obstacles[i].exist &amp;&amp; obstacles[i].passed == false &amp;&amp; 
			obstacles[i].x + obstacleImgs[obstacles[i].type][0].getwidth() &lt; heroX &amp;&amp; obstacles[i].hited == false) 
		{
			score++; 
			obstacles[i].passed = true;
			printf("score:%d\n", score);
		}
	}
}

void updateScore() {
	char str[8];
	sprintf(str, "%d", score);

	int x = 20;
	int y = 25;

	for (int i = 0; str[i]; i++) {
		int sz = str[i] - '0';
		putimagePNG(x, y, &amp;imgSZ[sz]);
		x += imgSZ[sz].getwidth() + 5;
	}
}

void checkWin() {
	if (score &gt;= WIN_SCORE) {
		FlushBatchDraw();
		mciSendString("play res/win.mp3", 0, 0, 0);
		Sleep(2000);
		loadimage(0, "res/");
		FlushBatchDraw();
		mciSendString("stop res/bg.mp3", 0, 0, 0);
		system("pause");

		heroBlood = 100;
		score = 0;
		mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
	}
}

int main(void)
{
	init();

	//Show the initialization surface	loadimage(0, "res/");
	system("pause");

	int timer = 0;
	while (1) {
		keyEvent();
		timer += getDelay();//This function returns the time from the last call interval, and the first time it returns 0		if (timer &gt; 30) { //30ms refresh time			timer = 0;
			update = true;
		}
		if (update) {
			update = false;
			BeginBatchDraw();//This function is used to start batch drawing.  After execution, any drawing operations will not be output to the drawing window for the time being, and the previous drawing will not be output until FlushBatchDraw or EndBatchDraw is executed.			updateBg();
			//putimagePNG2(heroX, heroY, &amp;imgHeros[heroIndex]);
			updateHero();
			updateEnemy();
			updateBloodBar();
			updateScore();
			checkWin();
			EndBatchDraw();//This function is used to end batch drawing and execute unfinished drawing tasks.    These two functions are mainly intended to eliminate flickering.
			checkOver();
			checkScore();
			
			run();
		}
		

		//Sleep(30); //Sleep	}

	system("pause");
	return 0;
}

## tool header file```javascript
#include &lt;&gt;
#include &lt;&gt;
#include ""

#include &lt;&gt;
#pragma comment(lib, "")


int getDelay() {
    static unsigned long long lastTime = 0;
    unsigned long long currentTime = GetTickCount();
    if (lastTime == 0) {
        lastTime = currentTime;
        return 0;
    }
    else {
        int ret = currentTime - lastTime;
        lastTime = currentTime;
        return ret;
    }
}

// Load the PNG image and remove the transparent partvoid putimagePNG(int  picture_x, int picture_y, IMAGE* picture) //x is the X coordinate of the loading picture, and y is the Y coordinate{
    DWORD* dst = GetImageBuffer();    // GetImageBuffer() function is used to obtain the video memory pointer of the drawing device. EASYX comes with its own    DWORD* draw = GetImageBuffer();
    DWORD* src = GetImageBuffer(picture); //Get picture memory pointer    int picture_width = picture-&gt;getwidth(); //Get the width of the picture, EASYX comes with its own    int picture_height = picture-&gt;getheight(); //Get the height of the picture, EASYX comes with its own    int graphWidth = getwidth();       //Get the width of the drawing area, EASYX comes with its own    int graphHeight = getheight();     //Get the height of the drawing area, EASYX comes with its own    int dstX = 0;    //The angle mark of pixels in the video memory
    // Implement transparent maps Formula: Cp=αp*FP+(1-αp)*BP, Bayes theorem to calculate the probability of point color    for (int iy = 0; iy &lt; picture_height; iy++)
    {
        for (int ix = 0; ix &lt; picture_width; ix++)
        {
            int srcX = ix + iy * picture_width; //The angle mark of pixels in the video memory            int sa = ((src[srcX] &amp; 0xff000000) &gt;&gt; 24); //0xAArrggbb;AA is transparency            int sr = ((src[srcX] &amp; 0xff0000) &gt;&gt; 16); //Get R in RGB            int sg = ((src[srcX] &amp; 0xff00) &gt;&gt; 8);   //G
            int sb = src[srcX] &amp; 0xff;              //B
            if (ix &gt;= 0 &amp;&amp; ix &lt;= graphWidth &amp;&amp; iy &gt;= 0 &amp;&amp; iy &lt;= graphHeight &amp;&amp; dstX &lt;= graphWidth * graphHeight)
            {
                dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //The angle mark of pixels in the video memory                int dr = ((dst[dstX] &amp; 0xff0000) &gt;&gt; 16);
                int dg = ((dst[dstX] &amp; 0xff00) &gt;&gt; 8);
                int db = dst[dstX] &amp; 0xff;
                draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) &lt;&lt; 16)  //Formula: Cp=αp*FP+(1-αp)*BP; αp=sa/255, FP=sr, BP=dr                    | ((sg * sa / 255 + dg * (255 - sa) / 255) &lt;&lt; 8)         //αp=sa/255 , FP=sg , BP=dg
                    | (sb * sa / 255 + db * (255 - sa) / 255);              //αp=sa/255 , FP=sb , BP=db
            }
        }
    }
}

// Applicable for any situations where y <0 and x <0void putimagePNG2(int x, int y, IMAGE* picture) {
    IMAGE imgTmp;
    if (y &lt; 0) {
        SetWorkingImage(picture);
        getimage(&amp;imgTmp, 0, -y,
            picture-&gt;getwidth(), picture-&gt;getheight() + y);
        SetWorkingImage();
        y = 0;
        picture = &amp;imgTmp;
    }

    if (x &lt; 0) {
        SetWorkingImage(picture);
        getimage(&amp;imgTmp, -x, 0, picture-&gt;getwidth() + x, picture-&gt;getheight());
        SetWorkingImage();
        x = 0;
        picture = &amp;imgTmp;
    } 

    putimagePNG(x, y, picture);
}

// Applicable for any situations where y <0 and y>0void putimagePNG2(int x, int y, int winWidth, IMAGE* picture) {
    IMAGE imgTmp;
    if (y &lt; 0) {
        SetWorkingImage(picture);
        getimage(&amp;imgTmp, 0, -y,
            picture-&gt;getwidth(), picture-&gt;getheight() + y);
        SetWorkingImage();
        y = 0;
        picture = &amp;imgTmp;
    }

    if (x &lt; 0) {
        SetWorkingImage(picture);
        getimage(&amp;imgTmp, -x, 0, picture-&gt;getwidth() + x, picture-&gt;getheight());
        SetWorkingImage();
        x = 0;
        picture = &amp;imgTmp;
    }
    else if (x &gt;= winWidth) {
        return;
    }
    else if (x &gt; winWidth-picture-&gt;getwidth()) {
        SetWorkingImage(picture);
        getimage(&amp;imgTmp, 0, 0, winWidth - x, picture-&gt;getheight());
        SetWorkingImage();
        picture = &amp;imgTmp;
    }

    putimagePNG(x, y, picture);
}

//Suppose A[x01,y01,x02,y02] B[x11,y11,x12,y12].bool rectIntersect(int x01, int y01, int x02, int y02,
    int x11, int y11, int x12, int y12)
{
    int zx = abs(x01 + x02 - x11 - x12);
    int x = abs(x01 - x02) + abs(x11 - x12);
    int zy = abs(y01 + y02 - y11 - y12);
    int y = abs(y01 - y02) + abs(y11 - y12);
    return  (zx &lt;= x &amp;&amp; zy &lt;= y);
}

void  preLoadSound(const char* name) {
    char cmd[512];
    sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
    mciSendString(cmd, 0, 0, 0);
    sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
    mciSendString(cmd, 0, 0, 0);
}

void  playSound(const char* name) {
    static int index = 1;
    char cmd[512];

    if (index == 1) {
        sprintf_s(cmd, sizeof(cmd), "play %s-1", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "close %s-2", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "open %s alias %s-2", name, name);
        mciSendString(cmd, 0, 0, 0);
        index++;
    }
    else if (index == 2) {
        sprintf_s(cmd, sizeof(cmd), "play %s-2", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "close %s-1", name);
        mciSendString(cmd, 0, 0, 0);
        sprintf_s(cmd, sizeof(cmd), "open %s alias %s-1", name, name);
        mciSendString(cmd, 0, 0, 0);
        index = 1;
    }
}

void drawBloodBar(int x, int y, int width, int height, int lineWidth, int boardColor, int emptyColor, int fillColor, float percent) {
    LINESTYLE lineStyle;
    getlinestyle(&amp;lineStyle);
    int lineColor = getlinecolor();
    int fileColor = getfillcolor();

    if (percent &lt; 0) {
        percent = 0;
    }

    setlinecolor(BLUE);
    setlinestyle(PS_SOLID | PS_ENDCAP_ROUND, lineWidth);
    setfillcolor(emptyColor);
    fillrectangle(x, y, x + width, y + height);
    setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 0);
    setfillcolor(fillColor);
    setlinecolor(fillColor);
    if (percent &gt; 0) {
        fillrectangle(x + 0.5 * lineWidth, y + lineWidth * 0.5, x + width * percent, y + height - 0.5 * lineWidth);
    }
    
    setlinecolor(lineColor);
    setfillcolor(fillColor);
    setlinestyle(&amp;lineStyle);
}

The above is the detailed content of C++ using easyx graphics library to achieve creative daily cool run games. For more information about C++ easyx cool run games, please follow my other related articles!