從零到有的游戲開發(visual studio 2022 + easyx.h)

引言

本文章適用于C語言初學者掌握基本的游戲開發,

我將用詳細的步驟引領大家如何開發屬于自己的游戲。

作者溫馨提示:不要認為開發游戲很難,一些基本的游戲邏輯其實很簡單,

關于游戲的開發環境也不用擔心,我會詳細說明如何配置開發環境,下載鏈接我也會列出。

文章前半部分教你掌握開發游戲的基本邏輯(各種游戲邏輯)。

文章后半部分我會提供一個基本的2D角色扮演的游戲框架,(開發功能取決于玩家)。

游戲開發環境的配置

首先我們需要一個能安裝easyx.h圖形界面庫的C語言編譯器,這里我推薦vsual studio 2022

該編譯器功能也是很強大,可以兼容各種編程語言的項目開發,這里我們只使用C語言即可。

visual studio 2022 的 安裝

下載鏈接:Visual Studio 2022 IDE - 適用于軟件開發人員的編程工具

選擇圖中的 community 2022 社區版本(社區版免費),

然后等待安裝資源包的下載。

下載好后,彈出來的窗口,點擊繼續→

稍稍等待一小會兒.....

從左上角可以看到(工作負荷,單個組件,語言包,安裝位置)四個頭目錄。

首先是(工作負荷):我們只需要勾選 “使用C++的桌面開發”。

然后(單個組件):只需要檢查一下圖中是否勾選了上述選項,一般不用更改(確定好win系統)

(語言包)默認勾選“簡體中文”即可

最后(安裝位置)要注意的是分成三個不同的子文件夾,你可以在同一個文件夾中新建三個子文件夾,然后將上述三個位置分別選中子文件夾即可,(如果第三個路徑不可選,說明你之前下載過該編譯器。)關于安裝路徑在哪個盤都隨意。

第三個路徑不可選的解決辦法也很簡單

第一步 :win + R 打開運行

第二步:輸入 regedit 打開注冊表

第三步:找到該位置

第四步:刪除圖中除(默認)以外的配置即可

然后點擊安裝,等待下載完成即可(需要一段時間,內存不小)

下載好后,運行打開,點擊圖中創建新項目。

選擇空項目點擊下一步

輸入項目名稱和路徑

右鍵點擊

新建項

定義名

然后就可以寫代碼了(你可以用helloworld試試)

現在編譯器便安裝好了,然后還需要安裝圖形界面庫(很快)

easyx庫的配置

下載鏈接:EasyX Graphics Library for C++

點擊右側紅色 “下載EasyX”

下載好后,彈出窗口點擊下一步。

然后會自動檢測你的編譯器版本,找到剛下載的Visual C++2022點擊安裝,,顯示安裝成功就可以了,重啟visual studio 2022,即可。

(最上面的EasyX文檔也可以安裝,里面包含easyx圖形界面庫的全部函數用法)

測試easyx庫的配置(將下述代碼復制進去)


