植物明星大乱斗15

news2024/11/21 0:19:41

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


文章目录

  • player.h
  • player.cpp
  • particle.h
  • particle.cpp

player.h

#pragma once
#include <graphics.h>
#include "vector2.h"
#include "animation.h"
#include "playerID.h"
#include "platform.h"
#include "bullet.h"
#include "particle.h"


extern bool isDebug;

extern Atlas atlasRunEffect;						
extern Atlas atlasJumpEffect;
extern Atlas atlasLandEffect;

extern std::vector<Bullet*> bullets;
extern std::vector<Platform> platforms;

class Player {
public:
	Player();
public:
	virtual void receiveInput(const ExMessage& msg);
	virtual void update(int time);
	virtual void render();

	void setId(const PlayerID& id);
	void setPosition(float x, float y);
public:
	const Vector2& getPosition() const;
	const Vector2& getSize() const;
public:
//攻击
	virtual void onAttack() = 0;
	virtual void onAttackEx() = 0;
protected:
	//无敌
	void makeInvulnerable();

public:
	const int getHp() const;
	const int getMp() const;

protected:
	virtual void onRun(float distance);			//奔跑
	virtual void onJump();						//跳跃
	virtual void onLand();						//落地

	void moveAndCollide(int time);				//重力和碰撞

protected:
	const float runVelocity = 0.55;				//奔跑速度
	const float jumpVelocity = -0.85;			//跳跃速度
	const float gravity = 1.6e-3f;				//重力加速度
	Vector2 velocity;							//玩家速度

	PlayerID id = P1;

//HP MP
	int hp = 100, mp = 0;

//攻击
	bool isCanAttck = true;
	Timer timerAttckCd;
	int attackCd = 500;

	bool isAttackingEx = false;

//无敌
	IMAGE imgSketch;

	bool isInvulnerable = false;
	bool isShowSketchFram = false;				//当前帧是否应该显示剪影

	Timer timerInvulnerable;					//玩家无敌
	Timer timerInvulnerableBlink;				//闪烁切换

//粒子特效
	std::vector<Particle> particles;

	Timer timerRunEffectGeneration;				//玩家跑动粒子发射器
	Timer timerDieEffectGeneration;				//玩家死亡粒子发射器


//按键信息
	bool isLeftKeyDown = false;
	bool isRightKeyDown = false;


//移动信息
	
	Vector2 position;							//玩家位置
	Vector2 size;								//碰撞尺寸

	bool isFacingRight = true;					//玩家朝向——(根据按键决定)

	

//渲染数据
	Animation animationIdleLeft;
	Animation animationIdleRight;
	Animation animationRunLeft;
	Animation animationRunRight;

	Animation animationAttackExLeft;
	Animation animationAttackExRight;

	Animation animationJumpEffect;				//跳跃动画
	Animation animationLandEffect;				//落地

	bool isJumpEffectVisible = false;			//跳跃可见
	bool isLandEffectVisible = false;			//落地可见

	Vector2 positionJumpEffect;
	Vector2 positionLandEffect;

	Animation* currentAni = nullptr;

};

player.cpp

#include "player.h"

Player::Player() {
	currentAni = &animationIdleRight;

	timerAttckCd.setCallback([&] {
		isCanAttck = true;
		});
	timerAttckCd.setTimer(attackCd);
	timerAttckCd.setIsOneShot(true);

	//无敌定时器
	timerInvulnerable.setCallback([&] {
		isInvulnerable = false;
		});
	timerInvulnerable.setTimer(750);
	timerInvulnerable.setIsOneShot(true);
	
	//无敌动画切换
	timerInvulnerableBlink.setCallback([&] {
		isShowSketchFram = !isShowSketchFram;
	});
	timerInvulnerableBlink.setTimer(75);

	//粒子发射
	timerRunEffectGeneration.setTimer(75);
	timerRunEffectGeneration.setCallback([&] {
		
		Vector2 particlePosition;
		auto frame = atlasRunEffect.getImage(0);

		//粒子位于玩家水平中央
		particlePosition.x = position.x + (size.x - frame->getwidth()) / 2;
		//玩家脚底
		particlePosition.y = position.y + size.y - frame->getheight();

		particles.emplace_back(particlePosition, &atlasRunEffect, 45);

	});
	
	timerDieEffectGeneration.setTimer(35);
	timerDieEffectGeneration.setCallback([&] {

		Vector2 particlePosition;
		auto frame = atlasRunEffect.getImage(0);

		//粒子位于玩家水平中央
		particlePosition.x = position.x + (size.x - frame->getwidth()) / 2;
		//玩家脚底
		particlePosition.y = position.y + size.y - frame->getheight();

		particles.emplace_back(particlePosition, &atlasRunEffect, 150);

	});

	//跳跃和落地
	animationJumpEffect.setAtlas(&atlasJumpEffect);
	animationJumpEffect.setInterval(25);
	animationJumpEffect.setIsLoop(false);
	animationJumpEffect.setCallback([&] {
		isJumpEffectVisible = false;
	});

	animationLandEffect.setAtlas(&atlasLandEffect);
	animationLandEffect.setInterval(50);
	animationLandEffect.setIsLoop(false);
	animationLandEffect.setCallback([&] {
		isLandEffectVisible = false;
	});

}

