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

提瓦特幸存者4


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


#include <iostream>
#include <windows.h>
#include <string>
#include <graphics.h>
#include <vector>

#pragma comment(lib, "MSIMG32.LIB")
#pragma comment(lib, "Winmm.lib")

class Button {
private:
	enum State { idle, hovered, pushed };
private:
	IMAGE imgIdle, imgHovered, imgPushed;	//资源
	RECT region;							//位置
	State state = idle;						//状态
public:
	Button(const RECT& region, LPCTSTR pathIdle, LPCTSTR pathHovered, LPCTSTR pathPushed);
	virtual ~Button() = 0;	//纯虚函数 依然可以有函数体
public:
	void processMessage(const ExMessage& msg);
	void draw();
private:
	//鼠标是否命中区域
	bool checkCursorHit(int x, int y) {
		return x >= region.left && x <= region.right && y >= region.top && y <= region.bottom;
	}

	virtual void onClick() = 0;
};

class StartButton :public Button {
public:
	StartButton(RECT region, LPCTSTR pathIdle, LPCTSTR pathHovered, LPCTSTR pathPushed)
		:Button(region, pathIdle, pathHovered, pathPushed) {};

	virtual ~StartButton();
private:
	virtual void onClick();
};

class QuitButton :public Button {
public:
	QuitButton(RECT region, LPCTSTR pathIdle, LPCTSTR pathHovered, LPCTSTR pathPushed)
		:Button(region, pathIdle, pathHovered, pathPushed) {};

	virtual ~QuitButton();

private:
	virtual void onClick();
};

class Atlas {
public:
	std::vector<IMAGE*> images;
public:
	Atlas(LPCTSTR path, int num);
	~Atlas();
};
class Animation {
private:
	int imgIndex = 0;									//帧索引	
	int timer = 0;										//计时器: 本张动画已经播放的时间
	
	int frameInterval;									//帧间隔ms: 两帧动画间的时间
	Atlas* atlas;
public:
	Animation(Atlas* atlas, int frameInterval);			//LPCSTR 更通用的常字符指针
public:
	//播放一张动画
	void draw(int x, int y, int playTime);				//playTime :本张动画播放的时间
};
class Bullet {
private:
	const int radius = 10;
public:
	POINT pos{ 0,0 };
public:
	void draw() const {
		//橙红色填充圆
		setlinecolor(RGB(255, 155, 50));
		setfillcolor(RGB(200, 75, 10));

		fillcircle(pos.x, pos.y, radius);
	}
};
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 shadowWidth = 32;							//玩家阴影高度

public:
	const int width = 80;								//玩家高度
	const int height = 80;

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 pos{ 0,0 };									//敌人位置

	const int width = 80;								//敌人高度
	const int height = 80;
	const int shadowWidth = 48;							//敌人阴影高度

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

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

	bool isFacingLeft = false;							//面部朝向	

	bool isAlive = true;									//怪物存活

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

	bool checkBulletCollision(const Bullet& bullet) const;
	bool checkPlayerCollision(const Player& player) const;
	
	void draw(int frameInterval);
public:
	void hurt() { isAlive = false; }
	bool checkAlive() const { return isAlive; }
};

void putImageAlpha(int x, int y, IMAGE* img);								//图像绘制(透明度)
void generateEnemy(std::vector<Enemy*>& enemys);							//生成敌人
void updateBullets(std::vector<Bullet>& bullets, const Player& player);		//更新子弹
void drawScore(const int& score);

Atlas* playerLeftAtlas;
Atlas* playerRightAtlas;
Atlas* enemyLeftAtlas;
Atlas* enemyRightAtlas;

const int windowWidth = 1280;
const int windowHeight = 720;
const int buttonWidth = 192;
const int buttonHeight = 75;
const int frameInterval = 1000 / 120;

bool running = true;
bool isStarted = false;