#include<graphics.h> //需安裝easyx圖形庫插件
#include<conio.h>
#include<time.h>
#include<math.h>
#include<sys/timeb.h>struct MyLove
{int NUMS;  //  編號double m;double n;double size;bool Is_show;int x;int y;
};MyLove mylove[400];
int CenterX = 320;
int CenterY = 180;
double Size = 60;
void initdata();  // 初始化數據
void updata();    // 更新
void movedata();  // 平移
void showdata();  // 顯示
int* GetRand(int* buf, int count, int range);  // 隨機數的生成
void heart(int x0, int y0, int size, COLORREF C);
void HpSleep(int ms);int main()
{initgraph(640, 480);initdata();BeginBatchDraw();while (true){updata();showdata();HpSleep(30);    // 改為精確延時FlushBatchDraw();cleardevice();}EndBatchDraw();_getch();return 0;
}void updata()
{int* buf = (int*)malloc(sizeof(int) * 20);buf = GetRand(buf, 20, (int)(2 * Size / 0.01));movedata();for (int i = 0; i < 20; i++){mylove[i].m = buf[i] * 0.01;mylove[i].n = (((sin(buf[(int)i] * 0.01) * sqrt(fabs(cos(buf[(int)i] * 0.01)))) / (sin(buf[(int)i] * 0.01) + 1.4142)) - 2 * sin(buf[(int)i] * 0.01) + 2);mylove[i].size = Size;mylove[i].NUMS = i / 20;mylove[i].Is_show = true;mylove[i].x = (int)(-Size * mylove[i].n * cos(mylove[i].m) + CenterX);mylove[i].y = (int)(-Size * mylove[i].n * sin(mylove[i].m) + CenterY - mylove[i].size);}for (int i = 20; i < 400; i++){mylove[i].size = mylove[i].size + 1;if (mylove[i].size > 80){mylove[i].size = 80;}mylove[i].NUMS = i / 20;mylove[i].x = (int)(-mylove[i].size * mylove[i].n * cos(mylove[i].m) + CenterX);mylove[i].y = (int)(-mylove[i].size * mylove[i].n * sin(mylove[i].m) + CenterY - mylove[i].size);}
}void movedata()
{for (int i = 399; i > 19; i--){mylove[i] = mylove[i - 20];}
}void showdata()
{settextcolor(RED);wchar_t c = 0x59;    // 0x28 是電話機在 Wingdings 字體中的對應編碼for (int i = 0; i < 400; i++){settextstyle(mylove[i].NUMS + 10, 0, _T("Webdings"));setbkmode(TRANSPARENT);outtextxy(mylove[i].x + 20, mylove[i].y + 20, c);}
}int* GetRand(int* buf, int count, int range)
{struct timeb timeSeed;ftime(&timeSeed);srand(timeSeed.time * 1000 + timeSeed.millitm);  // milli timefor (int i = 0; i < count; i++){int randTmp = rand() % range;for (int j = 0; j < i; j++){if (buf[j] == randTmp){break;//檢查重復。}}buf[i] = randTmp;}return buf;
}void initdata()
{for (int i = 0; i < 400; i++){mylove[i].NUMS = 0;mylove[i].m = 0;mylove[i].n = 0;mylove[i].size = 0;mylove[i].Is_show = false;mylove[i].x = 0;mylove[i].y = 0;}
}// 精確延時函數(可以精確到 1ms,精度 ±1ms)
// by yangw80<yw80@qq.com>, 2011-5-4
void HpSleep(int ms)
{static clock_t oldclock = clock();    // 靜態變量,記錄上一次 tickoldclock += ms * CLOCKS_PER_SEC / 1000;  // 更新 tickif (clock() > oldclock)          // 如果已經超時,無需延時oldclock = clock();elsewhile (clock() < oldclock)      // 延時Sleep(1);            // 釋放 CPU 控制權,降低 CPU 占用率,精度 10~16ms//      Sleep(0);            // 更高精度、更高 CPU 占用率,精度 1ms
}

復制好后,點擊上方綠色空三角運行。(運行效果如下)

以上便完成了全部的環境配置,開啟開發游戲之旅

基本游戲邏輯

首先需要包含頭文件 #include<easyx.h>來調用圖形函數

想要將代碼中的效果展現出來,需要一個圖形化窗口,并非是黑框框。

所以,第一步初始化一個圖形化窗口。

initgraph(800,800);

該函數運行后,除了命令提示符的黑窗口之外,還會產生一個新的窗口,此時窗口內是空的。

如果我們想把外部圖片貼上去,需要一個容器儲存外部圖片

IMAGE img;//聲明一個可以存儲外部圖片的容器

然后儲存外部圖片進入容器操作,&img是獲取容器地址,“photo.png”是需要引入圖片的路徑

(路徑可分為相對路徑和絕對路徑,我推薦將圖片和源程序放到同一個根目錄中,既方便引用,又方便后續對于游戲的封裝)

loadimage(&img, "photo.png");加載圖片進容器

那儲存好的圖片如何顯示在屏幕上,我們需要函數將圖片貼到屏幕上。

圖中,x,y,前兩個函數是指貼入圖片的坐標(圖片左上角頂點的坐標),

&img參數指貼入的圖片容器,確定具體貼入哪個圖片。

putimage(x,y, &img);

現在基本的圖片顯示便有了。

如果我們想讓這個圖片動起來,很好理解,我們只需要逐漸改變putimage函數的坐標參數就可以。

需要一個循環來刷新新的圖像(改變坐標之后的貼圖),(還需要刷新屏幕,或者使用背景覆蓋法)

1,刷新屏幕:FlushBatchDraw ();(不需要參數) 清除掉上一個貼圖,執行目前的貼圖。

FlushBatchDraw ();

2,背景覆蓋法:可以每次循環(先貼背景(覆蓋掉上個位置的貼圖)再貼改變坐標后的貼圖)

關于圖像的移動

#include<stdio.h>
#include<easyx.h>
#include<windows.h>IMAGE back;
IMAGE img;
int main()
{loadimg (&back,"選中背景圖片的路徑");
loadimg (&img,"選中目標圖片的路徑");
for(int i=1;i<=500;i++)
{
putimage(0,0,&back);
putimage(i,i,&img);
Sleep(100);
}return 0;
}

其中,&back 是獲取背景(IMAGE back 容器存儲著與窗口大小一致的背景圖片),

所以每次貼圖的坐標是0,0,

&img存取的則是需要移動的目標貼圖,

每次循環,會在不同坐標貼上目標圖片,

由于每次循環都會貼一次背景圖,所以會覆蓋掉上次的目標貼圖,再貼下次的目標貼圖,

這樣,窗口中就始終只能看到一個目標貼圖,且位置在不停發生改變,產生目標圖片移動的效果。

(Sleep(100)是沒隔100ms也就是每0.1秒刷新一次位置,不然上述循環會在一瞬間結束,無法觀察,該函數在Windows.h庫內)

上述代碼就會產生一個從(1,1)移動到(500,500)的圖像。

自主控制實時移動

既然貼圖函數的坐標參數決定了目標圖像的位置,那么我們如果按下相應的按鍵改變坐標參數,便可實現用按鍵控制移動,

我們可以調用一個Windows.h函數 GetAsyncKeyState('D') ,括號內參數是被檢測的按鍵,

如果D( 不分大小寫)按鍵被按下,則返回非零值,否則返回零,

所以,該代碼便可檢測按鍵的實時狀態,如果按下D則x++(向右移動)

if(GetAsyncKeyState('D'))
x++;

所以整體移動函數模塊就是(其中設置了范圍,防止目標移動出邊界),每次增加或減少的值不是1,而是一個預先定義好的值,可以自由控制移動速度(#define SPEED 10)

void control_move()//控制人物移動
{if (GetAsyncKeyState('D') && hero.x < width)//角色右移{hero.x += SPEED;}if (GetAsyncKeyState('A') && hero.x > 0)//角色左移{hero.x -= SPEED;}if (GetAsyncKeyState('W') && hero.y > 0)//角色上移hero.y -= SPEED;if (GetAsyncKeyState('S') && hero.y < high)//角色下移hero.y += SPEED;
}

然后把這個函數放入主循環內,因為游戲是一致運行的,所以全部需要改變的行為都要放到一個主循環內,由于GetAsyncKeyState是非阻塞性函數,也就是說,即使沒有按鍵按下,主循環依然循環著,游戲持續運行著,只是目標貼圖未移動。

int main()
{
....省略
while(1)
{
control_move();
putimage(0,0,&back);
putimage(i,i,&img);
}
return 0;}

關于目標發射物(開發目標遠程攻擊)

struct bang{
int x;//坐標
int y;
bool live = false;//是否存活
}fire;if(GetAsyncKeyState('j'))
fire.live = true;if(fire.live)
{
fire.x+=SPEED;
putimage(x,y,&img);
}

需要設定發射物的結構體,如果檢測到J按鍵,則讓發射物存活,并且自定義邏輯發射出去。

圖中假設只有一個發射物,并且橫向發射移動,如果需要發射多個,則只需要將結構體變量改成結構體變量數組,然后每次判斷存活和移動的操作加一個外層數組遍歷,同時同步所有狀態。

現在基本的移動和發射邏輯都已說明

我們還需要一些輔助函數代碼塊,比如時間戳,每間隔多少ms運行一次函數體,且不阻塞主循環

bool timer(int ms, int id)//時間戳
{static DWORD t[500];// 將 clock() 的返回值轉換為 DWORD 類型if (static_cast<DWORD>(clock()) - t[id] > static_cast<DWORD>(ms)){t[id] = static_cast<DWORD>(clock());return true;}return false;
}
/*時間戳*/

飛機大戰測試

1,頭文件

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<time.h>
#include<windows.h>
#include<stdlib.h>

2,設定圖形變量存儲圖片

IMAGE BACK_DROP;
IMAGE PLANE_1;//飛機1
IMAGE PLANE_2;//飛機2
IMAGE DG_1;//敵機1
IMAGE DG_2;//敵機2
IMAGE BULLET_1;//子彈1
IMAGE BULLET_2;//子彈2

3,預定義需要使用參數值? ?設定?結構體(飛機和敵機)

enum My {WIDTH = 600,HEIGHT = 864,BULLET_NUM = 300,SHIP_SPEED = 2,BULLET_SPEED = 30,ENEMY_NUM = 5,ENEMY_SPEED = 1,
};struct ZT//狀態結構體
{int x;int y;  //坐標int hp = 100;//血量bool live = false;//是否存活int width;int height;
};ZT myplane;//飛機ZT BULLET[BULLET_NUM];//子彈ZT ENEMY[ENEMY_NUM];//敵機

基本互動(如果子彈和敵機圖像有交叉,則判定擊中,減血,血量<=0則判定死亡 )

int play()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){continue;}for (int j = 0;j < BULLET_NUM;j++){if (!BULLET[i].live){continue;}//檢測擊中if (BULLET[j].x > ENEMY[i].x && BULLET[j].x<ENEMY[i].x + ENEMY[i].width&& BULLET[j].y>ENEMY[i].y && BULLET[j].y < ENEMY[i].y + ENEMY[i].height){BULLET[i].live = false;ENEMY[i].hp--;}//掉血就去死,ok?if (ENEMY[i].hp == 0){ENEMY[i].live = false;}}}return 0;
}

子彈和敵機的創建

int PLANE_MY()//構建飛機和子彈和敵機
{//繪制飛機putimage(myplane.x, myplane.y, &PLANE_1,NOTSRCERASE);putimage(myplane.x, myplane.y, &PLANE_2, SRCINVERT);//繪制子彈for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){putimage(BULLET[i].x, BULLET[i].y, &BULLET_2, NOTSRCERASE);putimage(BULLET[i].x, BULLET[i].y, &BULLET_1, SRCINVERT);}}//繪制敵機for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){putimage(ENEMY[i].x, ENEMY[i].y, &DG_2, NOTSRCERASE);putimage(ENEMY[i].x, ENEMY[i].y, &DG_1, SRCINVERT);}}return 0;
}
int createbullet()//子彈創建
{for (int i = 0;i <= BULLET_NUM;i++){if (!BULLET[i].live){BULLET[i].x = myplane.x + 49;BULLET[i].y = myplane.y;BULLET[i].live = true;break;}}return 0;
} 

詳細解釋一下該部分(使用兩張互補的色差圖像可以實現透明貼圖,后續有優化版本)

putimage(BULLET[i].x, BULLET[i].y, &BULLET_2, NOTSRCERASE);putimage(BULLET[i].x, BULLET[i].y, &BULLET_1, SRCINVERT);

子彈和敵機的移動,以及碰撞檢測(檢測可以放到里面,也可以獨立出一個函數)非

int bulletmove()//子彈移動
{for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){BULLET[i].y -= BULLET_SPEED;}if (BULLET[i].y < 0){BULLET[i].live = false;}}return 0;
}
int createenemy()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){ENEMY[i].x = rand() % (WIDTH - 60);ENEMY[i].y = 0;ENEMY[i].live = true;break;}enemyhp(i);}return 0;
}int enemymove()//敵機的移動
{for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){ENEMY[i].y += ENEMY_SPEED;}if (ENEMY[i].y > HEIGHT){ENEMY[i].live = false;}
}return 0;
}
int penzhuang()//碰撞檢測
{for (int i = 0;i <= ENEMY_NUM;i++){if (myplane.y <= ENEMY[i].y && myplane.y >= ENEMY[i].y + ENEMY[i].height&& myplane.x >= ENEMY[i].x && myplane.x <= ENEMY[i].x + ENEMY[i].width){myplane.live = false;exit(0);}}
}

需要采用雙緩沖繪圖法,可以去除游戲循環的卡頓,

BeginBatchDraw();	開始批量繪圖。寫在循環外
EndBatchDraw();	結束批量繪制,并執行未完成的繪制任務。循壞外,程序結束前
FlushBatchDraw();	執行未完成的繪制任務。寫在循環內,構圖后,延遲前

