前言:
根據前面的項目框架,搭建游戲的運行場景......
1.0 框架預覽
基于該框架首先實現游戲的運行場景
2.0 圖片文件
創建圖片文件,本次項目使用easyx作為圖形庫文件,在easyx中想要顯示圖片,需要有一張圖片和圖片的掩碼文件,創建image.h文件和image.cpp文件用于圖片設置,具體創建效果如下所示。創建的頭文件中做了函數的聲明,包含函數的x,y軸的坐標和圖片的地址,掩碼圖片的地址。
#ifndef __IMAGE_H_
#define __IMAGE_H_
#include <easyx.h>void put_trans_parent_image
(int x, int y, const IMAGE* mask, const IMAGE* img
);#endif
#define _CRT_SECURE_NO_WARNINGS
#include "image.h"void put_trans_parent_image(int x, int y, const IMAGE *mask, const IMAGE *img)
{putimage(x, y, mask, SRCAND);putimage(x, y, img, SRCPAINT);
}
3.0 程序對象
注:對于這款飛機大戰游戲,對應的對象就是精靈,然后將將所有元素的共性抽象成一個對象進行方便后續的調用,具體程序如下所示。
這個精靈對象就是所有游戲中出現的對象的共同特性,我們只需要繼承精靈,就能在此基礎上,續寫其他的對象了。將上面的代碼寫入文件 sprite.h,并添加上頭文件守衛。
#ifndef __SPRITE_H_
#define __SPRITE_H_// 飛機的屬性和方法
typedef struct sprite
{void (*draw)(sprite*);void (*update)(sprite*);int x;int y;int width;int height;
}sprite_t;#endif
hero.h文件,繼承sprite對象的屬性和方法
#ifndef __HERO_H_
#define __HERO_H_#include "sprite.h"
#include <easyx.h>typedef enum heroStatus
{hero_normal0 = 0, // 英雄處于正常的狀態hero_normal1 = 1, // 英雄處于正常的狀態hero_down0 = 3, // 英雄處于銷毀狀態1hero_down1 = 4, // 英雄處于銷毀狀態2hero_down2 = 5, // 英雄處于銷毀狀態3hero_down3 = 6, // 英雄處于銷毀狀態4hero_destory // 英雄完全被銷毀
}heroStatus_e;typedef struct hero
{sprite_t super;IMAGE* imgArrHero[6]; // 對應6種不同狀態的圖片IMAGE* imgArrHeroMask[6]; // 對應6種不同狀態的掩碼heroStatus_e status; // 英雄的狀態更換int life; // 英雄的生命值int heroUpdateCnt; // 計數值
}hero_t;void heroInit(hero_t* h);void heroDestory(hero_t* h);#endif
hero.cpp文件,對hero.h文件中的對象和參數進行處理
#define _CRT_SECURE_NO_WARNINGS
#include "hero.h"
#include <stdio.h>
#include "image.h"#define HERO_IMAGE_STRUCT 6
#define IMAGE_PAST 50
#define MASK_IMAGE_PAST 50
#define IMAGE_TYPE_NORMAL 2
#define IMAGE_TYPE_DOWN 4enum heroStatus heroStatusSqauence[7] =
{hero_normal0,hero_normal1,hero_down0,hero_down1,hero_down2,hero_down3,hero_destory
};void heroDraw(hero_t* h) // 繪制函數
{put_trans_parent_image(h->super.x,h->super.y,h->imgArrHero[h->status],h->imgArrHeroMask[h->status]);
};void heroUpdate(hero_t* h) // 飛機狀態更新
{h->heroUpdateCnt++;if (h->heroUpdateCnt >= 15){h->heroUpdateCnt = 0;if (h->life != 0){if (h->status == hero_normal0){h->status = hero_normal1;}else if (h->status == hero_normal1){h->status = hero_normal0;}}else{// 狀態向后變化if (h->status < hero_destory){h->status = heroStatusSqauence[h->status + 1];}}}
}void heroInit(hero_t *h)
{h->super.draw = (void (*)(sprite_t*))heroDraw;h->super.update = (void (*)(sprite_t*))heroUpdate;h->heroUpdateCnt = 0;h->status = hero_normal0;h->life = 1;h->super.x = 178;h->super.y = 600;for (int i = 0; i < HERO_IMAGE_STRUCT; i++){h->imgArrHero[i] = new IMAGE;h->imgArrHeroMask[i] = new IMAGE;}char imgPath[IMAGE_PAST];char imgMaskPath[MASK_IMAGE_PAST];for (int i = 0; i < IMAGE_TYPE_NORMAL; i++) // 飛機完整的2種狀態{sprintf(imgPath, "asset/img/hero/hero%d.png", i);sprintf(imgMaskPath, "asset/img/hero/hero%d_mask.png", i);loadimage(h->imgArrHero[i], imgPath);loadimage(h->imgArrHeroMask[i], imgMaskPath);}for (int i = 0; i < IMAGE_TYPE_DOWN; i++) // 飛機銷毀的4種狀態{sprintf(imgPath, "asset/img/hero/hero_down%d.png", i);sprintf(imgMaskPath, "asset/img/hero/hero_down%d_mask.png", i);loadimage(h->imgArrHero[i + 2], imgPath);loadimage(h->imgArrHeroMask[i + 2], imgMaskPath);}
}// 銷毀飛機函數
void heroDestory(hero_t* h)
{for (int i = 0; i < HERO_IMAGE_STRUCT; i++){delete h->imgArrHero[i];delete h->imgArrHeroMask[i];}
}
4.0 游戲循環
gameLoop.h文件:現在,我們將這個循環封裝成一個函數,放置到源文件 gameloop.cpp 當中。函數的參數為 sprite 對象
指針與游戲幀率 fps 。為了保證 sprite 對象的 draw 方法與 update 方法,每一幀都被執行一次。可以在循環中,調用 sprite 的 draw 方法與 update 方法。
gameLoop.cpp文件
#define _CRT_SECURE_NO_WARNINGS
#include <easyx.h>
#include "gameloop.h"
#include "sprite.h"void gameLoop(sprite_t* s, int fps)
{timeBeginPeriod(1); // 設置系統計時器的分辨率到 1 毫秒,提高時間測量精度LARGE_INTEGER startCount, endCount, F; // 聲明用于高精度計時的變量QueryPerformanceFrequency(&F); // 獲取高性能計數器每秒的頻率(即每秒計數值),存儲在 F 中BeginBatchDraw(); // 開始批處理繪圖模式,減少不必要的屏幕刷新,優化繪圖效率while(1) // 無限循環,保持游戲運行{QueryPerformanceCounter(&startCount); // 獲取當前計數器值作為起始時間點cleardevice(); // 清除繪圖設備,準備新一幀的繪制s->draw(s); // 繪制 s->update(s); // 狀態更新 QueryPerformanceCounter(&endCount); // 獲取當前計數器值作為結束時間點long long elapse = // 算從幀開始到結束所經過的時間,單位是微秒(endCount.QuadPart - startCount.QuadPart) /F.QuadPart * 1000000;while (elapse < 1000000 / fps) // 確保每一幀的時間間隔大致等于 1000000 / fps 微秒(即每秒幀數的倒數){Sleep(1);QueryPerformanceCounter(&endCount);elapse = (endCount.QuadPart - startCount.QuadPart)* 1000000 / F.QuadPart;}FlushBatchDraw(); // 將批處理中的繪圖操作立即提交并顯示在屏幕上}EndBatchDraw(); // 結束批處理繪圖模式timeEndPeriod(1); // 恢復系統計時器的默認分辨率
}
5.0 游戲場景
background.h文件,背景文件中包含背景A和背景B,背景還繼承了精靈的屬性和方法,同時包含存儲背景圖片地址的參數。
#ifndef __BACKGROUND_H_
#define __BACKGROUND_H_
#include "sprite.h"
#include <easyx.h>typedef struct background
{sprite_t super;int yA;int yB;IMAGE* imgBackground;
}background_t;void backgroundInit(background_t *);void backgroundDestory(background_t *);#endif
background.cpp文件:
backgroundpraw 為分別繪制A、B兩幅背景的函數。兩幅背景圖片的左上角坐標分別為:(0, yA),(0,yB) 圖片為 imgBackground。
backgroundupdate 更新兩幅圖片的左上角坐標,每次移動1像素。若 yA 大于等于0,則將 yA 復位
為-750,yB 復位為0。
接著就是初始化函數,將 draw 和 update 兩個方法賦值為 backgroundpraw 和backgroundupdate? yA初始值設置為-750, y8 初始值設置為0。創建并載入圖片 img/bg·png。
最后是 backgroundDestroy 函數,銷毀初始化時創建的 IMAGE 對象即可。
把當前主函數中 hero 對象,改為 background 對象,可以看到游戲循環 gameloop ,正常渲染并更新了背景對象。
6.0 渲染更新
#define _CRT_SECURE_NO_WARNINGS
#include "gameloop.h"
#include "hero.h"
#include "background.h"int main(void)
{initgraph(422, 750); // 初始化畫布setbkcolor(WHITE); // 設置畫布顏色cleardevice(); // 清除畫布hero_t h;heroInit(&h);gameLoop((sprite*)&h, 60);heroDestory(&h);background b;backgroundInit(&b);gameLoop((sprite_t*)&b, 60);backgroundDestory(&b);closegraph();return 0;
}