int main() {
	initgraph(windowWidth, windowHeight);
	
	//加载地图
	IMAGE background;
	loadimage(&background, _T("resources/img/background.png"));
	
	//加载菜单
	IMAGE menu;
	loadimage(&menu, _T("resources/img/menu.png"));

	//加载按钮
	RECT startRegion, quitRegion;	
	startRegion.left = (windowWidth - buttonWidth) / 2;
	startRegion.right = startRegion.left + buttonWidth;
	startRegion.top = 430;
	startRegion.bottom = startRegion.top + buttonHeight;

	quitRegion.left = (windowWidth - buttonWidth) / 2;
	quitRegion.right = quitRegion.left + buttonWidth;
	quitRegion.top = 550;
	quitRegion.bottom = quitRegion.top + buttonHeight;

	StartButton startButton{ startRegion , _T("resources/img/ui_start_idle.png"),
		_T("resources/img/ui_start_hovered.png"),_T("resources/img/ui_start_pushed.png") };
	QuitButton quitButton{ quitRegion , _T("resources/img/ui_quit_idle.png"),
		_T("resources/img/ui_quit_hovered.png"),_T("resources/img/ui_quit_pushed.png") };

	//初始化资产
	playerLeftAtlas = new Atlas(_T("resources/img/player_left_%d.png"), 6);
	playerRightAtlas = new Atlas(_T("resources/img/player_right_%d.png"), 6);
	enemyLeftAtlas = new Atlas(_T("resources/img/enemy_left_%d.png"), 6);
	enemyRightAtlas = new Atlas(_T("resources/img/enemy_right_%d.png"), 6);

	//加载mp3 
		//取 alias 为 bgm
	mciSendString(_T("open resources/mus/bgm.mp3 alias bgm"), nullptr, 0, nullptr);
	mciSendString(_T("open resources/mus/hit.wav alias hit"), nullptr, 0, nullptr);
		//重复播放bgm 从0开始
	mciSendString(_T("play bgm repeat from 0"), nullptr, 0, nullptr);

	Player player;
	std::vector<Bullet> bullets(3);
	std::vector<Enemy*> enemys;	
	unsigned int score = 0;

	ExMessage message;	
	
	BeginBatchDraw();

	while (running) {

		ULONGLONG startTime = GetTickCount64();

	//读数据
		peekmessage(&message);
	
	//数据处理	
		//事件处理
		if(isStarted)
			player.processMessage(message);
		else {			
			quitButton.processMessage(message);
			startButton.processMessage(message);
		}
		
		//数据处理
		if (isStarted) {
			//更新玩家
			player.move();

			//更新子弹
			updateBullets(bullets, player);

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

			//怪物和子弹碰撞检测
			for (auto& enemy : enemys)
				for (const auto& bullet : bullets)
					if (enemy->checkBulletCollision(bullet)) {
						score++;
						enemy->hurt();
						mciSendString(_T("play hit from 0"), nullptr, 0, nullptr);
					}

			//怪物和玩家碰撞检测
			for (auto& enemy : enemys) {
				if (enemy->checkPlayerCollision(player)) {
					TCHAR text[64];
					_stprintf_s(text, _T("最终得分: %d"), score);
					MessageBox(GetHWnd(), text, _T("游戏结束"), MB_OK);

					running = false;
					break;
				}
			}

			//移除以消失的怪物
			for (auto& enemy : enemys) {

				for (const auto& bullet : bullets) {

					if (!enemy->checkAlive()) {

						std::swap(enemy, enemys.back());
						delete enemys.back();
						enemys.pop_back();
					}

				}
			}
		}
						
	//渲染
		cleardevice();
		if (isStarted) {
			
			putimage(0, 0, &background);

			player.draw(frameInterval);
			for (const auto& bullet : bullets)
				bullet.draw();
			for (const auto& enemy : enemys)
				enemy->draw(frameInterval);

			drawScore(score);
			
		}
		else {
			putimage(0, 0, &menu);
			startButton.draw();
			quitButton.draw();
		}
		FlushBatchDraw();
		
		//120刷新
		ULONGLONG executionTime = GetTickCount64() - startTime;
		if (executionTime < frameInterval)
			Sleep(frameInterval - executionTime);
	}

	EndBatchDraw();

	//释放资产
	delete playerLeftAtlas;
	delete playerRightAtlas;
	delete enemyLeftAtlas;
	delete enemyRightAtlas;
}

