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<vector<IMAGE>>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 < 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(&imgBgs[i], name);//Load 3 pictures into the image array bgX[i] = 0; } //Load Hero running picture frame material for (int i = 0; i < 12; i++) { sprintf(name, "res/hero%", i + 1); loadimage(&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(&imgTortoise, "res/"); //torToiseExist = false; //torToiseY = 345 - () + 5; IMAGE imgTort; loadimage(&imgTort, "res/"); vector<IMAGE>imgTortArray; imgTortArray.push_back(imgTort);//Add pictures obstacleImgs.push_back(imgTortArray); IMAGE imgLion; vector<IMAGE>imgLionArray; for (int i = 0; i < 6; i++) { sprintf(name, "res/p%", i + 1); loadimage(&imgLion, name); imgLionArray.push_back(imgLion); } obstacleImgs.push_back(imgLionArray); //Initialize the obstacle pool for (int i = 0; i < OBSTACLE_COUNT; i++) { obstacles[i].exist = false; } //Load squat material loadimage(&imgHeroDown[0], "res/"); loadimage(&imgHeroDown[1], "res/"); heroDown = false; //Picture of loading column IMAGE imgH; for (int i = 0; i < 4; i++) { vector<IMAGE> imgHookArray; sprintf(name, "res/h%", i + 1); loadimage(&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 < 10; i++) { sprintf(name, "res/sz/%", i); loadimage(&imgSZ[i], name); } } //Random creation of obstaclesvoid creatObstacle() { int i; for (i = 0; i < OBSTACLE_COUNT; i++) { if (obstacles[i].exist == false) { break; } } if (i >= 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 >= 0 && obstacles[lastObsIndex].type >= HOOK1 && obstacles[lastObsIndex].type <= HOOK4 && obstacles[i].type == LION && obstacles[lastObsIndex].x > 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 >= HOOK1 && obstacles[i].type <= HOOK4) { obstacles[i].speed = 0; obstacles[i].power = 20; obstacles[i].y = 0; } obstacles[i].passed = false; } void checkHit() { for(int i = 0; i < OBSTACLE_COUNT; i++) { if (obstacles[i].exist && 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 < 3; i++) { bgX[i] -= bgSpeed[i];//The speed of the three backgrounds is different if (bgX[i] < -WIN_WIDTH) { bgX[i] = 0; } } //Realize jump if (heroJump) { if (heroY < jumpHeightMax) //Achieving the maximum jump height, the jump offset is positive, jump downward { heroJumpOff = 4; } heroY += heroJumpOff; if (heroY > 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 >= delays[heroIndex]) { count = 0; heroIndex++; if (heroIndex >= 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 > 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 < -()) { // torToiseExist = false; // } //} //Update the coordinates of all obstacles for (int i = 0; i < OBSTACLE_COUNT; i++) { if (obstacles[i].exist) { obstacles[i].x -= obstacles[i].speed + bgSpeed[2]; if (obstacles[i].x < -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, &imgBgs[0]); putimagePNG2(bgX[1], 119, &imgBgs[1]); putimagePNG2(bgX[2], 330, &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, &imgTortoise); }*/ for (int i = 0; i < OBSTACLE_COUNT; i++) { if (obstacles[i].exist) { putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH, &obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]); } } } void updateHero() { if (!heroDown) { //Not squat - run and jump putimagePNG2(heroX, heroY, &imgHeros[heroIndex]); } else { int y = 345 - imgHeroDown[heroIndex].getheight(); putimagePNG2(heroX, y, &imgHeroDown[heroIndex]); } } void updateBloodBar() { drawBloodBar(10, 10, 200, 10, 2, BLUE, DARKGRAY, RED, heroBlood / 100.0); } void checkOver() { if (heroBlood <= 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 < OBSTACLE_COUNT; i++) { if (obstacles[i].exist && obstacles[i].passed == false && obstacles[i].x + obstacleImgs[obstacles[i].type][0].getwidth() < heroX && 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, &imgSZ[sz]); x += imgSZ[sz].getwidth() + 5; } } void checkWin() { if (score >= 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 > 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, &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 <> #include <> #include "" #include <> #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->getwidth(); //Get the width of the picture, EASYX comes with its own int picture_height = picture->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 < picture_height; iy++) { for (int ix = 0; ix < picture_width; ix++) { int srcX = ix + iy * picture_width; //The angle mark of pixels in the video memory int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA is transparency int sr = ((src[srcX] & 0xff0000) >> 16); //Get R in RGB int sg = ((src[srcX] & 0xff00) >> 8); //G int sb = src[srcX] & 0xff; //B if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight) { dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //The angle mark of pixels in the video memory int dr = ((dst[dstX] & 0xff0000) >> 16); int dg = ((dst[dstX] & 0xff00) >> 8); int db = dst[dstX] & 0xff; draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16) //Formula: Cp=αp*FP+(1-αp)*BP; αp=sa/255, FP=sr, BP=dr | ((sg * sa / 255 + dg * (255 - sa) / 255) << 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 < 0) { SetWorkingImage(picture); getimage(&imgTmp, 0, -y, picture->getwidth(), picture->getheight() + y); SetWorkingImage(); y = 0; picture = &imgTmp; } if (x < 0) { SetWorkingImage(picture); getimage(&imgTmp, -x, 0, picture->getwidth() + x, picture->getheight()); SetWorkingImage(); x = 0; picture = &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 < 0) { SetWorkingImage(picture); getimage(&imgTmp, 0, -y, picture->getwidth(), picture->getheight() + y); SetWorkingImage(); y = 0; picture = &imgTmp; } if (x < 0) { SetWorkingImage(picture); getimage(&imgTmp, -x, 0, picture->getwidth() + x, picture->getheight()); SetWorkingImage(); x = 0; picture = &imgTmp; } else if (x >= winWidth) { return; } else if (x > winWidth-picture->getwidth()) { SetWorkingImage(picture); getimage(&imgTmp, 0, 0, winWidth - x, picture->getheight()); SetWorkingImage(); picture = &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 <= x && zy <= 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(&lineStyle); int lineColor = getlinecolor(); int fileColor = getfillcolor(); if (percent < 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 > 0) { fillrectangle(x + 0.5 * lineWidth, y + lineWidth * 0.5, x + width * percent, y + height - 0.5 * lineWidth); } setlinecolor(lineColor); setfillcolor(fillColor); setlinestyle(&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!