植物明星大乱斗15


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


文章目录

player.h

cpp 复制代码
#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

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

cpp 复制代码
#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

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));
}
相关推荐
Tisfy2 小时前
LeetCode 3240.最少翻转次数使二进制矩阵回文 II:分类讨论
算法·leetcode·矩阵·题解·回文·分类讨论
橘子遇见BUG3 小时前
算法日记 31 day 动态规划(01背包)
算法·动态规划
东方巴黎~Sunsiny3 小时前
java-图算法
java·开发语言·算法
ac-er88883 小时前
PHP二维数组排序算法函数
算法·php·排序算法
Tisfy4 小时前
LeetCode 3244.新增道路查询后的最短距离 II:贪心(跃迁合并)-9行py(O(n))
算法·leetcode·题解·贪心·思维
Matlab程序猿小助手4 小时前
【MATLAB源码-第218期】基于matlab的北方苍鹰优化算法(NGO)无人机三维路径规划,输出做短路径图和适应度曲线.
开发语言·嵌入式硬件·算法·matlab·机器人·无人机
捕鲸叉5 小时前
C++创建型模式之生成器模式
开发语言·c++·建造者模式
sxtyjty5 小时前
人机打怪小游戏(非常人机)
c++
oioihoii5 小时前
单例模式详解
c++·单例模式·c#·大学必学