void Player::setId(const PlayerID& id){
	this->id = id;
}

void Player::setPosition(float x, float y){
	position.x = x, position.y = y;
}

const Vector2& Player::getPosition() const{

	return position;
}

const Vector2& Player::getSize() const{

	return size;
}

void Player::makeInvulnerable(){
	isInvulnerable = true;
	timerInvulnerable.reStart();
}

const int Player::getHp() const{
	return hp;
}

const int Player::getMp() const{
	return mp;
}

void Player::onRun(float distance){
	if (isAttackingEx)
		return;
	position.x += distance;
	timerRunEffectGeneration.resume();
}

void Player::onJump(){

	if (velocity.y || isAttackingEx)
		return;

	//仅需更改速度即可
		//位置在moveAndCollide修改
	velocity.y += jumpVelocity;

	//跳跃
	isJumpEffectVisible = true;
	animationJumpEffect.reset();
	auto frame = animationJumpEffect.getFrame();
		//jump位于玩家中央
	positionJumpEffect.x = position.x + (size.x - frame->getwidth()) / 2;
	positionJumpEffect.y = position.y + size.x - frame->getheight();

}

void Player::onLand(){

	//落地
	isLandEffectVisible = true;
	animationLandEffect.reset();
	auto frame = animationLandEffect.getFrame();
	//jump位于玩家中央
	positionLandEffect.x = position.x + (size.x - frame->getwidth()) / 2;
	positionLandEffect.y = position.y + size.x - frame->getheight();
}

void Player::moveAndCollide(int time){

	auto lastVelocityY = velocity.y;

	velocity.y += gravity * time;

	position += velocity * time;

//碰撞检测
	//玩家与平台
	if (velocity.y) {

		for (const auto& platform : platforms) {

			const auto& shape = platform.shape;

			bool isCollideX = max(position.x + size.x, shape.right) - min(position.x, shape.left) <= shape.right - shape.left + size.x;
			bool isCollideY = shape.y >= position.y && shape.y <= position.y + size.y;

			//对玩家坐标进行修正
			if (isCollideX && isCollideY) {

				//判断上一帧玩家是否在平台之上
				auto deltaY = velocity.y * time;
				auto lastY = position.y + size.y - deltaY;

				if (lastY <= shape.y) {

					position.y = shape.y - size.y;

					//平台上速度为0
					velocity.y = 0;

					if (lastVelocityY)
						onLand();

					break;
				}

			}
		}
	}
	//玩家与子弹
	if (!isInvulnerable) {
		for (const auto& bullet : bullets) {

			if (!bullet->getValid() || bullet->getCollideTarget() != id)
				continue;

			if (bullet->checkCollision(position, size)) {

				makeInvulnerable();

				bullet->onCollide();

				bullet->setValid(false);

				hp -= bullet->getDamage();
			}
		}
	}


}