飛機大戰代碼匯總

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<time.h>
#include<windows.h>
#include<stdlib.h>
//牢籠
IMAGE BACK_DROP;
IMAGE PLANE_1;
IMAGE PLANE_2;
IMAGE DG_1;
IMAGE DG_2;
IMAGE BULLET_1;
IMAGE BULLET_2;enum My {WIDTH = 600,HEIGHT = 864,BULLET_NUM = 300,SHIP_SPEED = 2,BULLET_SPEED = 30,ENEMY_NUM = 5,ENEMY_SPEED = 1,
};
const int MAX = 10;struct ZT//狀態結構體
{int x;int y;  //坐標int hp = 100;//血量bool live = false;//是否存活int width;int height;
};ZT myplane;//飛機ZT BULLET[BULLET_NUM];//子彈ZT ENEMY[ENEMY_NUM];//敵機int DRAW_BACKDROP()//構造背景圖
{putimage(0, 0, &BACK_DROP);return 0;
}
int enemyhp(int i)
{ENEMY[i].hp = 1;ENEMY[i].width = 90;	ENEMY[i].height = 100;return 0;
}int play()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){continue;}for (int j = 0;j < BULLET_NUM;j++){if (!BULLET[i].live){continue;}//檢測擊中if (BULLET[j].x > ENEMY[i].x && BULLET[j].x<ENEMY[i].x + ENEMY[i].width&& BULLET[j].y>ENEMY[i].y && BULLET[j].y < ENEMY[i].y + ENEMY[i].height){BULLET[i].live = false;ENEMY[i].hp--;}//掉血就去死,ok?if (ENEMY[i].hp == 0){ENEMY[i].live = false;}}}return 0;
}int PLANE_MY()//構建飛機和子彈和敵機
{//繪制飛機putimage(myplane.x, myplane.y, &PLANE_1,NOTSRCERASE);putimage(myplane.x, myplane.y, &PLANE_2, SRCINVERT);//繪制子彈for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){putimage(BULLET[i].x, BULLET[i].y, &BULLET_2, NOTSRCERASE);putimage(BULLET[i].x, BULLET[i].y, &BULLET_1, SRCINVERT);}}//繪制敵機for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){putimage(ENEMY[i].x, ENEMY[i].y, &DG_2, NOTSRCERASE);putimage(ENEMY[i].x, ENEMY[i].y, &DG_1, SRCINVERT);}}return 0;
}
int createbullet()//子彈創建
{for (int i = 0;i <= BULLET_NUM;i++){if (!BULLET[i].live){BULLET[i].x = myplane.x + 49;BULLET[i].y = myplane.y;BULLET[i].live = true;break;}}return 0;
} 
bool timer(int ms, int id)//制造隨機性
{static DWORD t[MAX];if (clock() - t[id] > ms){t[id] = clock();return true;}return false;}int bulletmove()//子彈移動
{for (int i = 0;i <= BULLET_NUM;i++){if (BULLET[i].live){BULLET[i].y -= BULLET_SPEED;}if (BULLET[i].y < 0){BULLET[i].live = false;}}return 0;
}
int createenemy()
{for (int i = 0;i <= ENEMY_NUM;i++){if (!ENEMY[i].live){ENEMY[i].x = rand() % (WIDTH - 60);ENEMY[i].y = 0;ENEMY[i].live = true;break;}enemyhp(i);}return 0;
}int enemymove()//敵機的移動
{for (int i = 0;i <= ENEMY_NUM;i++){if (ENEMY[i].live){ENEMY[i].y += ENEMY_SPEED;}if (ENEMY[i].y > HEIGHT){ENEMY[i].live = false;}
}return 0;
}
int penzhuang()//碰撞檢測
{for (int i = 0;i <= ENEMY_NUM;i++){if (myplane.y <= ENEMY[i].y && myplane.y >= ENEMY[i].y + ENEMY[i].height&& myplane.x >= ENEMY[i].x && myplane.x <= ENEMY[i].x + ENEMY[i].width){myplane.live = false;exit(0);}}
}int main()
{initgraph(600, 1000);loadimage(&BACK_DROP, "back.jpg");loadimage(&PLANE_1,"plane1.png");loadimage(&PLANE_2, "plane2.png");loadimage(&DG_1, "D1.png");loadimage(&DG_2, "D2.png");loadimage(&BULLET_1, "zd1.png");loadimage(&BULLET_2, "zd2.png");myplane.x = 200;myplane.y = 500;myplane.live = true;for (int i = 0;i <= BULLET_NUM;i++){BULLET[i].x = 0;BULLET[i].y = 0;BULLET[i].live = false;}while (1){if (_kbhit())//檢測案件發生{char c = _getch();//獲取鍵盤信息switch (c)//控制移動{case 'w'://上if (myplane.y >= 10)myplane.y -= 20;break;case 's'://下if (myplane.y <= 885)myplane.y += 20;break;case 'a'://左if (myplane.x >= 20)myplane.x -= 20;break;case 'd'://右if (myplane.x <= 465)myplane.x += 20;break;case 'j':createbullet();break;}}else {Sleep(100);//基本刷新頻率}DRAW_BACKDROP();//構建背景圖//FlushBatchDraw();PLANE_MY();//基本原件生成bulletmove();//子彈移動if (timer(500, 0))//控制敵機的出現頻率{createenemy();}if (timer(30, 2))	{	enemymove();}play();//打penzhuang();//碰撞檢測}//主循環return 0;
}//八個小時,老弟。

?需要鏈接圖片才可以運行哦,(上述說過,需要將目標圖片放入指定容器)

上述可能會不太好理解,純干貨,可以參照b站課程

原創優化游戲邏輯的2D角色扮演游戲框架

先展示優化的游戲函數

設定好的全局變量和常量宏

#include <graphics.h>//圖形算法庫
#include <conio.h>//控制臺交流庫
#include<windows.h>//系統函數庫
#include<stdio.h>//標準輸入輸出庫
#include<time.h>//時間定義庫
#include<easyx.h>//圖形界面庫
#include<math.h>//數學函數庫#pragma comment( lib, "MSIMG32.LIB")//圖形鏈接庫
//============================================================================預處理
#define M_PI 3.1415926  //圓周率#define HERO_SPEED  1     //hero.移動速度#define HERO_JUMP_SPEED 10 //hero.跳躍幀高度#define HERO_JUMP_NUM  5   //hero.跳躍幀數#define LIGHT_SWORD_SPEED 3 //light_sword.光刃飛行速度#define DRAGON_NUM_MAX 2 //龍同時存在最大數量#define DRAGON_SPEED 2 //龍的移動速度
//============================================================================常量宏
int HEIGHT = 1000;//當前屏幕設備的高度(單位毫米)int WIDTH = 1700;//當前屏幕設備的寬度(單位毫米)IMAGE back;//背景IMAGE stop_imgR[13];//靜止 右 待機動作IMAGE stop_imgL[13];//靜止 左 待機動作IMAGE run_imgR[5];//奔跑 右 動作IMAGE run_imgL[5];//奔跑 左 動作IMAGE raise_sword;//舉劍的動作IMAGE light_sword_imgR;//右光刃
IMAGE light_sword_imgL;//左光刃IMAGE HP_img;//血量顯示IMAGE MP_img;//藍量顯示IMAGE TX_ADD_HP[16]; //加血特效圖IMAGE dragon_imgR[7]; //右 龍圖片
IMAGE dragon_imgL[7]; //左 龍圖片IMAGE light_effect[31]; //受擊光效圖片int run_num = 1;//移動動作循環底碼int stop_num = 1;//待機動作循環底碼int TX_ADD_HP_num = 1;//特效圖像循環底碼int dragon_img_num = 1;//龍圖運動循環底碼int Affected_img_num = 1;//基礎光刃受擊特效圖循環底碼bool Previous_direction = true;//前一時刻方向判定量int dragon_rand_move_num[DRAGON_NUM_MAX + 1];//龍的隨機運動底碼
int dragon_rand_pursuit_num[DRAGON_NUM_MAX + 1];//龍的隨機追擊底碼
//=============================================================================全局變量

設定好的結構體

struct role {int x = 200;			//hero.x坐標int y = 100;			//hero.y坐標int blood = 100;    //hero.血量int blue = 100;     //hero.藍量bool live = true;   //hero.存活bool ground = true; //hero.觸地
}hero;
/*人物狀態結構體*/struct sword {int x = 0;//光刃x坐標int y = 0;//光刃y坐標bool live = false;//光刃存活bool direction = true;//光刃方向
};
/*基本遠程攻擊結構體*/struct sword light_sword[11];//光刃struct Special_effects {int x = 1; //特效.x坐標int y = 1; //特效.y坐標bool live = false; //是否激活
};/*基本特效結構體*/
struct Special_effects add_blood; //加血特效
struct Special_effects Affected_effect[11];//基礎光刃受擊效果struct move {//基本移動體坐標int x = 800;int y = 500;//坐標int HP = 100;//血量int speed_x = 10;int speed_y = 10;//速度bool live = false;//是否存活bool if_move = true; //是否能移動bool direction = true;//向左向右bool pursuit = true;//是否追擊int die_num_zhen = 0;//死亡后的幀數
};
//基本敵對目標結構體
struct move dragon[DRAGON_NUM_MAX + 1]; //敵龍  同時最多存在五只//==============================================================================結構體

