提瓦特幸存者2


能帮到你的话,就给个赞吧 😘


#include <iostream>
#include <windows.h>
#include <string>
#include <graphics.h>
#include <vector>
#pragma comment(lib, "MSIMG32.LIB")

class Animation {
private:
	std::vector<IMAGE*> imgs;
	int imgIndex = 0;

	int frameInterval = 0;								//帧间隔ms: 两帧动画间的时间

	int timer = 0;										//计时器: 本张动画已经播放的时间

public:
	Animation(LPCTSTR path, int num, int frameInterval);//LPCSTR 更通用的常字符指针
	~Animation();
public:
	//播放一张动画
	void play(int x, int y, int playTime);				//playTime :本张动画播放的时间
};

class Player {
private:	
	POINT playerPos{ 500,500 };							//玩家位置
	
	const int playerSpeed = 6;							//移动速度
	
	bool isLeft = false, isRight = false,				//移动方向
		isUp = false, isDown = false;					

	bool isFacingLeft = false;							//面部朝向

	const int playerWidth = 80;							//玩家高度
	const int playerHeight = 80;
	
	const int shadowWidth = 32;							//玩家阴影高度

private:
	IMAGE playerShadow;
	Animation* animationPlayerLeft;						//玩家动画
	Animation* animationPlayerRight;
public:
	Player();
	~Player();
public:
	void processMessage(const ExMessage& msg);

	void move();
	void draw(int frameInterval);
public:
	int x() const { return playerPos.x; }
	int y() const { return playerPos.y; }
};

class Enemy {
private:
	POINT enemyPos{ 0,0 };								//敌人位置

	const int enemySpeed = 2;							//移动速度

	bool isLeft = false, isRight = false,				//移动方向
		isUp = false, isDown = false;					

	bool isFacingLeft = false;							//面部朝向

	const int enemyWidth = 80;							//敌人高度
	const int enemyHeight = 80;
	const int shadowWidth = 48;							//敌人阴影高度

private:
	IMAGE enemyShadow;
	Animation* animationEnemyLeft;						//敌人动画
	Animation* animationEnemyRight;
public:
	Enemy();
	~Enemy();
public:	
	void move(const Player& player);
	void draw(int frameInterval);
};

void putImageAlpha(int x, int y, IMAGE* img);			//图像绘制(透明度)
void generateEnemy(std::vector<Enemy*>& enemys);

const int windowWidth = 1280;
const int windowHeight = 720;
const int frameInterval = 1000 / 120;

int main() {

	initgraph(windowWidth, windowHeight);	

	Player player;
	std::vector<Enemy*> enemys;
		
	IMAGE background;	
	ExMessage message;
	bool running = true;

	loadimage(&background, _T("resources/img/background.png"));

	BeginBatchDraw();

	while (running) {
		ULONGLONG startTime = GetTickCount64();

	//读数据
		peekmessage(&message);
	
	//处理数据	
		player.processMessage(message);
		player.move();

		generateEnemy(enemys);
		for (auto& enemy : enemys)
			enemy->move(player);

	//渲染
		cleardevice();

		putimage(0, 0, &background);
		player.draw(frameInterval);

		for (auto& enemy : enemys)
			enemy->draw(frameInterval);

		FlushBatchDraw();

		//120刷新
		ULONGLONG executionTime = GetTickCount64() - startTime;
		if (executionTime < frameInterval)
			Sleep(frameInterval - executionTime);
	}
}

Player::Player(){

	loadimage(&playerShadow, _T("resources/img/shadow_player.png"));

	animationPlayerLeft = new Animation(_T("resources/img/player_left_%d.png"), 6, 45);
	animationPlayerRight = new Animation(_T("resources/img/player_right_%d.png"), 6, 45);
}

Player::~Player(){
	delete animationPlayerLeft;
	delete animationPlayerRight;
}

void Player::processMessage(const ExMessage& msg){

	//判断移动方向
	if (msg.message == WM_KEYDOWN) {

		switch (msg.vkcode) {

		case VK_UP:
			isUp = true;
			break;
		case VK_DOWN:
			isDown = true;
			break;
		case VK_LEFT:
			isLeft = true;
			break;
		case VK_RIGHT:
			isRight = true;
			break;
		default:
			break;
		}
	}
	else if (msg.message == WM_KEYUP) {
		switch (msg.vkcode) {

		case VK_UP:
			isUp = false;
			break;
		case VK_DOWN:
			isDown = false;
			break;
		case VK_LEFT:
			isLeft = false;
			break;
		case VK_RIGHT:
			isRight = false;
			break;
		default:
			break;
		}

	}
}