void Player::receiveInput(const ExMessage& msg){
	switch (msg.message){
		case WM_KEYDOWN:

			switch (id){
				case P1:
					switch (msg.vkcode){
						//'A'
						case 0x41:
							isLeftKeyDown = true;
							break;
						//'D'
						case 0x44:
							isRightKeyDown = true;
							break;
						//'W'
						case 0x57:
							onJump();
							break;
						//'J'
						case 0x4a:
							if (isCanAttck) {
								onAttack();
								isCanAttck = !isCanAttck;
								timerAttckCd.reStart();								
							}
							break;
						//'K'
						case 0x4b:
							if (mp >= 100) {
								onAttackEx();
								mp = 0;
							}
							break;
						default:
							break;
					}
					break;
				case P2:
					switch (msg.vkcode) {
						//<
						case VK_LEFT:
							isLeftKeyDown = true;
							break;
						//>
						case VK_RIGHT:
							isRightKeyDown = true;
							break;
						//'↑'
						case VK_UP:
							onJump();
							break;
						//'1'
						case 0x6e:
							if (isCanAttck) {
								onAttack();
								isCanAttck = !isCanAttck;
								timerAttckCd.reStart();
							}
							break;
						//'2'
						case 0x62:
							if (mp >= 100) {
								onAttackEx();
								mp = 0;
							}
							break;
						default:
							break;
					}
					break;
				default:
					break;
			}

			break;
		case WM_KEYUP:

			switch (id) {
				case P1:
					switch (msg.vkcode) {
						//'A'
						case 0x41:
							isLeftKeyDown = false;
							break;
							//'D'
						case 0x44:
							isRightKeyDown = false;
							break;
						default:
							break;
					}
					break;
				case P2:
					switch (msg.vkcode) {
						//<
						case VK_LEFT:
							isLeftKeyDown = false;
							break;
							//>
						case VK_RIGHT:
							isRightKeyDown = false;
							break;
						default:
							break;
					}
					break;
				default:
					break;
			}

			break;
		default:
			break;
	}

}

void Player::update(int time){

	//direction:——玩家是否按键: 0——没有按键
	int direction = isRightKeyDown - isLeftKeyDown;

	//按键
	if (direction) {
		//特殊攻击时不允许转向
		if(!isAttackingEx)
			isFacingRight = direction > 0;	//根据按键判断当前朝向
		//根据当前朝向 选择 动画
		currentAni = isFacingRight ? &animationRunRight : &animationRunLeft;

		//水平方向移动
		auto distance = direction * runVelocity * time;
		onRun(distance);
	}
	else {
		currentAni = isFacingRight ? &animationIdleRight : &animationIdleLeft;
		timerRunEffectGeneration.pause();
	}
		

	if (isAttackingEx)
		currentAni = isFacingRight ? &animationAttackExRight : &animationAttackExLeft;

	//更新动画
	currentAni->update(time);
	animationJumpEffect.update(time);
	animationLandEffect.update(time);

	//更新定时器
	timerAttckCd.update(time);

	timerInvulnerable.update(time);

	timerInvulnerableBlink.update(time);

//粒子
	//生成粒子
	timerRunEffectGeneration.update(time);
	if (hp <= 0)
		timerDieEffectGeneration.update(time);

	//更新粒子
	particles.erase(std::remove_if(particles.begin(), particles.end(), [](const Particle& particle) {

		return !particle.checkIsValid();
	}), particles.end());

	for (auto& particle : particles)
		particle.update(time);


	//剪影
	if (isShowSketchFram)
		sketchImage(currentAni->getFrame(), &imgSketch);


	//重力模拟 和 碰撞检测
	moveAndCollide(time);
}

void Player::render(){

	if (isJumpEffectVisible)
		animationJumpEffect.render(positionJumpEffect.x, positionJumpEffect.y);

	if (isLandEffectVisible)
		animationLandEffect.render(positionLandEffect.x, positionLandEffect.y);

	//让粒子渲染在玩家身后
	for (const Particle& particle : particles)
		particle.render();

	if (hp > 0 && isInvulnerable && isShowSketchFram)
		putImageAlpha(position.x, position.y, &imgSketch);
	else
		currentAni->render(position.x, position.y);

	if (isDebug) {

		setlinecolor(RGB(0, 125, 255));

		rectangle(position.x, position.y, position.x + size.x, position.y + size.y);
	}
}

particle.h

#pragma once

#include "atlas.h"
#include "vector2.h"
#include "util.h"

class Particle {

public:
	Particle() = default;
	Particle(const Vector2& position, Atlas* atlas, int lifeSpan) :position(position), lifeSpan(lifeSpan),
		atlas(atlas) {}

public:
//设置
	void setPosition(const Vector2& position);
	void setAtlas(Atlas* atlas);
	void setLifeSpan(int lifeSpan);

//检测
	bool checkIsValid() const;

//更新
	void update(int deltaT);
//渲染
	void render() const;

private:

//物理
	Vector2 position;
	bool isValid = true;						//粒子是否有效

//渲染
	int timer = 0;								//计时器
	int lifeSpan = 0;							//单帧持续时间
	int index = 0;								//当前帧
	Atlas* atlas = nullptr;						
};

