植物明星大乱斗15
能帮到你的话,就给个赞吧 😘
文章目录
- 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));
}