加載圖片

void load()//加載圖片素材
{loadimage(&back, "back.png", 1700, 1000);//背景圖的加載loadimage(&HP_img, "HP.png", 100, 50);//血條HP圖片加載loadimage(&MP_img, "MP.png", 100, 50);//藍條MP圖片加載//loadimage(&raise_sword, "attack.png", 400, 400);//攻擊舉劍動作圖片加載loadimage(&light_sword_imgR, "光刃.png", 400, 400);//右光刃攻擊特效圖片加載loadimage(&light_sword_imgL, "光刃f.png", 400, 400);//左光刃攻擊特效圖片加載for (int i = 1;i <= 9;i++)//01.png  02.png  03.png  04........{char str[50];sprintf_s(str, "0%d.png", i);loadimage(&stop_imgR[i], str, 200, 200);//加載待機動作}for (int x = 10;x <= 12;x++){char str2[50];sprintf_s(str2, "%d.png", x);loadimage(&stop_imgR[x], str2, 200, 200);//加載 右 待機動作}for (int y = 1;y <= 4;y++){char str3[50];char str4[50];sprintf_s(str3, "run%d.png", y);loadimage(&run_imgR[y], str3, 180, 180);//加載 右 奔跑動作sprintf_s(str4, "frun%d.png", y);loadimage(&run_imgL[y], str4, 180, 180);//加載 左 奔跑動作}for (int a = 1; a <= 12; a++){char str5[50];sprintf_s(str5, "fs%d.png", a);loadimage(&stop_imgL[a], str5, 200, 200);//加載 左 待機動作}for (int i = 1;i <= 15;i++)//加載加血特效{char str6[50];sprintf_s(str6, "tx%d.png", i);loadimage(&TX_ADD_HP[i], str6, 400, 400);}for (int i = 1;i <= 6;i++)//加載龍的素材圖{char str7[50];sprintf_s(str7, "dg%d.png", i);loadimage(&dragon_imgR[i], str7, 200, 200);char str8[50];sprintf_s(str8, "dgf%d.png", i);loadimage(&dragon_imgL[i], str8, 200, 200);}for (int i = 1;i <= 30;i++)//加載受擊光效{char str9[50];sprintf_s(str9, "gx%d.png", i);loadimage(&light_effect[i], str9, 200, 200);}}
//加載圖片素材

時間戳

bool timer(int ms, int id)//時間戳
{static DWORD t[500];// 將 clock() 的返回值轉換為 DWORD 類型if (static_cast<DWORD>(clock()) - t[id] > static_cast<DWORD>(ms)){t[id] = static_cast<DWORD>(clock());return true;}return false;
}
/*時間戳*/

獲取屏幕參數(全屏的關鍵)

/*獲取當前屏幕的參數*/void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //png_windows透明貼圖
{HDC dstDC = GetImageHDC(dstimg);HDC srcDC = GetImageHDC(srcimg);int w = srcimg->getwidth();int h = srcimg->getheight();BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}

前面我們每個目標都要采用兩張疊加的圖片才能實現透明貼圖,而該函數只需要使用wps工具將單個圖片背景設置成win的透明背景,然后插入該函數可自動剔除掉背景


void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //png_windows透明貼圖
{HDC dstDC = GetImageHDC(dstimg);HDC srcDC = GetImageHDC(srcimg);int w = srcimg->getwidth();int h = srcimg->getheight();BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
/*windows.h 的png透明貼圖工具*/

原創函數,用于可控范圍的切換圖片目標,實現特定范圍的人物移動行走效果,和特效

void random_nums()//一幀內生成十個的隨機數,前五個賦值給龍的判斷移動變量,后五個給龍的追擊判斷變量
{int num = 10;int used[100] = { 0 };  // 標記數組,初始化為 0int numbers[10];srand((unsigned int)time(NULL));  // 初始化隨機數種子for (int i = 0; i < num; i++) {int num;do {num = rand() % 100;  // 生成 0 到 RANGE - 1 之間的隨機數} while (used[num]);  // 如果該數字已被使用,則重新生成numbers[i] = num;used[num] = 1;  // 標記該數字已被使用}// 輸出生成的隨機數for (int i = 1; i <= num / 2; i++) {dragon_rand_move_num[i] = numbers[i];}for (int i = num / 2 + 1;i <= num;i++){dragon_rand_pursuit_num[i - num / 2] = numbers[i];}
}
//一幀內生成特定數量的隨機數int cycle_count(int min, int max, int type)//調用返回值從min~max之間的單向循環
{static int count[10];while (count[type] < min - 1)count[type]++;count[type]++;if (count[type] > max)count[type] = min;return count[type];
}//不同type參數分配不同的靜態變量count
/*可控范圍的底碼循環,用于運動圖片的切換*/
控制特效的單次便利圖像運行,單次便利結束后,將傳入的bool類型指針變為falsevoid draw_effect_ADD_blood()
{if (add_blood.live)transparentimage3(NULL, hero.x - 100, hero.y - 150, &TX_ADD_HP[TX_ADD_HP_num]);
}

控制移動(通過檢測上次的移動方向,可以知道某時刻角色的面朝向,從而決定貼圖朝向)

void control_hero()//控制人物移動
{if (GetAsyncKeyState('D') && hero.x < 1550)//角色右移{hero.x += HERO_SPEED;Previous_direction = true;}if (GetAsyncKeyState('A') && hero.x > -5)//角色左移{hero.x -= HERO_SPEED;Previous_direction = false;}if (GetAsyncKeyState('W') && hero.y > -5)//角色上移hero.y -= HERO_SPEED;if (GetAsyncKeyState('S') && hero.y < 850)//角色下移hero.y += HERO_SPEED;
}
/*控制角色移動*/

發射物光刃一體化程序

//創造光刃void move_sword()
{for (int i = 1;i <= 10;i++){if (light_sword[i].live){if (light_sword[i].direction)//是否朝右light_sword[i].x += LIGHT_SWORD_SPEED;elselight_sword[i].x -= LIGHT_SWORD_SPEED;}}
}
//移動光刃void draw_sword()
{for (int i = 1;i <= 10;i++)if (light_sword[i].live){if (light_sword[i].direction)transparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgR);elsetransparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgL);}
}
//繪畫光刃void draw_HPMP()
{transparentimage3(NULL, 10, 10, &HP_img);transparentimage3(NULL, 10, 70, &MP_img);
}//對基本光刃受擊特效的繪畫void Attack_detection()
{for (int i = 1;i <= 10;i++){int ctr = 1;for (int a = 1;a <= DRAGON_NUM_MAX;a++){if (light_sword[i].x - dragon[a].x<200 && light_sword[i].x - dragon[a].x>-200 && light_sword[i].live)if (dragon[a].live)if (light_sword[i].y - dragon[a].y<0 && light_sword[i].y - dragon[a].y>-200){dragon[a].HP -= 20;Affected_effect[i].x = dragon[a].x + 50;Affected_effect[i].y = dragon[a].y + 30;Affected_effect[i].live = true;light_sword[i].live = false;ctr = 0;break;}}if (ctr == 0)break;}
}
//基本光刃命中判定以及反饋

游戲特效

//創造加血特效 (內含按鍵 U )int control_effect_count(int min, int max, bool* live, int type)//控制特效的單次循環運行
{static int count[10] = { min - 1 };count[type]++;if (count[type] >= max + 1){*live = false;count[type] = min - 1;return count[type] + 1;}return count[type];
}//加血特效的繪畫void select_dragon_speed() //根據距離分配速度
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].pursuit && dragon[i].live){//同時滿足追擊和移動和存活條件后,賦值追擊速度double cx = (double)(dragon[i].x - hero.x);  //敵我x坐標差double cy = (double)(dragon[i].y - hero.y);  //敵我y坐標差double cz = sqrt(cx * cx + cy * cy);     //絕對距離if (cx == 0 && cy == 0)//防止敵我目標重合帶來的除0bug{cz = 1;}double cxz = cx / cz;double cyz = cy / cz;//移動方向參數dragon[i].speed_x = (int)(-DRAGON_SPEED * cxz);dragon[i].speed_y = (int)(-DRAGON_SPEED * cyz);//分配速度}
}

