当前位置: 首页 > article >正文

1-2 飞机大战游戏场景

前言:


根据前面的项目框架,搭建游戏的运行场景......


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,			// 英雄处于销毁状态1
	hero_down1   = 4,			// 英雄处于销毁状态2
	hero_down2   = 5,			// 英雄处于销毁状态3
	hero_down3   = 6,			// 英雄处于销毁状态4
	hero_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		4

enum 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;
}

7.0 运行结果



http://www.kler.cn/a/527068.html

相关文章:

  • 6.进程的使用方式
  • 20个整流电路及仿真实验汇总
  • 基于STM32的智能温控花盆设计
  • 国产碳化硅(SiC)MOSFET模块在电镀电源中全面取代进口IGBT模块
  • jEasyUI 转换 HTML 表格为数据网格
  • concurrent.futures.Future对象详解:利用线程池与进程池实现异步操作
  • 磁感应编码器实现原理和C语言实现
  • 讯飞绘镜(ai生成视频)技术浅析(四):图像生成
  • MinDoc 安装与部署
  • C++范围for和auto关键字
  • 数据结构与算法 —— 常用算法模版
  • c++进制转换
  • 计算机网络部分知识点(王道考研笔记)
  • 第05章 15 VTK中Implicit Function的作用原理与基本应用场合
  • 本地部署DeepSeek开源多模态大模型Janus-Pro-7B实操
  • vue插件安装后使用没反应
  • 一文读懂 Faiss:开启高维向量高效检索的大门
  • TCP UDP Service Model
  • 玩转大语言模型——配置图数据库Neo4j(含apoc插件)并导入GraphRAG生成的知识图谱
  • Python练习(3)
  • 计算机网络 笔记 传输层
  • flowable expression和json字符串中的双引号内容
  • DeepSeek大模型技术深度解析:揭开Transformer架构的神秘面纱
  • 4-图像梯度计算
  • Java小白入门教程:两大类型的修饰符以及示例
  • Kafka常见问题之 java.io.IOException: Disk error when trying to write to log