Player::Player(){

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

	animationPlayerLeft = new Animation(playerLeftAtlas, 45);
	animationPlayerRight = new Animation(playerRightAtlas, 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 + width > windowWidth)	playerPos.x = windowWidth - width;
	if (playerPos.y + height > windowHeight)	playerPos.y = windowHeight - height;

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

void Player::draw(int frameInterval){

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

Enemy::Enemy(){

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

	animationEnemyLeft = new Animation(enemyLeftAtlas, 45);
	animationEnemyRight = new Animation(enemyRightAtlas, 45);

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

	switch (edge){

	case up:
		pos.x = rand() % windowWidth;
		pos.y = -height;
		break;
	case down:
		pos.x = rand() % windowWidth;
		pos.y = windowHeight;
		break;
	case left:
		pos.x = -width ;
		pos.y = rand() % windowHeight;
		break;
	case right:
		pos.x = windowWidth;
		pos.y = rand() % windowHeight;
		break;
	default:
		break;
	}
}

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

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

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

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

	if (modulus) {

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

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

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

bool Enemy::checkBulletCollision(const Bullet& bullet) const{

	//判断子弹 是否在 矩形内
	bool isInX = bullet.pos.x >= pos.x && bullet.pos.x <= pos.x + width;

	bool isInY = bullet.pos.y >= pos.y && bullet.pos.y <= pos.y + height;

	return isInX && isInY;
}

bool Enemy::checkPlayerCollision(const Player& player) const{
	//判断中心点 是否在 玩家矩形内

	int centerX = pos.x + width / 2;
	int centerY = pos.y + height / 2;

	bool isInPlayerX = centerX >= player.x() && centerX <= player.x() + player.width;
	bool isInPlayerY = centerY >= player.y() && centerY <= player.y() + player.height;

	return isInPlayerX && isInPlayerY;
}

void Enemy::draw(int frameInterval){

	//绘制阴影
	int x = pos.x + (width - shadowWidth) / 2;
	int y = pos.y + height - 35;
	putImageAlpha(x, y, &enemyShadow);

	//等于0时,指向原先的面部朝向
	if (isFacingLeft)
		animationEnemyLeft->draw(pos.x, pos.y, frameInterval);
	else
		animationEnemyRight->draw(pos.x, pos.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 = 25;
	static int timer = 0;

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

void updateBullets(std::vector<Bullet>& bullets, const Player& player){
	static const double radialCoefficient = 0.0045;			//径系数
	static const double tangentCoefficient = 0.009;		//切系数
	static const double Pi = 3.1415926;

	//弧度制
	double radianInterval = 2 * Pi / bullets.size();		//计算 子弹间隔

	//半径:		随时间和径系数变化
	double r = 100 + 25 * sin(GetTickCount() * radialCoefficient);

	for (int i = 0; i < bullets.size(); i++) {

		//角度:	随时间和切系数变化
		double radian = GetTickCount() * tangentCoefficient + radianInterval * i;

		bullets[i].pos.x = player.x() + player.width / 2 + int(r * cos(radian));
		bullets[i].pos.y = player.y() + player.height / 2 + int(r * sin(radian));
	}
}

void drawScore(const int& score){
	static TCHAR text[64];

	_stprintf_s(text, _T("当前玩家得分: %d"), score);

	setbkmode(TRANSPARENT);			//将背景设为透明
	settextcolor(RGB(255, 85, 185));
	outtextxy(10, 10, text);
}

Animation::Animation(Atlas* atlas, int frameInterval): atlas(atlas), frameInterval(frameInterval){

}

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

	if (timer > frameInterval) {
		imgIndex = (imgIndex + 1) % atlas->images.size();
		timer = 0;
	}

	//一张图片可以被多次绘制
	putImageAlpha(x, y, atlas->images[imgIndex]);

	timer += playTime;
}

Atlas::Atlas(LPCTSTR path, int num){
	TCHAR tPath[256];

	for (int i = 0; i < num; i++) {

		_stprintf_s(tPath, path, i);
		
		auto img = new IMAGE;

		loadimage(img, tPath);

		images.push_back(img);
	}
}

Atlas::~Atlas(){
	for (auto& img : images)
		delete img;
}

Button::Button(const RECT& region, LPCTSTR pathIdle, LPCTSTR pathHovered, LPCTSTR pathPushed) :region(region) {
	loadimage(&imgIdle, pathIdle);
	loadimage(&imgHovered, pathHovered);
	loadimage(&imgPushed, pathPushed);
}

Button::~Button() {}

//移动,左键按下,左键抬起
void Button::processMessage(const ExMessage& msg){

	switch (msg.message){
		case WM_MOUSEMOVE:
			if (state == idle && checkCursorHit(msg.x, msg.y))
				state = hovered;
			else if (state == hovered && !checkCursorHit(msg.x, msg.y))
				state = idle;
			break;
		case WM_LBUTTONDOWN:
			if (state == hovered)	
				state = pushed;
			break;
		case WM_LBUTTONUP:
			if (state == pushed)
				onClick();
			break;
		default:
			break;
	}
}

void Button::draw(){

	switch (state){
	case idle:
		putimage(region.left, region.top, &imgIdle);
		break;
	case hovered:
		putimage(region.left, region.top, &imgHovered);
		break;
	case pushed:
		putimage(region.left, region.top, &imgPushed);
		break;
	default:
		break;
	}
}

StartButton::~StartButton(){}

void StartButton::onClick(){
	isStarted = true;

}

QuitButton::~QuitButton(){}

void QuitButton::onClick(){
	running = false;
}


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

相关文章:

  • 【MySQL】数据库知识突破:数据类型全解析与详解
  • AI赋能电商:创新应用提升销售与用户体验
  • 【Python特征工程系列】利用SHAP进行特征重要性分析-XGB模型为例(案例+源码)
  • uniapp的基本使用(easycom规范和条件编译)和uview组件的安装和使用
  • Select,poll,epoll和IO多路复用和NIO
  • Java static静态变量 C语言文件读写
  • Linux - 信号
  • Android GPU纹理数据拷贝
  • 谐振式DCDC设计与参数计算 - 笔记汇聚
  • 云轴科技ZStack助力新远科技开启化工行业智能制造新篇章
  • Spring Boot框架下的注解(完整版)
  • fpga开发原理图设计仿真分析
  • Kafka生产者如何提高吞吐量?
  • 使用 Redux 在 Flutter鸿蒙next 中实现状态管理
  • Excel:vba实现正则匹配
  • 【Linux】Ansible集中化运维工具(详解)安装、常用模块、playbook脚本
  • MQTT协议解析 : 物联网领域的最佳选择
  • 浏览器是如何渲染页面的? - 2024最新版前端秋招面试短期突击面试题
  • Git遇到“fatal: bad object refs/heads/master - 副本”问题的解决办法
  • 【Webpack配置全解析】打造你的专属构建流程️(1-4)
  • DBeaver工具连接Hive
  • 冒泡选择法(c基础)
  • 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--角色可访问接口管理
  • 探索 Python 的新边疆:sh 库的革命性功能
  • AWTK fscript 中的 JSON 扩展函数
  • Spark 的介绍与搭建:从理论到实践