先上效果圖,

游戲的實作主要基于EasyX圖形庫,有暫停界面、結束界面、游戲音效、游戲存檔,并最終集成為一個exe檔案,可謂麻雀雖小,五臟俱全 ,這里先放出源代碼和下載鏈接,以后有時間會以此為例寫一個C++小游戲開發教程系列,
鏈接:https://pan.baidu.com/s/1sBPD-LTGCTa7bfkI1RloXg
提取碼:8848
組件類及其定義:
#pragma once
#include<graphics.h>
#include<utility>
using namespace std;
typedef pair<double, double> BVec;
class Object
{
int _x;
int _y; //組件基類的四個成員,邏輯坐標(x,y),運動速度v,加速度g,貼圖ptr、掩碼ym_ptr
int _width;
int _height;
BVec _v;
BVec _g;
IMAGE _ptr;
IMAGE ym_ptr;
public:
Object(LPCTSTR path, LPCTSTR ympath, int width, int height, int posx= 0, int posy= 0);//建構式
Object(LPCTSTR path, int width, int height, int posx = 0, int posy = 0);
Object(LPCTSTR path);
Object():_x(0),_y(0),_width(0),_height(0),_v(make_pair(0,0)),_g(make_pair(0,0)){}
int x()const{ return _x; };
int y()const { return _y; };
BVec v()const { return _v; }; //資料成員操作介面
BVec g()const { return _g; }
int width()const { return _width; };
int height()const { return _height; };
void setx(int newx) { _x = newx; };
void sety(int newy) { _y = newy; };
void setv(int x, int y) { _v.first = x; _v.second = y; };
void setvx(int x) { _v.first = x; };
void setvy(int y) { _v.second = y; }
void setg(BVec newg) { _g = newg; };
void setptr(IMAGE newptr) { _ptr = newptr; }
void setptr_ym(IMAGE newptr_ym) { ym_ptr = newptr_ym; }
void setwidth(int newwidth) { _width = newwidth; }
void setheight(int newheight) { _height = newheight; }
void move();//此方法更新組件位置,
void show_n()const; //顯示組件,不顯示掩碼
void show()const; //顯示組件和掩碼
~Object() { };
};
#include "Object.h"
Object::Object(LPCTSTR path,LPCTSTR ympath, int width, int height, int posx , int posy )
{
loadimage(&_ptr, _T("PNG"), path, width, height);
loadimage(&ym_ptr,_T("PNG"), ympath, width, height);
_x = posx;
_y = posy;
_width = width;
_height = height;
_v.first = 0;
_v.second = 0;
_g.first = 0;
_g.second = 0;
}
Object::Object(LPCTSTR path, int width, int height, int posx , int posy )
{
loadimage(&_ptr, _T("PNG"),path, width, height);
_x = posx;
_y = posy;
_width = width;
_height = height;
_v.first = 0;
_v.second = 0;
_g.first = 0;
_g.second = 0;
}
Object::Object(LPCTSTR path)
{
loadimage(&_ptr, path);
_x = 0;
_y = 0;
_width = _ptr.getwidth();
_height = _ptr.getheight();
_v.first = 0;
_v.second = 0;
_g.first = 0;
_g.second = 0;
}
void Object::move()
{
_x += _v.first;
_y += _v.second;
_v.first += _g.first;
_v.second += _g.second;
}
void Object::show_n()const
{
putimage(_x, _y, &_ptr);
}
void Object::show()const
{
putimage(_x, _y, &ym_ptr, SRCAND);
putimage(_x, _y, &_ptr, SRCPAINT);
}
角色類及其定義
#pragma once
#include "Object.h"
#include<vector>
class bird : //角色類繼承組件類(游戲角色只有小鳥,故名為小鳥類)
public Object //添加動作序列、死亡貼圖
{
vector<IMAGE> seq;
vector<IMAGE> seq_ym;
IMAGE die;
IMAGE die_ym;
int index = 0;
public:
bird();
void add_seq(LPCTSTR path,LPCTSTR ympath);//添加動作序列
void add_die(LPCTSTR path, LPCTSTR ympath);//添加死亡貼圖
void select_next();//切換動作
IMAGE& getdie() { return die; }
IMAGE& getdie_ym() { return die_ym; }
};
#include "bird.h"
#include "GamePragma.h"
#include<utility>
using std::make_pair;
void bird::add_seq(LPCTSTR path,LPCTSTR ympath)
{
IMAGE img,ym;
loadimage(&img,_T("PNG"), path);
loadimage(&ym, _T("PNG"),ympath);
seq.push_back(img);
seq_ym.push_back(ym);
setptr(seq.back());
setptr_ym(seq_ym.back());
setwidth(img.getwidth());
setheight(img.getheight());
}
void bird::select_next()
{
int len = seq.size();
if (index == len - 1)
{
index = 0;
}
else index++;
setptr(seq[index]);
setptr_ym(seq_ym[index]);
}
bird::bird()
{
setg(make_pair(0, gravity));
setx(100);
sety(200);
}
void bird::add_die(LPCTSTR path, LPCTSTR ympath)
{
loadimage(&die, _T("PNG"), path);
loadimage(&die_ym,_T("PNG"), ympath);
}
障礙物類及其實作
#pragma once
#include "Object.h"
class Tube : //管道類繼承組件類,添加方向、和小鳥是否通過,
public Object
{
int direction;
bool if_pass ;
public:
Tube(LPCTSTR path, LPCTSTR ympath, int width, int height, int posx, int posy,int d);
int getd()const { return direction; }
bool getp() { return if_pass; }
void setp(bool p) { if_pass = p; }
};
#include "Tube.h"
Tube::Tube(LPCTSTR path, LPCTSTR ympath, int width, int height, int posx, int posy, int d) :
Object(path, ympath, width, height, posx, posy)
{
direction = d;
if_pass = false;
}
游戲引數定義
#pragma once
#define WinWidth 800
#define WinHeight 600
#define gravity 0.5
#define BirdUpdate 5
#define TubeWidth 90
#define TubeSpeed -3.5
#define BirdJump -9
#define TubeGenerateSpeed 85
#define CloudSpeed -5
用到的函式
含游戲運作需要的函式
#pragma once
#include "Object.h"
#include "GamePragma.h"
#include<vector>
#include<cmath>
#include "bird.h"
#include"Tube.h"
#include<ctime>
#include<random>
#include<iostream>
using namespace std;
void PlayBg(Object& bg1,Object& bg2)//播放背景
{
bg1.show_n();
bg2.setx(bg1.x() + WinWidth);
bg2.show_n();
bg1.move();
bg2.move();
if (bg1.x() == -WinWidth)bg1.setx(0);
}
void PlayCloud(vector<Object>& cloud,int& count,long double runtime)//播放云
{
if (!count)
{
cloud.push_back(Object(_T("cloud"), _T("cloud_ym"), 0, 0, 800, rand()%400));
cloud.back().setvx(CloudSpeed);
}
for (auto& c : cloud)
{
c.show();
c.move();
}
if (count == 130)count = 0;
else count++;
if (cloud.front().x() <= -200)
{
cloud.erase(cloud.begin());
}
}
void PlayBird(bird& b,int& flyfreq,bool start)//播放鳥
{
b.show();
if(start)
b.move();
if (flyfreq == 8)
{
b.select_next();
flyfreq = 0;
}
else flyfreq++;
}
int TubeGenerator()//生成障礙物高度,所用的分布與正態分布影像關于x軸對稱
{
mt19937 rng;
rng.seed(std::random_device()());
std::uniform_int_distribution<int> dist(30, 390);
int h = dist(rng);
if (h >= 210)h -= 180;
else h += 180;
return h;
}
void PlayTube(vector<Tube>& tube, int& count)//播放障礙物
{
if (!count)
{
int h = TubeGenerator();
tube.push_back(Tube(_T("tube"), _T("tube_ym"), TubeWidth, h, 800, 0,0));
tube.back().setvx(TubeSpeed);
tube.push_back(Tube(_T("rtube"), _T("rtube_ym"), TubeWidth, 400-h, 800, 200+h, 1));
tube.back().setvx(TubeSpeed);
}
if (count == TubeGenerateSpeed)count = 0;
else count++;
for (auto& t : tube)
{
t.show();
t.move();
}
if (!tube.empty()&&tube.front().x() <= -100)
{
tube.erase(tube.begin());
tube.erase(tube.begin());
}
}
int CheckColid(const vector<Tube>& tube,const bird& b)//碰撞檢測
{
int bx1 = b.x();
int by1 = b.y();
int bx2 = bx1 + b.width();
int by2 = by1 + b.height();
for (int i =0; i < tube.size(); i++)
{
int d = tube[i].getd();
int tx1 = tube[i].x();
int ty1 = tube[i].y();
int tx2 = tx1 + tube[i].width();
int ty2 = ty1 + tube[i].height();
bool s1 = (d == 0 && tx1 <= bx1 && bx1 <= tx2 && (by1+20) <= ty2);
bool s2= (d == 0 && tx1 <= bx2-5 && bx2-5 <= tx2 && by1 <= ty2);
bool s3= (d == 1 && tx1 <= bx1+20 && bx1+20 <= tx2 && by2-5 >= ty1);
bool s4= (d == 1 && tx1 <= bx2-20 && bx2-20 <= tx2 && by2-5 >= ty1);
if (s1 || s2 || s3 || s4)return true;
}
return false;
}
void PlayBirdDie(bird& b)//播放鳥死亡影片
{
b.setvy(5);
b.setptr(b.getdie());
b.setptr_ym(b.getdie_ym());
b.show();
b.move();
}
void add_score(vector<Tube>&tube, long int& score)//計分器
{
for (auto it = tube.begin(); it != tube.end(); it++)
{
if ((*it).getp() == false)
{
if ((*it).x() <= 20)
{
(*it).setp(true);
score++;
it++;
(*it).setp(true);
break;
}
}
}
}
int GetPauseMenuAct(ExMessage& p)//獲取玩家在用戶界面的操作
{
if (329 <= p.x && p.x <= 468 && 147 <= p.y && p.y <= 186)return 1;
if (329 <= p.x && p.x <= 468 && 260 <= p.y && p.y <= 300)return 2;
if (329 <= p.x && p.x <= 468 && 375 <= p.y && p.y <= 413)return 3;
return 0;
}
int GetOverMenuAct(ExMessage& p)//獲取玩家在結束界面的操作
{
if (330 <= p.x && p.x <= 470 && 320 <= p.y && p.y <= 360)return 1;
if (330 <= p.x && p.x <= 470 && 395 <= p.y && p.y <= 435)return 2;
return 0;
}
主函式
#include<graphics.h>
#include<conio.h>
#include<windows.h>
#include "Object.h"
#include "function.h"
#include<vector>
#include<ctime>
#include<windows.h>
#include"Tube.h"
#include<string>
#include<fstream>
#include<Mmsystem.h>
#include<dsound.h>
#include<stdio.h>
#pragma comment(lib,"WINMM.LIB")
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
using namespace std;
int main()
{
initgraph(800, 600);
BEGIN:
Object Background1(_T("Background"), 800, 600); //加載貼圖資源及相關變數
Object Background2(_T("Background"), 800, 600);
Background1.setv(-1, 0);
Background2.setv(-1, 0);
Object pausemenu(_T("pausemenu"), _T("pausemenu_ym"),400,450,200,50);
Object overmenu(_T("overmenu"), _T("overmenu_ym"), 400, 450, 200,-500);
Object startflag(_T("startflag"), _T("startflag_ym"), 0, 0, 200, 200);
bird b;
b.add_seq(_T("bird1"), _T("bird1_ym"));
b.add_seq(_T("bird2"), _T("bird2_ym"));
b.add_seq(_T("bird3"), _T("bird3_ym"));
b.add_seq(_T("bird4"), _T("bird4_ym"));
b.add_die(_T("bird_die"), _T("bird_die_ym"));
vector<Tube> tube;
vector<Object> cloud;
int cloudcount = 0;
int tubecount = 0;
int flyfreq = 0;
int birdcount = 0;
time_t begin, now;
double runtime;
begin = clock();
bool start = false;
long int score = 0;
settextstyle(50, 0, _T("JandaManateesolid"));
setbkmode(TRANSPARENT);
BeginBatchDraw();
bool firstflag = true;
bool LASTKEYDOWN = false;
GAMING:
while (true)//主回圈
{
if (_kbhit()) //檢測是否按下ESC鍵暫停
{
int a = _getch();
if (a == 27)
{
pausemenu.show();
FlushBatchDraw();
ExMessage m;
while (1)
{
peekmessage(&m);
if (KEY_DOWN(VK_LBUTTON)) //檢測選擇
{
int selection = GetPauseMenuAct(m);
switch (selection)
{
case 0:break;
case 1:
PlaySound(TEXT("click"), NULL, SND_RESOURCE | SND_ASYNC );//播放點擊音效
Sleep(100);
goto GAMING; //選擇Resume,回到主回圈
case 2:
PlaySound(TEXT("click"), NULL, SND_RESOURCE | SND_ASYNC );
goto BEGIN; //選擇Restart,跳轉到開始
case 3:
PlaySound(TEXT("click"), NULL, SND_RESOURCE | SND_ASYNC );
Sleep(100); //選擇Exit退出游戲
exit(1);
}
}
}
}
}
now = clock();
runtime = (now - begin) / CLOCKS_PER_SEC; //記錄游戲運行時間
PlayBg(Background1, Background2);
PlayCloud(cloud, cloudcount, runtime); //使各貼圖運動
PlayBird(b, flyfreq,start);
if (start)
{
PlayTube(tube, tubecount); //游戲進行程序加載障礙物和計分器
add_score(tube, score);
}
TCHAR scorestr[10];
_stprintf_s(scorestr, _T("%d"), score);
outtextxy(380- 13 * (to_string(score).length() - 1), 20, scorestr); //列印分數
if (KEY_DOWN(VK_LBUTTON)) //檢測是否單擊滑鼠左鍵并松開,
{
if (!LASTKEYDOWN)
{
b.setvy(BirdJump); //重設速度使小鳥跳躍
start = true;
LASTKEYDOWN = true;
PlaySound(TEXT("plop"), NULL, SND_RESOURCE | SND_ASYNC );//播放音效
}
}
else
{
LASTKEYDOWN = false;
}
FlushBatchDraw();//重繪幀
if (CheckColid(tube, b) || b.y() >= 620||b.y()<=-50) //死亡檢測
{
PlaySound(TEXT("die"), NULL, SND_RESOURCE | SND_ASYNC);//播放結束音樂及影片
b.setvy(-5);
b.setptr(b.getdie());
b.setptr_ym(b.getdie_ym());
ifstream f("data.dat", ios::binary | ios::in);
long int best;
if (!f.is_open()) //更新最高分
{
f.close();
ofstream fo("data.dat", ios::binary);
fo.write((char*)&score, sizeof(long int));
best = score;
fo.close();
}
else
{
f.read((char*)&best, sizeof(long int));
if (score > best)
{
best = score;
f.close();
ofstream fo("data.dat", ios::binary);
fo.write((char*)&score,sizeof(score));
fo.close();
}
f.close();
}
while (true) //死亡影片次回圈
{
PlayBg(Background1, Background2);
for (auto& c : cloud)
{
c.show();
}
for (auto& t : tube)
{
t.show();
}
outtextxy(370 , 20, scorestr);
b.show();
b.move();
FlushBatchDraw();
Sleep(1);
if (b.y() >= 620)
{
ExMessage m;
overmenu.setvy(10);
TCHAR scorestr[10];
_stprintf_s(scorestr, _T("%d"), score);
TCHAR beststr[10];
_stprintf_s(beststr, _T("%d"), best);
while (true) //結束選單播放次回圈
{
PlayBg(Background1, Background2);
for (auto& c : cloud)
{
c.show();
}
for (auto& t : tube)
{
t.show();
}
overmenu.show();
outtextxy(395-13*(to_string(score).length()-1), overmenu.y()+110, scorestr);
outtextxy(395 - 13 * (to_string(best).length() - 1), overmenu.y() + 200, beststr);
if (overmenu.y() < 50)
{
overmenu.move();
}
else
{
while (true)//等候操作次回圈
{
peekmessage(&m);
if (KEY_DOWN(VK_LBUTTON))
{
int selection = GetOverMenuAct(m);
switch (selection)
{
case 1:
PlaySound(TEXT("click"), NULL, SND_RESOURCE | SND_ASYNC );
goto BEGIN;
case 2:
PlaySound(TEXT("click"), NULL, SND_RESOURCE | SND_ASYNC );
Sleep(100);
exit(1);
default: break;
}
}
}
closegraph();
}
FlushBatchDraw();
Sleep(1);
}
}
}
}
Sleep(1);
}
EndBatchDraw();
closegraph();
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/332178.html
標籤:其他