//计算移动信息
void Player::move(){

	// x,y 代表 向量
	int x = isRight - isLeft;
	int y = isDown - isUp;
	
	double modulus = sqrt(x * x + y * y);	//向量的模

	if (modulus) {

		double vectorX = x / modulus;
		double vectorY = y / modulus;

		playerPos.x += int(playerSpeed * vectorX);
		playerPos.y += int(playerSpeed * vectorY);
	}

	//校准
	if (playerPos.x < 0)	playerPos.x = 0;
	if (playerPos.y < 0)	playerPos.y = 0;

	if (playerPos.x + playerWidth > windowWidth)	playerPos.x = windowWidth - playerWidth;
	if (playerPos.y + playerHeight > windowHeight)	playerPos.y = windowHeight - playerHeight;

	//修改面部朝向
		//等于0时,指向原先面部朝向
	if (x > 0)
		isFacingLeft = false;
	else if (x < 0)
		isFacingLeft = true;
	
}

void Player::draw(int frameInterval){

	//绘制阴影
	int xShadow = playerPos.x + (playerWidth - shadowWidth) / 2;
	int yShadow = playerPos.y + playerHeight - 8;
	putImageAlpha(xShadow, yShadow, &playerShadow);
	
	//绘制动画
	if (isFacingLeft)
		animationPlayerLeft->play(playerPos.x, playerPos.y, frameInterval);
	else
		animationPlayerRight->play(playerPos.x, playerPos.y, frameInterval);
}

Enemy::Enemy(){

	loadimage(&enemyShadow, _T("resources/img/shadow_enemy.png"));

	animationEnemyLeft = new Animation(_T("resources/img/enemy_left_%d.png"), 6, 45);
	animationEnemyRight = new Animation(_T("resources/img/enemy_right_%d.png"), 6, 45);

	enum spawnEdge { up, down, left, right };
	spawnEdge edge = spawnEdge(rand() % 4);

	switch (edge){

	case up:
		enemyPos.x = rand() % windowWidth;
		enemyPos.y = -enemyHeight;
		break;
	case down:
		enemyPos.x = rand() % windowWidth;
		enemyPos.y = windowHeight;
		break;
	case left:
		enemyPos.x = -enemyWidth ;
		enemyPos.y = rand() % windowHeight;
		break;
	case right:
		enemyPos.x = windowWidth;
		enemyPos.y = rand() % windowHeight;
		break;
	default:
		break;
	}


}

Enemy::~Enemy(){
	delete animationEnemyLeft;
	delete animationEnemyRight;
}

void Enemy::move(const Player& player){

	//怪物向玩家移动
	int x = player.x() - enemyPos.x;
	int y = player.y() - enemyPos.y;

	double modulus = sqrt(x * x + y * y);	//向量的模

	if (modulus) {

		double vectorX = x / modulus;
		double vectorY = y / modulus;

		enemyPos.x += int(enemySpeed * vectorX);
		enemyPos.y += int(enemySpeed * vectorY);
	}

	//修改面部朝向
	if (x > 0)
		isFacingLeft = false;
	else if(x < 0)
		isFacingLeft = true;
}

void Enemy::draw(int frameInterval){

	//绘制阴影
	int x = enemyPos.x + (enemyWidth - shadowWidth) / 2;
	int y = enemyPos.y + enemyHeight - 35;
	putImageAlpha(x, y, &enemyShadow);

	//等于0时,指向原先的面部朝向
	if (isFacingLeft)
		animationEnemyLeft->play(enemyPos.x, enemyPos.y, frameInterval);
	else
		animationEnemyRight->play(enemyPos.x, enemyPos.y, frameInterval);
}

void putImageAlpha(int x, int y, IMAGE* img){
	int w = img->getwidth();
	int h = img->getheight();

	/*
	AlphaBlend:	Windows GDI+ API,用于图像混合。
		GetImageHDC(nullptr), x, y, w, h:
			GetImageHDC(nullptr):获取屏幕
			x, y, w, h:	屏幕的位置,作为目标区域。(左上角坐标为x,y,宽为w,高为h)
		GetImageHDC(img), 0, 0, w, h:
			GetImageHDC(img):获取图像
			0, 0, w, h:	整个图像,作为源区域。

		{ AC_SRC_OVER,0,255, AC_SRC_ALPHA }: 将源图像以透明的方式覆盖到目标图像上,透明度由源图像的Alpha通道控制。
			AC_SRC_OVER:	源图像覆盖目标图像
			0,255:			参数,此处无作用
			AC_SRC_ALPHA:	指定源图像的Alpha通道覆盖
				图像的Alpha通道: 是图像的透明度通道,存储着每个像素的透明度信息
	*/
	AlphaBlend(GetImageHDC(nullptr), x, y, w, h, GetImageHDC(img), 0, 0, w, h, { AC_SRC_OVER,0,255, AC_SRC_ALPHA });
}