用算法賦予目標自動尋敵并且追擊的效果

//根據敵我位移分配速度和狀態void dragon_move()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].live && dragon[i].pursuit){//基本移動dragon[i].x += dragon[i].speed_x;dragon[i].y += dragon[i].speed_y;}if (dragon[i].speed_x > 0)dragon[i].direction = false;elsedragon[i].direction = true;}
}

敵對目標的創建

void dragon_move()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].live && dragon[i].pursuit){//基本移動dragon[i].x += dragon[i].speed_x;dragon[i].y += dragon[i].speed_y;}if (dragon[i].speed_x > 0)dragon[i].direction = false;elsedragon[i].direction = true;}
}
//龍的移動void draw_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].live){if (dragon[i].direction)transparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgR[dragon_img_num]);elsetransparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgL[dragon_img_num]);}
}
//龍的繪畫void Stop_the_Dragon_Crossing_Realm()//阻止龍的越界
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].x <= 20)// 注意30-20要 > speed_x,防止瞬間越界{dragon[i].x = 30;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].x >= 1680)// 注意980-970要 > speed_x,防止瞬間越界{dragon[i].x = 1670;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].y <= 20)// 注意30-20要 > speed_y,防止瞬間越界{dragon[i].y = 30;dragon[i].speed_y = -dragon[i].speed_y;}if (dragon[i].y >= 980)// 注意1680-1670要 > speed_y,防止瞬間越界{dragon[i].y = 970;dragon[i].speed_y = -dragon[i].speed_y;}}
}
//阻止龍越界void creat_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].HP <= 0 && dragon[i].live){dragon[i].die_num_zhen = 0;dragon[i].live = false;//dragon[i].deathTime = clock(); // 更新死亡時間}if (!dragon[i].live){if (dragon[i].die_num_zhen <= 4)//4*0.5=2scontinue;//if (clock() - dragon[i].deathTime < 2000) continue; // 5 秒內不重新生成dragon[i].x = 800;dragon[i].y = 500;dragon[i].live = true;dragon[i].HP = 100; // 重新生成時恢復血量break;}}
}
//創造龍,附帶空地才創造void dragon_x_dragon()//兩條龍之間保持距離,避免重疊
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){for (int a = 1;a <= i;a++){if (dragon[i].x - dragon[a].x <= 200 && dragon[i].x - dragon[a].x > 0){//  dragon[i]在左  <- -> dragon[i+1]在右 if (dragon[a].speed_x > 0)dragon[a].speed_x = 0;//如果左邊的在右移則水平停止if (dragon[i].speed_x < 0)dragon[i].speed_x = 0;}if (dragon[a].x - dragon[i].x <= 200 && dragon[a].x - dragon[i].x > 0){//   dragon[i+1]在左 <- ->  dragon[i]在右if (dragon[i].speed_x > 0)dragon[i].speed_x = 0;if (dragon[a].speed_x < 0)dragon[a].speed_x = 0;}}}
}
//兩條龍之間保持距離,避免重疊,該函數需要放到獲取所有速度之后void draw_light_effect()
{for (int i = 1;i <= 10;i++)if (Affected_effect[i].live)transparentimage3(NULL, Affected_effect[i].x, Affected_effect[i].y, &light_effect[Affected_img_num]);
}

組合的mian函數主運行塊

int main()
{Get_Height_And_Width(&HEIGHT, &WIDTH);//獲取屏幕參數,構建全屏窗口initgraph(WIDTH, HEIGHT);//初始化圖形界面窗口load();//加載圖片putback();//張貼背景BeginBatchDraw();//開啟雙緩沖繪圖srand(time(0));//設定隨機種子while (true){putback();//背景繪畫control_hero();//控制角色移動   (控制按鍵:W,A,S,D )Select_texture();//控制選擇人物狀態并繪圖出人物timer_thing();//需要時間延遲的事件集合(內含控制按鍵J)select_dragon_speed();//賦予龍追擊的能力Attack_detection();//受擊檢測dragon_x_dragon();//防止龍的重疊Stop_the_Dragon_Crossing_Realm();//繪畫{draw_sword();//光刃的繪畫draw_HPMP();//狀態條的繪畫draw_effect_ADD_blood();//加血特效的繪畫draw_dragon();//繪畫龍draw_light_effect();}//移動{move_sword();//光刃的移動}{creat_add_HP();//創造加血特效 (內含按鍵 U )}beyond_sword_boundary();//超出邊界的光刃判斷消失FlushBatchDraw();//刷新緩沖繪圖//cleardevice();}EndBatchDraw();//結束緩沖繪圖exit(0);//退出程序return 0;
}

該游戲總代碼

#include <graphics.h>//圖形算法庫
#include <conio.h>//控制臺交流庫
#include<windows.h>//系統函數庫
#include<stdio.h>//標準輸入輸出庫
#include<time.h>//時間定義庫
#include<easyx.h>//圖形界面庫
#include<math.h>//數學函數庫#pragma comment( lib, "MSIMG32.LIB")//圖形鏈接庫
//============================================================================預處理
#define M_PI 3.1415926  //圓周率#define HERO_SPEED  1     //hero.移動速度#define HERO_JUMP_SPEED 10 //hero.跳躍幀高度#define HERO_JUMP_NUM  5   //hero.跳躍幀數#define LIGHT_SWORD_SPEED 3 //light_sword.光刃飛行速度#define DRAGON_NUM_MAX 2 //龍同時存在最大數量#define DRAGON_SPEED 2 //龍的移動速度
//============================================================================常量宏
int HEIGHT = 1000;//當前屏幕設備的高度(單位毫米)int WIDTH = 1700;//當前屏幕設備的寬度(單位毫米)IMAGE back;//背景IMAGE stop_imgR[13];//靜止 右 待機動作IMAGE stop_imgL[13];//靜止 左 待機動作IMAGE run_imgR[5];//奔跑 右 動作IMAGE run_imgL[5];//奔跑 左 動作IMAGE raise_sword;//舉劍的動作IMAGE light_sword_imgR;//右光刃
IMAGE light_sword_imgL;//左光刃IMAGE HP_img;//血量顯示IMAGE MP_img;//藍量顯示IMAGE TX_ADD_HP[16]; //加血特效圖IMAGE dragon_imgR[7]; //右 龍圖片
IMAGE dragon_imgL[7]; //左 龍圖片IMAGE light_effect[31]; //受擊光效圖片int run_num = 1;//移動動作循環底碼int stop_num = 1;//待機動作循環底碼int TX_ADD_HP_num = 1;//特效圖像循環底碼int dragon_img_num = 1;//龍圖運動循環底碼int Affected_img_num = 1;//基礎光刃受擊特效圖循環底碼bool Previous_direction = true;//前一時刻方向判定量int dragon_rand_move_num[DRAGON_NUM_MAX + 1];//龍的隨機運動底碼
int dragon_rand_pursuit_num[DRAGON_NUM_MAX + 1];//龍的隨機追擊底碼
//=============================================================================全局變量struct role {int x = 200;			//hero.x坐標int y = 100;			//hero.y坐標int blood = 100;    //hero.血量int blue = 100;     //hero.藍量bool live = true;   //hero.存活bool ground = true; //hero.觸地
}hero;
/*人物狀態結構體*/struct sword {int x = 0;//光刃x坐標int y = 0;//光刃y坐標bool live = false;//光刃存活bool direction = true;//光刃方向
};
/*基本遠程攻擊結構體*/struct sword light_sword[11];//光刃struct Special_effects {int x = 1; //特效.x坐標int y = 1; //特效.y坐標bool live = false; //是否激活
};/*基本特效結構體*/
struct Special_effects add_blood; //加血特效
struct Special_effects Affected_effect[11];//基礎光刃受擊效果struct move {//基本移動體坐標int x = 800;int y = 500;//坐標int HP = 100;//血量int speed_x = 10;int speed_y = 10;//速度bool live = false;//是否存活bool if_move = true; //是否能移動bool direction = true;//向左向右bool pursuit = true;//是否追擊int die_num_zhen = 0;//死亡后的幀數
};
//基本敵對目標結構體
struct move dragon[DRAGON_NUM_MAX + 1]; //敵龍  同時最多存在五只//==============================================================================結構體
void load()//加載圖片素材
{loadimage(&back, "back.png", 1700, 1000);//背景圖的加載loadimage(&HP_img, "HP.png", 100, 50);//血條HP圖片加載loadimage(&MP_img, "MP.png", 100, 50);//藍條MP圖片加載//loadimage(&raise_sword, "attack.png", 400, 400);//攻擊舉劍動作圖片加載loadimage(&light_sword_imgR, "光刃.png", 400, 400);//右光刃攻擊特效圖片加載loadimage(&light_sword_imgL, "光刃f.png", 400, 400);//左光刃攻擊特效圖片加載for (int i = 1;i <= 9;i++)//01.png  02.png  03.png  04........{char str[50];sprintf_s(str, "0%d.png", i);loadimage(&stop_imgR[i], str, 200, 200);//加載待機動作}for (int x = 10;x <= 12;x++){char str2[50];sprintf_s(str2, "%d.png", x);loadimage(&stop_imgR[x], str2, 200, 200);//加載 右 待機動作}for (int y = 1;y <= 4;y++){char str3[50];char str4[50];sprintf_s(str3, "run%d.png", y);loadimage(&run_imgR[y], str3, 180, 180);//加載 右 奔跑動作sprintf_s(str4, "frun%d.png", y);loadimage(&run_imgL[y], str4, 180, 180);//加載 左 奔跑動作}for (int a = 1; a <= 12; a++){char str5[50];sprintf_s(str5, "fs%d.png", a);loadimage(&stop_imgL[a], str5, 200, 200);//加載 左 待機動作}for (int i = 1;i <= 15;i++)//加載加血特效{char str6[50];sprintf_s(str6, "tx%d.png", i);loadimage(&TX_ADD_HP[i], str6, 400, 400);}for (int i = 1;i <= 6;i++)//加載龍的素材圖{char str7[50];sprintf_s(str7, "dg%d.png", i);loadimage(&dragon_imgR[i], str7, 200, 200);char str8[50];sprintf_s(str8, "dgf%d.png", i);loadimage(&dragon_imgL[i], str8, 200, 200);}for (int i = 1;i <= 30;i++)//加載受擊光效{char str9[50];sprintf_s(str9, "gx%d.png", i);loadimage(&light_effect[i], str9, 200, 200);}}
//加載圖片素材bool timer(int ms, int id)//時間戳
{static DWORD t[500];// 將 clock() 的返回值轉換為 DWORD 類型if (static_cast<DWORD>(clock()) - t[id] > static_cast<DWORD>(ms)){t[id] = static_cast<DWORD>(clock());return true;}return false;
}
/*時間戳*/void Get_Height_And_Width(int* H, int* W)//獲取當前屏幕的參數
{int screenWidth = *W = GetSystemMetrics(SM_CXSCREEN);int screenHeight = *H = GetSystemMetrics(SM_CYSCREEN);
}
/*獲取當前屏幕的參數*/void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) //png_windows透明貼圖
{HDC dstDC = GetImageHDC(dstimg);HDC srcDC = GetImageHDC(srcimg);int w = srcimg->getwidth();int h = srcimg->getheight();BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
/*windows.h 的png透明貼圖工具*/void random_nums()//一幀內生成十個的隨機數,前五個賦值給龍的判斷移動變量,后五個給龍的追擊判斷變量
{int num = 10;int used[100] = { 0 };  // 標記數組,初始化為 0int numbers[10];srand((unsigned int)time(NULL));  // 初始化隨機數種子for (int i = 0; i < num; i++) {int num;do {num = rand() % 100;  // 生成 0 到 RANGE - 1 之間的隨機數} while (used[num]);  // 如果該數字已被使用,則重新生成numbers[i] = num;used[num] = 1;  // 標記該數字已被使用}// 輸出生成的隨機數for (int i = 1; i <= num / 2; i++) {dragon_rand_move_num[i] = numbers[i];}for (int i = num / 2 + 1;i <= num;i++){dragon_rand_pursuit_num[i - num / 2] = numbers[i];}
}
//一幀內生成特定數量的隨機數int cycle_count(int min, int max, int type)//調用返回值從min~max之間的單向循環
{static int count[10];while (count[type] < min - 1)count[type]++;count[type]++;if (count[type] > max)count[type] = min;return count[type];
}//不同type參數分配不同的靜態變量count
/*可控范圍的底碼循環,用于運動圖片的切換*/void control_hero()//控制人物移動
{if (GetAsyncKeyState('D') && hero.x < 1550)//角色右移{hero.x += HERO_SPEED;Previous_direction = true;}if (GetAsyncKeyState('A') && hero.x > -5)//角色左移{hero.x -= HERO_SPEED;Previous_direction = false;}if (GetAsyncKeyState('W') && hero.y > -5)//角色上移hero.y -= HERO_SPEED;if (GetAsyncKeyState('S') && hero.y < 850)//角色下移hero.y += HERO_SPEED;
}
/*控制角色移動*/void creat_sword()
{if (GetAsyncKeyState('J')){for (int i = 1;i <= 10;i++){if (!light_sword[i].live){light_sword[i].live = true;light_sword[i].x = hero.x - 100;//光刃繼承人物前坐標釋放light_sword[i].y = hero.y - 100;if (Previous_direction)//是否朝右light_sword[i].direction = true;elselight_sword[i].direction = false;break;}}}
}
//創造光刃void move_sword()
{for (int i = 1;i <= 10;i++){if (light_sword[i].live){if (light_sword[i].direction)//是否朝右light_sword[i].x += LIGHT_SWORD_SPEED;elselight_sword[i].x -= LIGHT_SWORD_SPEED;}}
}
//移動光刃void draw_sword()
{for (int i = 1;i <= 10;i++)if (light_sword[i].live){if (light_sword[i].direction)transparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgR);elsetransparentimage3(NULL, light_sword[i].x, light_sword[i].y, &light_sword_imgL);}
}
//繪畫光刃void draw_HPMP()
{transparentimage3(NULL, 10, 10, &HP_img);transparentimage3(NULL, 10, 70, &MP_img);
}
//狀態欄的構建void Select_texture()//選擇任務狀態并且繪圖
{if (GetAsyncKeyState('D'))//是否按下D{transparentimage3(NULL, hero.x, hero.y, &run_imgR[run_num]);}else {//沒有按下Dif (GetAsyncKeyState('A'))//是否按下A{transparentimage3(NULL, hero.x, hero.y, &run_imgL[run_num]);}else {//沒有按下Aif (GetAsyncKeyState('W') || GetAsyncKeyState('S')){//是否按下W或Sif (Previous_direction)//是否右朝向transparentimage3(NULL, hero.x, hero.y, &run_imgR[run_num]);//右朝向上下移動else//左朝向transparentimage3(NULL, hero.x, hero.y, &run_imgL[run_num]);//左朝向上下移動}else {//待機動作if (Previous_direction)//是否右朝向transparentimage3(NULL, hero.x, hero.y, &stop_imgR[stop_num]);//待機右朝向else//左朝向transparentimage3(NULL, hero.x, hero.y, &stop_imgL[stop_num]);//待機左朝向}}}
}
//人物動作狀態的選擇判斷繪圖void putback()
{putimage(0, 0, &back);
}
//背景圖的繪畫void beyond_sword_boundary()
{for (int i = 1;i <= 10;i++)if (light_sword[i].x<0 || light_sword[i].x>WIDTH)light_sword[i].live = false;}
//超出邊界的光刃判定消失void creat_add_HP()//創造加血特效 (內含按鍵 U )
{//觸發條件,檢驗按鍵“U" 并且 特效不存活 并且 特效已完成if (GetAsyncKeyState('U') && !add_blood.live)add_blood.live = true;
}
//創造加血特效 (內含按鍵 U )int control_effect_count(int min, int max, bool* live, int type)//控制特效的單次循環運行
{static int count[10] = { min - 1 };count[type]++;if (count[type] >= max + 1){*live = false;count[type] = min - 1;return count[type] + 1;}return count[type];
}
控制特效的單次便利圖像運行,單次便利結束后,將傳入的bool類型指針變為falsevoid draw_effect_ADD_blood()
{if (add_blood.live)transparentimage3(NULL, hero.x - 100, hero.y - 150, &TX_ADD_HP[TX_ADD_HP_num]);
}
//加血特效的繪畫void select_dragon_speed() //根據距離分配速度
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].pursuit && dragon[i].live){//同時滿足追擊和移動和存活條件后,賦值追擊速度double cx = (double)(dragon[i].x - hero.x);  //敵我x坐標差double cy = (double)(dragon[i].y - hero.y);  //敵我y坐標差double cz = sqrt(cx * cx + cy * cy);     //絕對距離if (cx == 0 && cy == 0)//防止敵我目標重合帶來的除0bug{cz = 1;}double cxz = cx / cz;double cyz = cy / cz;//移動方向參數dragon[i].speed_x = (int)(-DRAGON_SPEED * cxz);dragon[i].speed_y = (int)(-DRAGON_SPEED * cyz);//分配速度}
}
//根據敵我位移分配速度和狀態void dragon_move()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].live && dragon[i].pursuit){//基本移動dragon[i].x += dragon[i].speed_x;dragon[i].y += dragon[i].speed_y;}if (dragon[i].speed_x > 0)dragon[i].direction = false;elsedragon[i].direction = true;}
}
//龍的移動void draw_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++)if (dragon[i].live){if (dragon[i].direction)transparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgR[dragon_img_num]);elsetransparentimage3(NULL, dragon[i].x, dragon[i].y, &dragon_imgL[dragon_img_num]);}
}
//龍的繪畫void Stop_the_Dragon_Crossing_Realm()//阻止龍的越界
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].x <= 20)// 注意30-20要 > speed_x,防止瞬間越界{dragon[i].x = 30;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].x >= 1680)// 注意980-970要 > speed_x,防止瞬間越界{dragon[i].x = 1670;dragon[i].speed_x = -dragon[i].speed_x;}if (dragon[i].y <= 20)// 注意30-20要 > speed_y,防止瞬間越界{dragon[i].y = 30;dragon[i].speed_y = -dragon[i].speed_y;}if (dragon[i].y >= 980)// 注意1680-1670要 > speed_y,防止瞬間越界{dragon[i].y = 970;dragon[i].speed_y = -dragon[i].speed_y;}}
}
//阻止龍越界void creat_dragon()
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (dragon[i].HP <= 0 && dragon[i].live){dragon[i].die_num_zhen = 0;dragon[i].live = false;//dragon[i].deathTime = clock(); // 更新死亡時間}if (!dragon[i].live){if (dragon[i].die_num_zhen <= 4)//4*0.5=2scontinue;//if (clock() - dragon[i].deathTime < 2000) continue; // 5 秒內不重新生成dragon[i].x = 800;dragon[i].y = 500;dragon[i].live = true;dragon[i].HP = 100; // 重新生成時恢復血量break;}}
}
//創造龍,附帶空地才創造void dragon_x_dragon()//兩條龍之間保持距離,避免重疊
{for (int i = 1;i <= DRAGON_NUM_MAX;i++){for (int a = 1;a <= i;a++){if (dragon[i].x - dragon[a].x <= 200 && dragon[i].x - dragon[a].x > 0){//  dragon[i]在左  <- -> dragon[i+1]在右 if (dragon[a].speed_x > 0)dragon[a].speed_x = 0;//如果左邊的在右移則水平停止if (dragon[i].speed_x < 0)dragon[i].speed_x = 0;}if (dragon[a].x - dragon[i].x <= 200 && dragon[a].x - dragon[i].x > 0){//   dragon[i+1]在左 <- ->  dragon[i]在右if (dragon[i].speed_x > 0)dragon[i].speed_x = 0;if (dragon[a].speed_x < 0)dragon[a].speed_x = 0;}}}
}
//兩條龍之間保持距離,避免重疊,該函數需要放到獲取所有速度之后void draw_light_effect()
{for (int i = 1;i <= 10;i++)if (Affected_effect[i].live)transparentimage3(NULL, Affected_effect[i].x, Affected_effect[i].y, &light_effect[Affected_img_num]);
}
//對基本光刃受擊特效的繪畫void Attack_detection()
{for (int i = 1;i <= 10;i++){int ctr = 1;for (int a = 1;a <= DRAGON_NUM_MAX;a++){if (light_sword[i].x - dragon[a].x<200 && light_sword[i].x - dragon[a].x>-200 && light_sword[i].live)if (dragon[a].live)if (light_sword[i].y - dragon[a].y<0 && light_sword[i].y - dragon[a].y>-200){dragon[a].HP -= 20;Affected_effect[i].x = dragon[a].x + 50;Affected_effect[i].y = dragon[a].y + 30;Affected_effect[i].live = true;light_sword[i].live = false;ctr = 0;break;}}if (ctr == 0)break;}
}
//基本光刃命中判定以及反饋//=========================================================================功能函數的構建void timer_thing()//需要時間延遲的事件集合
{if (timer(100, 1)){//角色待機動作速率stop_num = cycle_count(1, 12, 1);}if (timer(60, 2)){//角色奔跑動作速率run_num = cycle_count(1, 4, 2);}if (timer(50, 3))  //防止一瞬間釋放過多的光刃{creat_sword();//控制光刃釋放(控制按鍵:J )}if (timer(50, 4) && add_blood.live)//控制加血特效圖片運行的延遲{TX_ADD_HP_num = control_effect_count(1, 15, &add_blood.live, 1);}if (timer(100, 5)) //控制龍的動作圖片{dragon_img_num = cycle_count(1, 6, 3);}if (timer(2000, 7)){creat_dragon();//創造龍}if (timer(10, 8)){dragon_move();//龍的移動}if (timer(10, 9)){//基礎光刃攻擊受擊特效速度控制for (int i = 1;i <= 10;i++)Affected_img_num = control_effect_count(1, 30, &Affected_effect[i].live, 2);}if (timer(500, 10)){for (int i = 1;i <= DRAGON_NUM_MAX;i++){if (!dragon[i].live)dragon[i].die_num_zhen++;}}}
//需要時間延遲的事件集合,內含J按鍵int main()
{Get_Height_And_Width(&HEIGHT, &WIDTH);//獲取屏幕參數,構建全屏窗口initgraph(WIDTH, HEIGHT);//初始化圖形界面窗口load();//加載圖片putback();//張貼背景BeginBatchDraw();//開啟雙緩沖繪圖srand(time(0));//設定隨機種子while (true){putback();//背景繪畫control_hero();//控制角色移動   (控制按鍵:W,A,S,D )Select_texture();//控制選擇人物狀態并繪圖出人物timer_thing();//需要時間延遲的事件集合(內含控制按鍵J)select_dragon_speed();//賦予龍追擊的能力Attack_detection();//受擊檢測dragon_x_dragon();//防止龍的重疊Stop_the_Dragon_Crossing_Realm();//繪畫{draw_sword();//光刃的繪畫draw_HPMP();//狀態條的繪畫draw_effect_ADD_blood();//加血特效的繪畫draw_dragon();//繪畫龍draw_light_effect();}//移動{move_sword();//光刃的移動}{creat_add_HP();//創造加血特效 (內含按鍵 U )}beyond_sword_boundary();//超出邊界的光刃判斷消失FlushBatchDraw();//刷新緩沖繪圖//cleardevice();}EndBatchDraw();//結束緩沖繪圖exit(0);//退出程序return 0;
}

游戲效果

?

該游戲資源

?游戲已經被我封裝好,分享到了網盤上,感情興趣的可以嘗試一下。

通過網盤分享的文件:封裝游戲測試.zip鏈接:

https://pan.baidu.com/s/1indM1boxj6QvrpsaIH_85Q?pwd=LONG

提取碼: LONG

提取后使用方法:

使用文件資源管理器打開,點擊Debug

點擊該運行文件,

?點擊全部解壓縮

同樣再找到該運行文件并運行

一步步操作

上述您選擇安裝的指定位置(一般和解壓后的文件一個位置)

就會出現一個軟件,點擊運行,就可以玩了

(注意:同時打開的有一個黑框框,最小化即可,不要關掉,他會獲取用戶的按鍵操作)

(W A S D 移動 U 特效 J 攻擊)(只是一個基礎2D游戲框架,未添加太多功能,感興趣的小伙伴可以按照喜好嘗試添加)

本文結束....感謝觀看。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77269.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77269.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77269.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大數據專業學習路線

大數據專業學習路線 目錄 基礎知識核心技術進階技能實戰項目職業發展學習資源學習計劃常見問題 1. 基礎知識 1.1 編程語言 Python&#xff1a;大數據分析的基礎語言 基礎語法和數據類型函數和模塊面向對象編程文件操作和異常處理常用庫&#xff1a;NumPy, Pandas, Matplot…

flink部署使用(flink-connector-jdbc)連接達夢數據庫并寫入讀取數據

flink介紹 1&#xff09;Apache Flink 是一個框架和分布式處理引擎&#xff0c;用于對無界和有界數據流進行有狀態計算。Flink 被設計在所有常見的集群環境中運行&#xff0c;以內存執行速度和任意規模來執行計算。 2&#xff09;在實時計算或離線任務中&#xff0c;往往需要…

用swift playground寫個ios應用和大模型或者網站交互

import SwiftUIstruct ContentView: View {State private var textFieldText: String ""State private var outputText: String "輸出將會顯示在這里"private let tip:String "消息已發送&#xff0c;請等待"State private var history:[Stri…

springboot+vue2集成JWT token實現權限驗證

前端項目搭建參考&#xff1a; Vue項目的搭建和啟動_vue項目啟動 csdn-CSDN博客 Vue ElementUI 登錄頁面_vue用戶登錄頁面-CSDN博客 跨域問題前端解決-CSDN博客 實現思路&#xff1a; 1. 實現的目的&#xff1a;為了保護網站安全信息&#xff0c;使用jwt進行權限驗證&#xf…

Cursor編程-從入門到精通__0409

早期的Github Copilot 最近更新了&#xff0c;支持Agent編程&#xff0c;字節跳動Trae使用&#xff08;免費&#xff09;&#xff0c;但成熟程度不如Cursor&#xff0c;Cursor前50次免費 Copilot VS Cursor*** 1&#xff0c;Cursor VSCode 二次開發&#xff0c;IDE級別 2&…

MyBatis 詳解及代碼示例

MyBatis 是一個 半自動 ORM 框架&#xff0c;主要用于 Java 與數據庫之間的持久化操作&#xff0c;它本質是對 JDBC 的封裝 全名&#xff1a;MyBatis&#xff08;前身 iBATIS&#xff09;核心作用&#xff1a;自動將 SQL 執行結果映射為 Java 對象&#xff1b;也可以將 Java 對…

1.6-抓包技術(Burp Suite\Yakit抓包\Web、APP、小程序)

1.6-抓包技術&#xff08;Burp Suite\Yakit抓包\Web、APP、小程序&#xff09; 如果要使用抓包軟件&#xff0c;基本上第一步都是要安裝證書的。原因如下&#xff1a; 客戶端&#xff08;瀏覽器或應用&#xff09;會檢測到證書不受信任&#xff0c;并彈出 證書錯誤&#xff0…

Java 大視界 -- 基于 Java 的大數據隱私保護在金融客戶信息管理中的實踐與挑戰(178)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

第十屆 藍橋杯 嵌入式 省賽

一、分析 這屆的真題&#xff0c;有點像第七屆的液位檢測。 這屆的題目開始&#xff0c;貌似比賽描述的功能&#xff0c;邏輯上變得更好梳理了。一開始就把大致的功能給你說明一遍&#xff0c;不像之前都是一塊一塊的說明。 1. 基本功能 1&#xff09;測量競賽板上電位器 R…

實現usb的MTP功能

前言:最終結果根據用戶自主選擇可實現host和device功能的切換。 效果展示: 當插入usb時設備會彈窗 當用戶選擇設備模式時pc端就會出現mtp設備盤符 實現mtp設備 ubuntu架構根文件系統通過uMTP-Responder實現usb的MTP功能 添加服務 /home/flynn/firfly_rootfs/lib/system…

React-05React中props屬性(傳遞數據),propTypes校驗,類式與函數式組件props的使用

1.類式組件props基本數據讀取與解構運算符傳遞 <script type"text/babel">// 創建組件class PersonalInfo extends React.Component {render() {// 讀取props屬性 并讀取值console.log(props,this.props);return(<ul><li>姓名&#xff1a;{this.p…

PCI認證 密鑰注入 ECC算法工具 NID_secp521r1 國密算法 openssl 全套證書生成,從證書提取公私鑰數組 x,y等

步驟 1.全套證書已經生成。OK 2.找國芯要ECC加密解密簽名驗簽代碼。給的邏輯說明沒有示例代碼很難的上。 3.集成到工具 與SP聯調。 1.用openssl全套證書生成及驗證 注意&#xff1a;這里CA 簽發 KLD 證書用的是SHA256。因為芯片只支持SHA256算法,不支持SHA512。改成統一。…

藍橋杯每日刷題c++

目錄 P9240 [藍橋杯 2023 省 B] 冶煉金屬 - 洛谷 (luogu.com.cn) P8748 [藍橋杯 2021 省 B] 時間顯示 - 洛谷 (luogu.com.cn) P10900 [藍橋杯 2024 省 C] 數字詩意 - 洛谷 (luogu.com.cn) P10424 [藍橋杯 2024 省 B] 好數 - 洛谷 (luogu.com.cn) P8754 [藍橋杯 2021 省 AB2…

oracle 數據庫字段類型為NUMBER(5,2)時,并且數據庫值為0.1,為什么Java執行SQL查出來時為“.1“?

在 Oracle 數據庫中&#xff0c;當字段類型為 NUMBER(5,2) 且存儲的值為 0.1 時&#xff0c;Java 程序查詢結果可能顯示為 ".1"&#xff08;省略前導零&#xff09;&#xff0c;這是由 Oracle JDBC 驅動默認的數字格式化行為 導致的。以下是原因分析和解決方案&#…

3月AI論文精選十篇

1. Feature-Level Insights into Artificial Text Detection with Sparse Autoencoders[1] 核心貢獻&#xff1a;通過稀疏自編碼器揭示AI生成文本的檢測特征&#xff0c;提出基于特征分布的鑒別方法。研究發現&#xff0c;AI文本在稀疏編碼空間中呈現獨特的"高頻低幅"…

STM32在裸機(無RTOS)環境下,需要手動實現隊列機制來替代FreeRTOS的CAN發送接收函數

xQueueSendToBackFromISR(ecuCanRxQueue, hcan->pRxMsg, &xHigherPriorityTaskWoken)&#xff0c;xQueueReceive(mscCanRxQueue,&mscRxMsg,0)和xQueueSendToBack(mscCanTxQueue, &TxMessageTemp, 0 )這3個函數&#xff0c;在裸機下實現&#xff1a; 在裸機&…

使用PX4,gazebo,mavros為旋翼添加下視的相機(仿真采集openrealm數據集-第一步)

目錄 一.方法一&#xff08;沒成功&#xff09; 1.運行PX4 2.運行mavros通訊 3.啟動仿真世界和無人機 &#xff08;1&#xff09;單獨測試相機 &#xff08;2&#xff09;make px4_sitl gazebo啟動四旋翼iris無人機 二.方法二&#xff08;成功&#xff09; 1.通過 rosl…

7、nRF52xx藍牙學習(nrf_gpiote.c庫函數學習)

續前一篇文章。 3、nrfx_gpiote_in_event_enable void nrfx_gpiote_in_event_enable(nrfx_gpiote_pin_t pin, bool int_enable) {NRFX_ASSERT(nrf_gpio_pin_present_check(pin));NRFX_ASSERT(pin_in_use_by_gpiote(pin));if (pin_in_use_by_port(pin)){nrf_gpiote_polarity_t…

Java 實現插入排序:[通俗易懂的排序算法系列之三]

引言 大家好!歡迎繼續關注我的排序算法系列。今天,我們要學習的是另一種非常基礎且重要的排序算法——插入排序 (Insertion Sort)。 插入排序的思路非常貼近我們日常整理撲克牌的方式,理解起來相對自然。雖然它在最壞情況下的效率不高,但在某些特定場景下,它的表現甚至優…

Java的spring boot項目編譯成功啟動報錯

問題現象&#xff1a;spring boot項目&#xff0c;候刪除一些無用代碼后&#xff0c;build成功&#xff0c;啟動時報錯&#xff1a;找不到java.util.Map或者其他對象&#xff08;用Lombok注解Data&#xff09;中的字段屬性找不到等錯誤。解答&#xff1a; 常見是Lombok版本問題…