particle.cpp

#include "particle.h"

void Particle::setPosition(const Vector2& position){
	this->position = position;
}

void Particle::setAtlas(Atlas* atlas){
	this->atlas = atlas;
}

void Particle::setLifeSpan(int lifeSpan){
	this->lifeSpan = lifeSpan;
}

bool Particle::checkIsValid() const{
	return isValid;
}

void Particle::update(int deltaT){

	timer += deltaT;

	if (timer >= lifeSpan) {

		timer = 0;
		index++;

		//粒子在播完动画后消失
		if (index == atlas->getSize()) {

			index = atlas->getSize() - 1;

			isValid = false;

		}
	}
}

void Particle::render() const{

	putImageAlpha(position.x, position.y, atlas->getImage(index));
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

支持用户注册和登录、发布动态、点赞、评论、私信等功能的社交媒体平台创建!!!

需要整体源代码的可以在我的代码仓下载https://gitcode.com/speaking_me/social-media-platformTest.git 社交媒体平台 描述&#xff1a;社交媒体平台需要支持用户注册、发布动态、点赞、评论、私信等功能。 技术栈&#xff1a; 前端&#xff1a;React, Angular, Vue.js后端…

【MySQL实战45讲笔记】基础篇——redo log 和 binlog

系列文章 基础篇——MySQL 的基础架构 目录 系列文章1. 重要的日志模块&#xff1a;redo log 和 binlog1.1 redo log1.2 binlog1.3 执行器和 InnoDB 引擎内部如何执行更新语句 1. 重要的日志模块&#xff1a;redo log 和 binlog 前面系统的了解了一个查询语句的执行流程&…

【Redis】Redis实现的消息队列

一、用list实现【这是数据类型所以支持持久化】 消息基于redis存储不会因为受jvm内存上限的限制&#xff0c;支持消息的有序性&#xff0c;基于redis的持久化机制&#xff0c;只支持单一消费者订阅&#xff0c;无法避免消息丢失。 二、用PubSub【这不是数据类型&#xff0c;是…

(计算机毕设)基于SpringBoot+Vue的房屋租赁系统的设计与实现

博主可接毕设设计&#xff01;&#xff01;&#xff01; 各种毕业设计源码只要是你有的题目我这里都有源码 摘 要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。互…

云原生之运维监控实践-使用Prometheus与Grafana实现对Nginx和Nacos服务的监测

背景 如果你要为应用程序构建规范或用户故事&#xff0c;那么务必先把应用程序每个组件的监控指标考虑进来&#xff0c;千万不要等到项目结束或部署之前再做这件事情。——《Prometheus监控实战》 去年写了一篇在Docker环境下部署若依微服务ruoyi-cloud项目的文章&#xff0c;当…

游戏引擎学习第19天

介绍 这段内容描述了开发者在进行游戏开发时&#xff0c;对于音频同步和平台层的理解和调整的过程。以下是更详细的复述&#xff1a; 开发者表达了他希望今天继续进行的工作内容。他提到&#xff0c;昨天他讲解了一些关于音频的内容&#xff0c;今天他想稍微深入讲解一下他正…

node版本升级,从卸载到使用nvm管理node版本并配置vue环境(学习趟雷版)

查找node版本和安装路径 查找当前node版本 node -v 查看弄得版本安装路径 where node 卸载node&#xff08;没安装过node的可以直接跳过&#xff09; 通过控制面板删除node&#xff0c;按下【winR】键&#xff0c;输入control 控制面板找到默认程序 找到node程序点击卸载 …

每天五分钟机器学习:支持向量机算法数学基础之核函数

本文重点 从现在开始,我们将开启支持向量机算法的学习,不过在学习支持向量机算法之前,我们先来学习一些支持向量机所依赖的数学知识,这会帮助我们更加深刻的理解支持向量机算法,本文我们先来学习核函数。 定义 核函数(Kernel Function)是一种在支持向量机(SVM)、高…

机器学习基础04

目录 1.朴素贝叶斯-分类 1.1贝叶斯分类理论 1.2条件概率 1.3全概率公式 1.4贝叶斯推断 1.5朴素贝叶斯推断 1.6拉普拉斯平滑系数 1.7API 2.决策树-分类 2.1决策树 2.2基于信息增益的决策树建立 2.2.1信息熵 2.2.2信息增益 2.2.3信息增益决策树建立步骤 2.3基于基…

STM32芯片EXIT外部中断的配置与原理以及模板代码(标准库)

配置EXIT外部中断其实就是把GPIO刀NVIC的各个外设配置好 第一步&#xff1a;配置RCC&#xff0c;把我们涉及到的外设的时钟都打开 &#xff08;此处EXTI是默认打开的&#xff0c;而NVIC是内核外设无需配置&#xff09; 第二步&#xff1a;配置GPIO,选择端口为输入模式 第三…

pytest结合allure做接口自动化

这是一个采用pytest框架&#xff0c;结合allure完成接口自动化测试的项目&#xff0c;最后采用allure生成直观美观的测试报告&#xff0c;由于添加了allure的特性&#xff0c;使得测试报告覆盖的内容更全面和阅读起来更方便。 1. 使用pytest构建测试框架&#xff0c;首先配置好…

生成自签名证书并配置 HTTPS 使用自签名证书

生成自签名证书 1. 运行 OpenSSL 命令生成证书和私钥 在终端中输入以下命令&#xff0c;生成自签名证书和私钥文件&#xff1a; sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout self_signed.key -out self_signed.pem-x509&#xff1a;生成自签名证书。…

Linux网络:守护进程

Linux网络&#xff1a;守护进程 会话进程组会话终端 守护进程setsiddaemon 在创建一个网络服务后&#xff0c;往往这个服务进程是一直运行的。但是对于大部分进程来说&#xff0c;如果退出终端&#xff0c;这个终端上创建的所有进程都会退出&#xff0c;这就导致进程的生命周期…

5.4.2-1 编写Java程序在HDFS上创建文件

本次实战涉及使用Java操作Hadoop HDFS&#xff0c;包括创建文件、判断文件存在性及异常处理。通过手动添加依赖、启动HDFS服务&#xff0c;成功在HDFS上创建和检查文件。进一步探索了文件操作的最佳实践&#xff0c;如检查文件存在性以避免重复创建&#xff0c;以及处理HDFS安全…

十六.SpringCloudAlibaba极简入门-整合Grpc代替OpenFeign

前言 他来了他来了&#xff0c;停了快2个月了终于又开始更新文章啦&#xff0c;这次带来的绝对是干货&#xff01;&#xff01;&#xff01;。由于公司项目进行重构的时候考虑到&#xff0c;OpenFeign做为服务通信组件在高并发情况下有一定的性能瓶颈&#xff0c;所以将其替换…

【pytest】pytest注解使用指南

前言&#xff1a;在 pytest 测试框架中&#xff0c;注解&#xff08;通常称为装饰器&#xff09;用于为测试函数、类或方法提供额外的信息或元数据。这些装饰器可以影响测试的执行方式、报告方式以及测试的组织结构。pytest 提供了多种内置的装饰器&#xff0c;以及通过插件扩展…

百度AI人脸检测与对比

1.注册账号 打开网站 https://ai.baidu.com/ &#xff0c;注册百度账号并登录 2.创建应用 3.技术文档 https://ai.baidu.com/ai-doc/FACE/yk37c1u4t 4.Spring Boot简单集成测试 pom.xml 配置&#xff1a; <!--百度AI--> <dependency> <groupId>com.baidu.…

A040-基于springboot的智能停车计费系统设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

力扣 LeetCode 222. 完全二叉树的节点个数(Day7:二叉树)

解题思路&#xff1a; 解法一&#xff1a;普通二叉树解法 使用后序遍历 有一行的精简版代码但不利于理解采用的哪一种遍历方式 解法二&#xff1a;利用上完全二叉树的特点 一个指针left&#xff0c;一个指针right left一直向左遍历&#xff0c;right一直向右遍历&#xff…

hhdb数据库介绍(9-21)

计算节点参数说明 checkClusterBeforeDnSwitch 参数说明&#xff1a; PropertyValue参数值checkClusterBeforeDnSwitch是否可见否参数说明集群模式下触发数据节点高可用切换时&#xff0c;是否先判断集群所有成员正常再进行数据节点切换默认值falseReload是否生效是 参数设…