void generateEnemy(std::vector<Enemy*>& enemys){

	static const int interval = 100;
	static int timer = 0;

	if (timer % interval == 0) {
		auto enemy = new Enemy;
		enemys.push_back(enemy);
	}
	
	timer++;
	timer %= interval;
}

Animation::Animation(LPCTSTR path, int num, int frameInterval): frameInterval(frameInterval){
	TCHAR tPath[256];	// TCHAR: 更通用的字符

	for (int i = 0; i < num; i++) {
		/*
		将一个格式化的字符串 写入到 tPath 指向的字符数组中。
			格式 由 path 字符串模板决定,其中包含一些格式占位符(比如 %d、%s 等)。
			i 变量的值将用来替换 path 中对应的格式占位符。
		*/
		_stprintf_s(tPath, path, i);

		IMAGE* img = new IMAGE;

		loadimage(img, tPath);

		imgs.push_back(img);
	}
}

Animation::~Animation(){
	for (int i = 0; i < imgs.size(); i++)
		delete imgs[i];
}

void Animation::play(int x, int y, int playTime) {

	if (timer > frameInterval) {
		imgIndex++;
		imgIndex %= imgs.size();

		timer = 0;
	}

	putImageAlpha(x, y, imgs[imgIndex]);

	timer += playTime;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/909895.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot的免税商品优选购物商城的设计与实现

一、项目背景 从古至今&#xff0c;通过书本获取知识信息的方式完全被互联网络信息化&#xff0c;但是免税商品优选购物商城&#xff0c;对于购物商城工作来说&#xff0c;仍然是一项非常重要的工作。尤其是免税商品优选购物商城&#xff0c;传统人工记录模式已不符合当前社会…

RabbitMQ 存储机制

一、消息存储机制 不管是持久化的消息还是非持久化的消息都可以被写入到磁盘。持久化的消息在到达队列时就被写入到磁盘&#xff0c;非持久化的消息一般只保存在内存中&#xff0c;在内存吃紧的时候会被换入到磁盘中&#xff0c;以节省内存空间。这两种类型的消息的落盘处理都…

Kafka自动生产消息软件(自动化测试Kafka)

点击下载《Kafka服务端(含Zookeeper)一键自启软件》 点击下载《kafka客户端生产者消费者kafka可视化工具&#xff08;可生产和消费消息&#xff09;》 点击下载《Kafka自动生产消息软件》 1. 前言 在软件开发过程中&#xff0c;Kafka常被用作消息队列来处理特定的业务功能。为…

C#应用随系统启动 - 开源研究系列文章

上次写过一个随系统启动的例子&#xff0c;不过那个是写到注册表中的&#xff0c;自从更新Windows操作系统后就不好使了&#xff0c;所以就换了个方式&#xff0c;只是将应用的快捷方式添加到操作系统的启动目录里&#xff0c;这样随系统启动。 1、 项目目录&#xff1b; 2、 源…

大语言模型在交通领域的应用分析

大语言模型在交通领域的研究进展 前言&#xff1a; 大语言模型&#xff08;Large Language Models, LLMs&#xff09;如 GPT (Generative Pre-trained Transformer) 系列&#xff0c;BERT (Bidirectional Encoder Representations from Transformers) 和其他基于 Transformer …

快速删除iPhone照片:释放你的空间,加速你的手机

随着时间的推移&#xff0c;我们的iPhone往往会积累下大量的照片&#xff0c;这不仅占用了大量的存储空间&#xff0c;还可能影响手机的性能。如果你正寻找一种快速、高效的方法快速删除iPhone照片&#xff0c;以下的策略将会大有帮助。此外&#xff0c;本文还将介绍如何利用Cl…

matlab 质心重合法实现点云配准

目录 一、算法原理1、原理概述2、参考文献二、代码实现三、结果展示1、初始位置2、配准结果本文由CSDN点云侠原创,原文链接,首发于:2024年11月5日。 一、算法原理 1、原理概述 质心重合法是将源点云 P P P

MySQL数据库中的视图

视图 ​ 本篇将开始介绍有关数据库中视图的相关知识点&#xff0c;其中主要包含视图的基本使用&#xff0c;视图规则和限制。 ​ 视图是一个虚拟表&#xff0c;其内容由查询定义。同真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据&#xff0c;视图的数据变化会…

软件测试基础:单元测试与集成测试

单元测试的重要性 单元测试是软件开发过程中的必要步骤。它通过针对软件的最小可测试单元进行测试&#xff0c;可以及早发现代码中的逻辑错误和缺陷。根据统计数据显示&#xff0c;单元测试可以在软件开发初期就发现约70%的错误&#xff0c;从而减少了后期修改的成本和时间消耗…

昆仑通态触摸屏-如何完成几个窗口的切换

一、启动窗口 想要哪一个窗口是启动时第一个显示的&#xff0c;就把谁设置为启动窗口就可以。 二、公共窗口 给一个窗口命名为公共窗口 然后选择一个窗口&#xff0c;将他的公共窗口设置为我们刚才命名的那个窗口 三、页面切换 页面切换&#xff0c;是通过在公共窗口内设置按…

修改elementUI等UI组件样式的5种方法总结,哪些情况需要使用/deep/, :deep()等方式来穿透方法大全

文章目录 方法 1:全局修改样式示例:修改 `ElMessage` 的背景色和字体颜色方法 2:修改特定类型的 `ElMessage` 样式-全局-不需要穿透示例:修改 `ElMessage` 成功类型的样式方法 3:通过 Scoped CSS 在组件内部修改-局部-不需要穿透方法 4:使用 JavaScript 动态修改样式-不需…

SpringBoot中使用SpringTask实现定时任务

SpringBoot默认在无任何第三方依赖的情况下使用spring-context模块下提供的定时任务工具SpringTask。我们只需要使用EnableScheduling注解就可以开启相关的定时任务功能。 定义一个SpringBean&#xff0c;然后定义具体的定时任务逻辑方法并使用Scheduled注解标记该方法即可。…

CTF中的phar反序列化 [SWPU 2018]SimplePHP

以[SWPU 2018]SimplePHP 这道题为例 页面可以查看文件和上传文件 点击查看文件,发现url变成/file.php?file 猜测可能存在文件包含,可以读取文件 尝试读取index.php文件 回显了源码 再读取base.php 只看最后有信息的代码: <!--flag is in f1ag.php--> 提示flag在f1…

车载通信架构 --- PNC、UB与信号的关系

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…

C++进阶-->红黑树的实现

1、红黑树的概念 红黑树是一棵二叉搜索树&#xff0c;他和前面AVL树不同的是红黑树不是通过平衡因子来保证树的平衡&#xff0c;而是在树结点的处加多了个记录颜色的变量&#xff0c;这个变量可以是红色或者黑色。通过对任何一条从根到叶子的路径上各个结点的颜色进行约束&…

微信公众号绑定设计-WeChat public platform bing and send message

一 WeChat bind ui 二、message style 三、 consume style 四、send log 五、temp setting

Linux多线程(个人笔记)

Linux多线程 1.Linux线程概念1.1线程的优点1.2线程的缺点 2.Linux线程VS进程3.Linux线程控制3.1创建线程3.2线程tid及进程地址空间布局3.3线程终止3.4线程等待 4.分离线程5.线程互斥5.1互斥锁mutex5.2互斥锁接口5.3互斥锁实现原理5.4可重入VS线程安全 6.线程同步6.1条件变量6.2…

Java项目实战II基于Spring Boot的药店管理系统的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着医疗行业的快速发展和人们对健康需…

LDO电路分析

一、LDO概述 在电压转换电路中&#xff0c;LDO和DC-DC电路是最常用的两种方式&#xff0c;本篇主要介绍LDO相关内容。 LDO是线性电源的一种&#xff0c;它可以实现电源电压的转换&#xff0c;不过主要用在降压领域。它的全称是Low Dropout Regulaor&#xff0c;就是低压差线性…

VirtualBox虚拟机扩容详解

VirtualBox虚拟机扩容详解 virtualbox 扩容找到虚拟机需要扩容的磁盘更改虚拟磁盘的大小 逻辑卷扩容1. 扩展物理卷2. 扩展逻辑卷3. 扩展文件系统 Ubuntu系统安转 minikube 集群后&#xff0c;提示文件系统要炸了&#xff0c;效果如下&#xff1a;可以明显看到 /dev/mapper/ubu…