1.代码介绍
1.游戏背景的设计
2.实现玩家奔跑
3.实现玩家跳跃
4.优化帧等待
5.使用结构体封装障碍物
6.封装后障碍物的初始化
7.显示多个障碍物
8.实现玩家下蹲技能
9.添加柱子障碍物
10.优化障碍物的出现频率
11.实现碰撞检测
12.添加音效,添加血条
13.判断游戏结束,添加初始化界面
14.解决死亡障碍
15.通过障碍物后自动加分
16.判断游戏胜利
相关素材可以上网查找,也可以私聊我!
2.运行结果展示
3.项目代码
1.tools.h
#pragma once
#include <graphics.h>
//返回距离上一次调用间隔的时间(单位:ms),第一次调用时返回0
int getDelay();
void putimagePNG(int picture_x, int picture_y, IMAGE* picture);
// 适用于 y <0 以及y>0的任何情况
void putimagePNG2(int x, int y, IMAGE* picture);
void putimagePNG2(int x, int y, int winWidth, IMAGE* picture);
// 判断两个矩形是否相交
bool rectIntersect(int a1X, int a1Y, int a2X, int a2Y,
int b1X, int b1Y, int b2X, int b2Y);
void preLoadSound(const char* name);
void playSound(const char* name);
void drawBloodBar(int x, int y, int width, int height, int lineWidth, int boardColor, int emptyColor, int fillColor, float percent);
2.easyx.h
#pragma once
#ifndef WINVER
#define WINVER 0x0400 // Specifies that the minimum required platform is Windows 95 and Windows NT 4.0.
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500 // Specifies that the minimum required platform is Windows 2000
#endif
#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0410 // Specifies that the minimum required platform is Windows 98
#endif
#ifdef UNICODE
#pragma comment(lib,"EasyXw.lib")
#else
#pragma comment(lib,"EasyXa.lib")
#endif
#ifndef __cplusplus
#error EasyX is only for C++
#endif
#include <windows.h>
#include <tchar.h>
// EasyX Window Properties
#define EX_SHOWCONSOLE 1 // Maintain the console window when creating a graphics window
#define EX_NOCLOSE 2 // Disable the close button
#define EX_NOMINIMIZE 4 // Disable the minimize button
#define EX_DBLCLKS 8 // Support double-click events
// Color constant
#define BLACK 0
#define BLUE 0xAA0000
#define GREEN 0x00AA00
#define CYAN 0xAAAA00
#define RED 0x0000AA
#define MAGENTA 0xAA00AA
#define BROWN 0x0055AA
#define LIGHTGRAY 0xAAAAAA
#define DARKGRAY 0x555555
#define LIGHTBLUE 0xFF5555
#define LIGHTGREEN 0x55FF55
#define LIGHTCYAN 0xFFFF55
#define LIGHTRED 0x5555FF
#define LIGHTMAGENTA 0xFF55FF
#define YELLOW 0x55FFFF
#define WHITE 0xFFFFFF
// Color conversion macro
#define BGR(color) ( (((color) & 0xFF) << 16) | ((color) & 0xFF00FF00) | (((color) & 0xFF0000) >> 16) )
class IMAGE;
// Line style class
class LINESTYLE
{
public:
LINESTYLE();
LINESTYLE(const LINESTYLE &style);
LINESTYLE& operator = (const LINESTYLE &style);
virtual ~LINESTYLE();
DWORD style;
DWORD thickness;
DWORD *puserstyle;
DWORD userstylecount;
};
// Fill style class
class FILLSTYLE
{
public:
FILLSTYLE();
FILLSTYLE(const FILLSTYLE &style);
FILLSTYLE& operator = (const FILLSTYLE &style);
virtual ~FILLSTYLE();
int style; // Fill style
long hatch; // Hatch pattern
IMAGE* ppattern; // Fill image
};
// Image class
class IMAGE
{
public:
int getwidth() const; // Get the width of the image
int getheight() const; // Get the height of the image
private:
int width, height; // Width and height of the image
HBITMAP m_hBmp;
HDC m_hMemDC;
float m_data[6];
COLORREF m_LineColor; // Current line color
COLORREF m_FillColor; // Current fill color
COLORREF m_TextColor; // Current text color
COLORREF m_BkColor; // Current background color
DWORD* m_pBuffer; // Memory buffer of the image
LINESTYLE m_LineStyle; // Current line style
FILLSTYLE m_FillStyle; // Current fill style
virtual void SetDefault(); // Set the graphics environment as default
public:
IMAGE(int _width = 0, int _height = 0);
IMAGE(const IMAGE &img);
IMAGE& operator = (const IMAGE &img);
virtual ~IMAGE();
virtual void Resize(int _width, int _height); // Resize image
};
// Graphics window related functions
HWND initgraph(int width, int height, int flag = 0); // Create graphics window
void closegraph(); // Close graphics window
// Graphics environment related functions
void cleardevice(); // Clear device
void setcliprgn(HRGN hrgn); // Set clip region
void clearcliprgn(); // Clear clip region
void getlinestyle(LINESTYLE* pstyle); // Get line style
void setlinestyle(const LINESTYLE* pstyle); // Set line style
void setlinestyle(int style, int thickness = 1, const DWORD *puserstyle = NULL, DWORD userstylecount = 0); // Set line style
void getfillstyle(FILLSTYLE* pstyle); // Get fill style
void setfillstyle(const FILLSTYLE* pstyle); // Set fill style
void setfillstyle(int style, long hatch = NULL, IMAGE* ppattern = NULL); // Set fill style
void setfillstyle(BYTE* ppattern8x8); // Set fill style
void setorigin(int x, int y); // Set coordinate origin
void getaspectratio(float *pxasp, float *pyasp); // Get aspect ratio
void setaspectratio(float xasp, float yasp); // Set aspect ratio
int getrop2(); // Get binary raster operation mode
void setrop2(int mode); // Set binary raster operation mode
int getpolyfillmode(); // Get polygon fill mode
void setpolyfillmode(int mode); // Set polygon fill mode
void graphdefaults(); // Reset the graphics environment as default
COLORREF getlinecolor(); // Get line color
void setlinecolor(COLORREF color); // Set line color
COLORREF gettextcolor(); // Get text color
void settextcolor(COLORREF color); // Set text color
COLORREF getfillcolor(); // Get fill color
void setfillcolor(COLORREF color); // Set fill color
COLORREF getbkcolor(); // Get background color
void setbkcolor(COLORREF color); // Set background color
int getbkmode(); // Get background mode
void setbkmode(int mode); // Set background mode
// Color model transformation related functions
COLORREF RGBtoGRAY(COLORREF rgb);
void RGBtoHSL(COLORREF rgb, float *H, float *S, float *L);
void RGBtoHSV(COLORREF rgb, float *H, float *S, float *V);
COLORREF HSLtoRGB(float H, float S, float L);
COLORREF HSVtoRGB(float H, float S, float V);
// Drawing related functions
COLORREF getpixel(int x, int y); // Get pixel color
void putpixel(int x, int y, COLORREF color); // Set pixel color
void line(int x1, int y1, int x2, int y2); // Draw a line
void rectangle (int left, int top, int right, int bottom); // Draw a rectangle without filling
void fillrectangle (int left, int top, int right, int bottom); // Draw a filled rectangle with a border
void solidrectangle(int left, int top, int right, int bottom); // Draw a filled rectangle without a border
void clearrectangle(int left, int top, int right, int bottom); // Clear a rectangular region
void circle (int x, int y, int radius); // Draw a circle without filling
void fillcircle (int x, int y, int radius); // Draw a filled circle with a border
void solidcircle(int x, int y, int radius); // Draw a filled circle without a border
void clearcircle(int x, int y, int radius); // Clear a circular region
void ellipse (int left, int top, int right, int bottom); // Draw an ellipse without filling
void fillellipse (int left, int top, int right, int bottom); // Draw a filled ellipse with a border
void solidellipse(int left, int top, int right, int bottom); // Draw a filled ellipse without a border
void clearellipse(int left, int top, int right, int bottom); // Clear an elliptical region
void roundrect (int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight); // Draw a rounded rectangle without filling
void fillroundrect (int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight); // Draw a filled rounded rectangle with a border
void solidroundrect(int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight); // Draw a filled rounded rectangle without a border
void clearroundrect(int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight); // Clear a rounded rectangular region
void arc (int left, int top, int right, int bottom, double stangle, double endangle); // Draw an arc
void pie (int left, int top, int right, int bottom, double stangle, double endangle); // Draw a sector without filling
void fillpie (int left, int top, int right, int bottom, double stangle, double endangle); // Draw a filled sector with a border
void solidpie(int left, int top, int right, int bottom, double stangle, double endangle); // Draw a filled sector without a border
void clearpie(int left, int top, int right, int bottom, double stangle, double endangle); // Clear a rounded rectangular region
void polyline (const POINT *points, int num); // Draw multiple consecutive lines
void polygon (const POINT *points, int num); // Draw a polygon without filling
void fillpolygon (const POINT *points, int num); // Draw a filled polygon with a border
void solidpolygon(const POINT *points, int num); // Draw a filled polygon without a border
void clearpolygon(const POINT *points, int num); // Clear a polygon region
void polybezier(const POINT *points, int num); // Draw three square Bezier curves
void floodfill(int x, int y, COLORREF color, int filltype = FLOODFILLBORDER); // Fill the area
// Text related functions
void outtextxy(int x, int y, LPCTSTR str); // Output a string at the specified location
void outtextxy(int x, int y, TCHAR c); // Output a char at the specified location
int textwidth(LPCTSTR str); // Get the width of a string
int textwidth(TCHAR c); // Get the width of a char
int textheight(LPCTSTR str); // Get the height of a string
int textheight(TCHAR c); // Get the height of a char
int drawtext(LPCTSTR str, RECT* pRect, UINT uFormat); // Output a string in the specified format within the specified area.
int drawtext(TCHAR c, RECT* pRect, UINT uFormat); // Output a char in the specified format within the specified area.
// Set current text style.
// nHeight: The height of the text
// nWidth: The average width of the character. If 0, the scale is adaptive.
// lpszFace: The font name
// nEscapement: The writing angle of the string, 0.1 degrees, defaults to 0.
// nOrientation: The writing angle of each character, 0.1 degrees, defaults to 0.
// nWeight: The stroke weight of the character
// bItalic: Specify whether the font is italic
// bUnderline: Specify whether the font is underlined
// bStrikeOut: Specify whether the font has a strikeout
// fbCharSet: Specifies the character set
// fbOutPrecision: Specifies the output accuracy of the text
// fbClipPrecision: Specifies the clip accuracy of the text
// fbQuality: Specifies the output quality of the text
// fbPitchAndFamily: Specifies a font family that describes a font in a general way
void settextstyle(int nHeight, int nWidth, LPCTSTR lpszFace);
void settextstyle(int nHeight, int nWidth, LPCTSTR lpszFace, int nEscapement, int nOrientation, int nWeight, bool bItalic, bool bUnderline, bool bStrikeOut);
void settextstyle(int nHeight, int nWidth, LPCTSTR lpszFace, int nEscapement, int nOrientation, int nWeight, bool bItalic, bool bUnderline, bool bStrikeOut, BYTE fbCharSet, BYTE fbOutPrecision, BYTE fbClipPrecision, BYTE fbQuality, BYTE fbPitchAndFamily);
void settextstyle(const LOGFONT *font); // Set current text style
void gettextstyle(LOGFONT *font); // Get current text style
// Image related functions
void loadimage(IMAGE *pDstImg, LPCTSTR pImgFile, int nWidth = 0, int nHeight = 0, bool bResize = false); // Load image from a file (bmp/gif/jpg/png/tif/emf/wmf/ico)
void loadimage(IMAGE *pDstImg, LPCTSTR pResType, LPCTSTR pResName, int nWidth = 0, int nHeight = 0, bool bResize = false); // Load image from resources (bmp/gif/jpg/png/tif/emf/wmf/ico)
void saveimage(LPCTSTR pImgFile, IMAGE* pImg = NULL); // Save image to a file (bmp/gif/jpg/png/tif)
void getimage(IMAGE *pDstImg, int srcX, int srcY, int srcWidth, int srcHeight); // Get image from device
void putimage(int dstX, int dstY, const IMAGE *pSrcImg, DWORD dwRop = SRCCOPY); // Put image to device
void putimage(int dstX, int dstY, int dstWidth, int dstHeight, const IMAGE *pSrcImg, int srcX, int srcY, DWORD dwRop = SRCCOPY); // Put image to device
void rotateimage(IMAGE *dstimg, IMAGE *srcimg, double radian, COLORREF bkcolor = BLACK, bool autosize = false, bool highquality = true);// Rotate image
void Resize(IMAGE* pImg, int width, int height); // Resize the device
DWORD* GetImageBuffer(IMAGE* pImg = NULL); // Get the display buffer of the graphics device
IMAGE* GetWorkingImage(); // Get current graphics device
void SetWorkingImage(IMAGE* pImg = NULL); // Set current graphics device
HDC GetImageHDC(IMAGE* pImg = NULL); // Get the graphics device handle
// Other functions
int getwidth(); // Get the width of current graphics device
int getheight(); // Get the height of current graphics device
void BeginBatchDraw(); // Begin batch drawing mode
void FlushBatchDraw(); // Refreshes the undisplayed drawing
void FlushBatchDraw(int left, int top, int right, int bottom); // Refreshes the undisplayed drawing
void EndBatchDraw(); // End batch drawing mode and refreshes the undisplayed drawing
void EndBatchDraw(int left, int top, int right, int bottom); // End batch drawing mode and refreshes the undisplayed drawing
HWND GetHWnd(); // Get the handle of the graphics window
const TCHAR* GetEasyXVer(); // Get version of EasyX library
// Get user input as a dialog box
bool InputBox(LPTSTR pString, int nMaxCount, LPCTSTR pPrompt = NULL, LPCTSTR pTitle = NULL, LPCTSTR pDefault = NULL, int width = 0, int height = 0, bool bOnlyOK = true);
// Message
//
// Category Type Description
//
// EX_MOUSE WM_MOUSEMOVE Mouse moves
// WM_MOUSEWHEEL Mouse wheel is rotated
// WM_LBUTTONDOWN Left mouse button is pressed
// WM_LBUTTONUP Left mouse button is released
// WM_LBUTTONDBLCLK Left mouse button is double-clicked
// WM_MBUTTONDOWN Middle mouse button is pressed
// WM_MBUTTONUP Middle mouse button is released
// WM_MBUTTONDBLCLK Middle mouse button is double-clicked
// WM_RBUTTONDOWN Right mouse button is pressed
// WM_RBUTTONUP Right mouse button is released
// WM_RBUTTONDBLCLK Right mouse button is double-clicked
//
// EX_KEY WM_KEYDOWN A key is pressed
// WM_KEYUP A key is released
//
// EX_CHAR WM_CHAR
//
// EX_WINDOW WM_ACTIVATE The window is activated or deactivated
// WM_MOVE The window has been moved
// WM_SIZE The size of window has changed
// Message Category
#define EX_MOUSE 1
#define EX_KEY 2
#define EX_CHAR 4
#define EX_WINDOW 8
// Message Structure
struct ExMessage
{
USHORT message; // The message identifier
union
{
// Data of the mouse message
struct
{
bool ctrl :1; // Indicates whether the CTRL key is pressed
bool shift :1; // Indicates whether the SHIFT key is pressed
bool lbutton :1; // Indicates whether the left mouse button is pressed
bool mbutton :1; // Indicates whether the middle mouse button is pressed
bool rbutton :1; // Indicates whether the right mouse button is pressed
short x; // The x-coordinate of the cursor
short y; // The y-coordinate of the cursor
short wheel; // The distance the wheel is rotated, expressed in multiples or divisions of 120
};
// Data of the key message
struct
{
BYTE vkcode; // The virtual-key code of the key
BYTE scancode; // The scan code of the key. The value depends on the OEM
bool extended :1; // Indicates whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is true if the key is an extended key; otherwise, it is false.
bool prevdown :1; // Indicates whether the key is previously up or down
};
// Data of the char message
TCHAR ch;
// Data of the window message
struct
{
WPARAM wParam;
LPARAM lParam;
};
};
};
// Message Function
ExMessage getmessage(BYTE filter = -1); // Get a message until a message is available for retrieval
void getmessage(ExMessage *msg, BYTE filter = -1); // Get a message until a message is available for retrieval
bool peekmessage(ExMessage *msg, BYTE filter = -1, bool removemsg = true); // Get a message if any exist, otherwise return false
void flushmessage(BYTE filter = -1); // Flush the message buffer
3.tools.cpp
#include <stdio.h>
#include <Windows.h>
#include "tools.h"
#include<mmsystem.h> //播放音乐
#pragma comment(lib,"winmm.lib") //播放音乐
#include <mmsystem.h>
#pragma comment(lib, "winmm.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;
}
}
// 载入PNG图并去透明部分
void putimagePNG(int picture_x, int picture_y, IMAGE* picture) //x为载入图片的X坐标,y为Y坐标
{
DWORD* dst = GetImageBuffer(); // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
DWORD* draw = GetImageBuffer();
DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
int graphWidth = getwidth(); //获取绘图区的宽度,EASYX自带
int graphHeight = getheight(); //获取绘图区的高度,EASYX自带
int dstX = 0; //在显存里像素的角标
// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
for (int iy = 0; iy < picture_height; iy++)
{
for (int ix = 0; ix < picture_width; ix++)
{
int srcX = ix + iy * picture_width; //在显存里像素的角标
int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
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; //在显存里像素的角标
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) //公式: 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
}
}
}
}
// 适用于 y <0 以及x<0的任何情况
void 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);
}
// 适用于 y <0 以及y>0的任何情况
void 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);
}
//设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);
}
4.可执行文件
#include<stdio.h>
#include<graphics.h> //图形库
#include"tools.h"
#include<conio.h> //kbhit
#include<vector> //长度可变数组 C++
using namespace std; //声明命名空间 C++
#define WIN_SCORE 15
#define WIN_WIDTH 1012 //窗口宽度
#define WIN_HEIGHT 396 //窗口长度
#define OBSTACLE_COUNT 20 //障碍物总数
IMAGE imgBgs[3]; //背景图片3张
int bgX[3]; //背景图片的X坐标
int bgSpeed[3] = { 1,2,4 }; //背景移动速度
IMAGE imgHeros[12]; //玩家图片12张
int heroX; //玩家的x坐标
int heroY; //玩家的y坐标
int heroIndex; //玩家奔跑的图片帧序号
bool heroJump; // 表示玩家正在跳跃
int jumpHeighMax; //跳跃最大高度
int heroJumpOff; //偏移量
int update;//表示是否需要马上刷新画面
//IMAGE imgTortoise; //小乌龟
//int torToiseX; //小乌龟的水平坐标
//int torToiseY; //小乌龟的垂直坐标
//bool torToiseExist;//当前窗口是否有小乌龟
int heroBlood; //初始化血量
int score; //分数
#define MAX_SCORE 0 // 定义初始接受最高分
//障碍物
typedef enum {
TORTOISE, //乌龟0
LION, //狮子
HOOK1,//柱子1
HOOK2,
HOOK3,
HOOK4,
OBSTACLE_TYPE_COUNT //6
}obstacle_type;
vector<vector<IMAGE>>obstacleImgs;
//IMAGEobstacleImgs[3][5] 存放所以障碍物的各个图片 C++语言
//障碍物属性
typedef struct obstacle {
int type;//障碍物类型
int x, y; //障碍物的坐标
int imgIndex; //当前显示的图片的序号
int speed; //速度
int power; //杀伤力
bool exist; //是否存在
bool hited; //表示是否已经发生碰撞
bool passed; //表示是否已经被通过
}obstacle_t;
int lastObsIndex;//上个障碍物的序号
obstacle_t obstacles[OBSTACLE_COUNT]; //障碍物池
IMAGE imgHeroDown[2];//下蹲素材
bool heroDown; //表示玩家是否处于下蹲状态
IMAGE imgSZ[10];
//游戏初始化
void init() {
//创建游戏窗口
initgraph(WIN_WIDTH, WIN_HEIGHT);
//加载背景资源
char name[64];
for (int i = 0; i < 3; i++) {
//"res/bg001.png" "res/bg002.png" "res/bg003.png"
sprintf(name, "res/bg%03d.png",i+1);//%03宽度占三位
loadimage(&imgBgs[i], name);
bgX[i] = 0;
}
//加载Hero奔跑的图片帧素材
for (int i = 0; i < 12; i++) {
//"res/hero1.png"..."res/hero12.png"
sprintf(name, "res/hero%d.png", i + 1);
loadimage(&imgHeros[i], name);
}
//设置玩家的初始化位置
heroX = WIN_WIDTH * 0.5 - imgHeros[0].getwidth() * 0.5;
heroY = 345 - imgHeros[0].getheight();
heroIndex = 0;
heroJump = false; //初始化跳跃开关
jumpHeighMax = 345 - imgHeros[0].getheight() - 120;
heroJumpOff = -4;
update = true;
//加载小乌龟素材
/* loadimage(&imgTortoise, "res/t1.png");
torToiseExist = false;
torToiseY = 345 - imgTortoise.getheight()+5;*/
IMAGE imgTort;
loadimage(&imgTort, "res/t1.png");
vector<IMAGE>imgTortArray; //定义一个数组
imgTortArray.push_back(imgTort); //添加图片
obstacleImgs.push_back(imgTortArray);//放进二维数组
//添加狮子
IMAGE imgLion;
vector<IMAGE>imgLionArray;
for (int i = 0; i < 6; i++) {
sprintf(name, "res/p%d.png", i + 1);
loadimage(&imgLion, name);
imgLionArray.push_back(imgLion);
}
obstacleImgs.push_back(imgLionArray);
//初始化障碍物池
for (int i = 0; i < OBSTACLE_COUNT; i++) {
obstacles[i].exist = false;
}
//加载下蹲素材
loadimage(&imgHeroDown[0], "res/d1.png");
loadimage(&imgHeroDown[1], "res/d2.png");
heroDown = false;
IMAGE imgH;
//添加柱子
for (int i = 0; i < 4; i++) {
vector<IMAGE>imgHookArray;
sprintf(name,"res/h%d.png", i + 1);
loadimage(&imgH, name, 63, 260, true);
imgHookArray.push_back(imgH);
obstacleImgs.push_back(imgHookArray);
}
heroBlood = 100;
//预加载音效
preLoadSound("res/hit.mp3");//解决第一次碰撞没声效
//开启音乐
mciSendString("play res/bg.mp3 repeat", 0, 0, 0);
lastObsIndex = -1;//上次障碍物序号为-1
score = 0;//分数初始化
//加载数字图片
for (int i = 0; i < 10; i++) {
sprintf(name, "res/sz/%d.png", i);
loadimage(&imgSZ[i], name);
}
}
//创建障碍物
void createObstacle() {
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 (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) {
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 = 10;
}
else if (obstacles[i].type == LION) {
obstacles[i].speed = 4;
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) {//非下蹲
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];
int b1x = obstacles[i].x + off;
int b1y = obstacles[i].y + off;
int b2x = obstacles[i].x + img.getwidth() - off;
int b2y = obstacles[i].y+ img.getheight()-10;
if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {//判断是否碰撞
heroBlood -= obstacles[i].power;
printf("血量剩余%d\n", heroBlood);
playSound("res/hit.mp3");
obstacles[i].hited = true;
}
}
}
}
void fly() {
for (int i = 0; i < 3; i++) {
bgX[i] -= bgSpeed[i];
if (bgX[i] < -WIN_WIDTH) {//小于窗口的宽度
bgX[i] = 0;
}
}
//实现跳跃
if (heroJump) {
if (heroY < jumpHeighMax) {
heroJumpOff = 4;
}
heroY += heroJumpOff;
if (heroY > 345 - imgHeros[0].getheight()) {
heroJump = false;
heroJumpOff = -4; //再次初始化为-4
}
}
else if (heroDown) {
static int count = 0;//下蹲保持帧率
int delays[2] = { 8,30 };//2个图片保持的帧度
count++;
if (count >= delays[heroIndex]) {
count = 0;
heroIndex++;
if (heroIndex >= 2) {
heroIndex = 0;
heroDown = false;
}
}
}
else {//不跳跃不下蹲
heroIndex = (heroIndex + 1) % 12;
}
//创建障碍物
static int frameCount = 0;
static int enemyFre = 50; //频度
frameCount++;
if (frameCount > enemyFre) {
frameCount = 0;
//if (!torToiseExist) {
// torToiseExist = true;
// torToiseX = WIN_WIDTH;
// enemyFre = 200+ rand() % 300;//200...500
//}
enemyFre = 80 + rand() % 50;//80...129
createObstacle();
}
//if (torToiseExist) {
// torToiseX -= bgSpeed[2];
// if (torToiseX < -imgTortoise.getwidth()) {
// torToiseExist = false;
// }
//}
//更新所有障碍物的目标
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;//更改图片索引
}
}
//玩家和障碍物的“碰撞检测”处理
checkHit();
}
//渲染“游戏背景”
void updateBg() {
putimagePNG2(bgX[0], 0, &imgBgs[0]);
putimagePNG2(bgX[1], 119, &imgBgs[1]);
putimagePNG2(bgX[2], 330, &imgBgs[2]);
}
//玩家跳跃
void jump() {
heroJump = true;
update = true;
}
//玩家下蹲
void down() {
update = true;
heroDown = true;
heroIndex = 0;
}
//处理用户按键的输入
void keyEvent() {
char ch;
if (kbhit()) {//如果按键按下,_kbhit()返回true
//scanf("%c", &c);//会直接阻塞程序的执行
ch = _getch(); //_getch()不需要按下回车即可直接读取
if (ch == ' ') {
jump();
}
else if (ch == 'a') {
down();
}
}
}
//渲染
void updateEnemy() {
//渲染小乌龟
/*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) {
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/over.png");
FlushBatchDraw();//刷新
mciSendString("stop res/bg.mp3", 0, 0, 0);
system("pause"); //用户按任意键继续
//暂停之后,充币复活,或者直接开始下一局
/* heroBlood = 100;
score = 0;
mciSendString("play res/bg.mp3 repeat",0, 0,0);*/
init();//调用,使游戏初始化
}
}
//检查得分
void checkScore() {
for (int i = 0; i < OBSTACLE_COUNT; i++) {
if (obstacles[i].hited == false && obstacles[i].exist && obstacles[i].passed == false
&& obstacles[i].x + obstacleImgs[obstacles[i].type][0].getwidth() < heroX) {
score++;
obstacles[i].passed = true;
}
}
// 读取宏的值
FILE* fp;
int macro_score;
fp = fopen("macro.txt", "r");
if (fp != NULL) {
fscanf(fp, "%d", ¯o_score);
fclose(fp);
}
else {
macro_score = MAX_SCORE;
}
// 判断并更新宏
if (score > macro_score) {
macro_score = score;
fp = fopen("macro.txt", "w");
if (fp != NULL) {
fprintf(fp, "%d", macro_score);
fclose(fp);
}
}
}
//显示分数
void updateScore() {
//50 =>"50" '5' '5'-'0'= 5
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(1000);
loadimage(0, "res/win.png");
FlushBatchDraw();
mciSendString("stop res/bg.mp3", 0, 0, 0);
system("pause");
init();
}
}
int main(void) {
init();
//显示初始画面
loadimage(0, "res/over.png");
system("pause");
int timer = 0;
while (1) {
keyEvent();
timer += getDelay(); //调用间隔
if (timer > 30) {
timer = 0;
update = true;
}
if (update) {
update = false;
BeginBatchDraw();
updateBg();
//putimagePNG2(heroX, heroY, &imgHeros[heroIndex]);
updateHero();
updateEnemy();
updateBloodBar();
updateScore();
checkWin();
EndBatchDraw();
checkOver();
checkScore();
fly();
}
//Sleep(5);//休眠 按键输入不响应
}
system("pause");
return 0;
}