added double jump and sitting state

This commit is contained in:
nihonium 2023-02-25 19:34:24 +03:00
parent 2e5c5a8dde
commit 90d07dde3f
Signed by: nihonium
GPG key ID: 0251623741027CFC
148 changed files with 13050 additions and 0 deletions

View file

@ -0,0 +1,64 @@
#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
using std::cout, std::endl;
class Animation
{
public:
enum class AnimationType {Repeat, OneIteration};
Animation(AnimationType type = AnimationType::Repeat) : mType{type}
{
}
void addTextureRect(sf::IntRect rect)
{
mTextureRects.push_back(rect);
}
void setAnimationSpeed(float animationSpeed)
{
mAnimationSpeed = animationSpeed;
}
sf::Vector2i getSize()
{
return {mTextureRects[mCurrentFrame].width, mTextureRects[mCurrentFrame].height};
}
void update(float dt)
{
mTime += dt;
mCurrentFrame = static_cast<int>(mAnimationSpeed * mTime);
if (mCurrentFrame >= mTextureRects.size())
{
if (mType == AnimationType::Repeat)
{
mCurrentFrame = 0;
mTime = 0;
}
else if (mType == AnimationType::OneIteration)
{
mCurrentFrame = mTextureRects.size() - 1;
mTime = mCurrentFrame / mAnimationSpeed;
}
}
}
void updateSprite(sf::Sprite& sprite) const
{
sprite.setTextureRect(mTextureRects[mCurrentFrame]);
}
private:
std::vector<sf::IntRect> mTextureRects {};
int mCurrentFrame {0};
float mAnimationSpeed {1};
float mTime {0};
AnimationType mType {AnimationType::OneIteration};
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View file

@ -0,0 +1,62 @@
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "world.hpp"
/*
Для компиляции:
g++ main.cpp player.cpp player_states.cpp -lsfml-window -lsfml-system -lsfml-graphics
Графика взята отсюда:
https://rvros.itch.io/animated-pixel-hero
*/
int main()
{
sf::ContextSettings settings;
settings.antialiasingLevel = 8.0;
sf::RenderWindow window(sf::VideoMode(1200, 900), "Player states", sf::Style::Close, settings);
window.setVerticalSyncEnabled(true);
window.setFramerateLimit(60);
double time = 0;
double dt = 1.0 / 60;
World world;
world.addBlock({-500, 770, 20000, 400});
world.addBlock({-400, 100, 700, 300});
world.addBlock({600, 500, 300, 120});
world.addBlock({800, 0, 400, 200});
world.addBlock({-100, -700, 400, 100});
world.addBlock({700, -700, 400, 100});
world.addBlock({1500, -700, 400, 100});
world.addBlock({1100, -300, 400, 100});
world.addBlock({1100, 400, 400, 400});
world.addBlock({1900, -100, 200, 800});
world.addBlock({3000, 500, 1000, 200});
while (window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
if(event.type == sf::Event::Closed)
window.close();
world.handleEvents(event);
}
window.clear(sf::Color::Black);
world.update(dt);
world.draw(window);
window.display();
time += dt;
}
return 0;
}

View file

@ -0,0 +1,153 @@
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#include "player.hpp"
#include "player_states.hpp"
Player::Player(sf::Vector2f position) : mPosition{position}
{
if (!mTexture.loadFromFile("./hero.png"))
{
std::cerr << "Can't load image ./hero.png for Player class" << std::endl;
std::exit(1);
}
setState(new Idle(this));
mSprite.setTexture(mTexture);
mSprite.setOrigin(mSprite.getLocalBounds().width / 2, mSprite.getLocalBounds().height / 2);
mSprite.setPosition(mPosition);
mScaleFactor = 4;
mSprite.setScale(mScaleFactor, mScaleFactor);
}
void Player::setState(PlayerState* pNewState)
{
delete mpState;
mpState = pNewState;
}
sf::Vector2f Player::getCenter() const
{
return mPosition;
}
void Player::applyVelocity(sf::Vector2f velocity)
{
mVelocity += velocity;
}
void Player::update(float dt)
{
mpState->update(this, dt);
mPosition += mVelocity * dt;
mSprite.setOrigin(mSprite.getLocalBounds().width / 2, mSprite.getLocalBounds().height / 2);
mSprite.setPosition(mPosition);
mpState->updateSprite(mSprite, mIsFacedRight, mScaleFactor);
}
void Player::draw(sf::RenderWindow& window)
{
window.draw(mSprite);
if (false) // For debuging
{
sf::RectangleShape shape {{mCollisionRect.width, mCollisionRect.height}};
shape.setPosition(mPosition.x + mCollisionRect.left, mPosition.y + mCollisionRect.top);
shape.setFillColor(sf::Color(150, 50, 50, 50));
window.draw(shape);
sf::CircleShape center {6};
center.setFillColor(sf::Color::Red);
center.setOrigin(center.getRadius(), center.getRadius());
center.setPosition(mPosition);
window.draw(center);
}
}
void Player::handleEvents(const sf::Event& event)
{
mpState->handleEvents(this, event);
}
bool Player::handleCollision(const sf::FloatRect& rect)
{
sf::FloatRect playerRect = {mPosition.x + mCollisionRect.left, mPosition.y + mCollisionRect.top, mCollisionRect.width, mCollisionRect.height};
float overlapx1 = playerRect.left + playerRect.width - rect.left;
float overlapx2 = rect.left + rect.width - playerRect.left;
float overlapy1 = playerRect.top + playerRect.height - rect.top;
float overlapy2 = rect.top + rect.height - playerRect.top;
if (overlapx1 < 0 || overlapx2 < 0 || overlapy1 < 0 || overlapy2 < 0)
return false;
int minOverlapDirection = 0;
float minOvelap = overlapx1;
if (overlapx2 < minOvelap) {minOverlapDirection = 1; minOvelap = overlapx2;}
if (overlapy1 < minOvelap) {minOverlapDirection = 2; minOvelap = overlapy1;}
if (overlapy2 < minOvelap) {minOverlapDirection = 3;}
switch (minOverlapDirection)
{
case 0:
mPosition.x -= overlapx1 - 1;
if (mVelocity.y > 0 && playerRect.top < rect.top + Hooked::kMaxHookOffset && playerRect.top > rect.top - Hooked::kMaxHookOffset)
{
mpState->hook(this);
}
break;
case 1:
mPosition.x += overlapx2 - 1;
if (mVelocity.y > 0 && playerRect.top < rect.top + Hooked::kMaxHookOffset && playerRect.top > rect.top - Hooked::kMaxHookOffset)
{
mpState->hook(this);
}
break;
case 2:
mPosition.y -= overlapy1 - 1;
mVelocity.y = 0;
mVelocity.y = 0;
mpState->hitGround(this);
break;
case 3:
mPosition.y += overlapy2 - 1;
if (mVelocity.y < 0)
{
mVelocity.y = 0;
mVelocity.y = 0;
}
break;
}
return true;
}
void Player::handleAllCollisions(const std::vector<sf::FloatRect>& blocks)
{
mIsColliding = false;
for (const sf::FloatRect& block : blocks)
{
if (handleCollision(block))
mIsColliding = true;
}
if (!mIsColliding)
mpState->startFalling(this);
}
Player::~Player()
{
delete mpState;
}

View file

@ -0,0 +1,51 @@
#pragma once
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "player_states.hpp"
class PlayerState;
class Player
{
public:
Player(sf::Vector2f position);
sf::Vector2f getCenter() const;
void applyVelocity(sf::Vector2f velocity);
void update(float dt);
void draw(sf::RenderWindow& window);
void handleEvents(const sf::Event& event);
bool handleCollision(const sf::FloatRect& rect);
void handleAllCollisions(const std::vector<sf::FloatRect>& blocks);
~Player();
friend class PlayerState;
friend class Idle;
friend class Running;
friend class Falling;
friend class Sliding;
friend class Hooked;
friend class Sitting;
private:
sf::Vector2f mPosition {0, 0};
sf::Vector2f mVelocity {0, 0};
bool mIsColliding {false};
sf::FloatRect mCollisionRect {-40, -60, 80, 120};
PlayerState* mpState {nullptr};
sf::Texture mTexture {};
sf::Sprite mSprite {};
float mScaleFactor {1};
bool mIsFacedRight {true};
void setState(PlayerState* pNewState);
};

View file

@ -0,0 +1,425 @@
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#include "animation.hpp"
#include "player.hpp"
#include "player_states.hpp"
using std::cout, std::endl;
PlayerState::PlayerState()
{
}
void PlayerState::updateSprite(sf::Sprite& sprite, bool isFacedRight, float scaleFactor)
{
mAnimation.updateSprite(sprite);
if (!isFacedRight)
{
sprite.setScale({-scaleFactor, scaleFactor});
}
else
{
sprite.setScale({scaleFactor, scaleFactor});
}
}
void PlayerState::jump(Player* player)
{
player->mPosition.y -= 1;
player->mVelocity.y = -kJumpingVelocity;
startFalling(player);
}
PlayerState::~PlayerState()
{
}
sf::FloatRect operator*(float x, sf::FloatRect rect)
{
return {x * rect.left, x * rect.top, x * rect.width, x * rect.height};
}
Idle::Idle(Player* player)
{
player->mVelocity = {0, 0};
mAnimation = Animation();
mAnimation.setAnimationSpeed(6);
mAnimation.addTextureRect({ 14, 6, 21, 30});
mAnimation.addTextureRect({ 64, 6, 21, 30});
mAnimation.addTextureRect({114, 6, 21, 30});
mAnimation.addTextureRect({164, 6, 21, 30});
player->mCollisionRect = player->mScaleFactor * sf::FloatRect(-10, -15, 20, 30);
cout << "Creating Idle state" << endl;
}
void Idle::hook(Player* player)
{
}
void Idle::update(Player* player, float dt)
{
mAnimation.update(dt);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
player->setState(new Running(player));
}
}
void Idle::handleEvents(Player* player, const sf::Event& event)
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Left || event.key.code == sf::Keyboard::Right)
{
player->setState(new Running(player));
}
else if (event.key.code == sf::Keyboard::Space)
{
jump(player);
}
else if (event.key.code == sf::Keyboard::LShift)
{
player->mVelocity.x = player->mIsFacedRight ? -100 : 100;
player->setState(new Sitting(player));
}
}
}
void Idle::startFalling(Player* player)
{
player->setState(new Falling(player));
}
void Idle::hitGround(Player* player)
{
}
Running::Running(Player* player) : PlayerState()
{
mRunningSpeed = 900;
mAnimation = Animation();
mAnimation.setAnimationSpeed(12);
mAnimation.addTextureRect({ 67, 45, 20, 27});
mAnimation.addTextureRect({116, 46, 20, 27});
mAnimation.addTextureRect({166, 48, 20, 27});
mAnimation.addTextureRect({217, 45, 20, 27});
mAnimation.addTextureRect({266, 46, 20, 27});
mAnimation.addTextureRect({316, 48, 20, 27});
player->mCollisionRect = player->mScaleFactor * sf::FloatRect(-10, -15, 20, 30);;
cout << "Creating Running state" << endl;
}
void Running::hook(Player* player)
{
}
void Running::update(Player* player, float dt)
{
mAnimation.update(dt);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
player->mVelocity.x = -mRunningSpeed;
player->mIsFacedRight = false;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
player->mVelocity.x = mRunningSpeed;
player->mIsFacedRight = true;
}
}
void Running::handleEvents(Player* player, const sf::Event& event)
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Space)
{
jump(player);
return;
}
else if (event.key.code == sf::Keyboard::LShift)
{
player->setState(new Sliding(player));
}
}
else if (event.type == sf::Event::KeyReleased)
{
if (event.key.code == sf::Keyboard::Left && !sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
player->setState(new Idle(player));
player->mVelocity.x = 0;
}
else if (event.key.code == sf::Keyboard::Right && !sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
player->setState(new Idle(player));
player->mVelocity.x = 0;
}
}
}
void Running::startFalling(Player* player)
{
player->setState(new Falling(player));
}
void Running::hitGround(Player* player)
{
}
Sliding::Sliding(Player* player) : PlayerState()
{
if (player->mVelocity.x > 0)
player->mVelocity.x = kSlidingVelocity;
else if (player->mVelocity.x < 0)
player->mVelocity.x = -kSlidingVelocity;
mAnimation = Animation(Animation::AnimationType::OneIteration);
mAnimation.setAnimationSpeed(10);
mAnimation.addTextureRect({155, 119, 34, 28});
mAnimation.addTextureRect({205, 119, 34, 28});
mAnimation.addTextureRect({255, 119, 34, 28});
mAnimation.addTextureRect({307, 119, 34, 28});
mAnimation.addTextureRect({ 9, 156, 34, 28});
player->mCollisionRect = sf::FloatRect(-80, -20, 160, 80);
player->mCollisionRect = player->mScaleFactor * sf::FloatRect(-20, -5, 40, 20);;
mCurrentTime = kSlidingTime;
cout << "Creating Sliding state" << endl;
}
void Sliding::hook(Player* player)
{
}
void Sliding::update(Player* player, float dt)
{
mAnimation.update(dt);
player->mVelocity.x *= kVelocityDecay;
mCurrentTime -= dt;
if (mCurrentTime < 0 && player->mIsColliding)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
player->setState(new Running(player));
else
player->setState(new Idle(player));
return;
}
}
void Sliding::handleEvents(Player* player, const sf::Event& event)
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Left || event.key.code == sf::Keyboard::Right)
player->setState(new Running(player));
if (event.key.code == sf::Keyboard::Space && player->mIsColliding)
{
jump(player);
player->setState(new Falling(player));
}
}
}
void Sliding::startFalling(Player* player)
{
}
void Sliding::hitGround(Player* player)
{
}
Falling::Falling(Player* player) : PlayerState()
{
mAnimation = Animation();
mAnimation.setAnimationSpeed(12);
mAnimation.addTextureRect({321, 155, 15, 26});
player->mCollisionRect = player->mScaleFactor * sf::FloatRect(-10, -15, 20, 30);;
cout << "Creating Falling state" << endl;
}
void Falling::hook(Player* player)
{
player->setState(new Hooked(player));
}
void Falling::update(Player* player, float dt)
{
mAnimation.update(dt);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
player->mVelocity.x = -kHorizontalVelocity;
player->mIsFacedRight = false;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
player->mVelocity.x = kHorizontalVelocity;
player->mIsFacedRight = true;
}
}
void Falling::handleEvents(Player* player, const sf::Event& event)
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Space && !hasJumped) {
kJumpingVelocity = 1000;
jump(player);
hasJumped = true;
}
}
}
void Falling::startFalling(Player* player)
{
}
void Falling::hitGround(Player* player)
{
player->setState(new Idle(player));
}
Hooked::Hooked(Player* player) : PlayerState()
{
mAnimation = Animation(Animation::AnimationType::OneIteration);
mAnimation.setAnimationSpeed(12);
mAnimation.addTextureRect({ 70, 151, 16, 34});
mAnimation.addTextureRect({119, 151, 16, 34});
mAnimation.addTextureRect({169, 151, 16, 34});
mAnimation.addTextureRect({219, 151, 16, 34});
player->mCollisionRect = player->mScaleFactor * sf::FloatRect(-10, -15, 20, 30);;
cout << "Creating Hooked state" << endl;
}
void Hooked::hook(Player* player)
{
}
void Hooked::update(Player* player, float dt)
{
player->mVelocity = {0, 0};
mAnimation.update(dt);
}
void Hooked::handleEvents(Player* player, const sf::Event& event)
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Space)
jump(player);
else if (event.key.code == sf::Keyboard::Down)
{
player->mVelocity.x = player->mIsFacedRight ? -100 : 100;
player->setState(new Falling(player));
}
}
}
void Hooked::startFalling(Player* player)
{
player->setState(new Falling(player));
}
void Hooked::hitGround(Player* player)
{
player->setState(new Idle(player));
}
/* Sitting */
Sitting::Sitting(Player* player) : PlayerState()
{
// mAnimation = Animation(Animation::AnimationType::OneIteration);
mAnimation = Animation();
mAnimation.setAnimationSpeed(4);
mAnimation.addTextureRect({168, 340, 18, 30});
mAnimation.addTextureRect({219, 340, 18, 30});
mAnimation.addTextureRect({267, 340, 18, 30});
// player->mCollisionRect = player->mScaleFactor * sf::FloatRect(-20, -20, 20, 40);;
player->mCollisionRect = sf::FloatRect(-80, -10, 160, 80);
player->mCollisionRect = player->mScaleFactor * sf::FloatRect(-20, -5, 40, 20);;
cout << "Creating Sitting state" << endl;
}
void Sitting::update(Player* player, float dt)
{
player->mVelocity = {0, 0};
mAnimation.update(dt);
}
void Sitting::handleEvents(Player* player, const sf::Event& event)
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::LShift)
{
player->mVelocity.x = player->mIsFacedRight ? -100 : 100;
player->setState(new Idle(player));
}
}
}
void Sitting::hook(Player* player) {}
void Sitting::startFalling(Player* player) {}
void Sitting::hitGround(Player* player) {}

View file

@ -0,0 +1,125 @@
#pragma once
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "animation.hpp"
#include "player.hpp"
class Player;
class PlayerState
{
public:
PlayerState();
virtual void update(Player* player, float dt) = 0;
virtual void handleEvents(Player* player, const sf::Event& event) = 0;
virtual void hook(Player* player) = 0;
virtual void startFalling(Player* player) = 0;
virtual void hitGround(Player* player) = 0;
virtual ~PlayerState();
void updateSprite(sf::Sprite& sprite, bool isFacedRight, float scaleFactor);
protected:
Animation mAnimation;
float kJumpingVelocity = 1500;
void jump(Player* player);
};
class Idle : public PlayerState
{
public:
Idle(Player* player);
void update(Player* player, float dt);
void handleEvents(Player* player, const sf::Event& event);
void hook(Player* player);
void startFalling(Player* player);
void hitGround(Player* player);
private:
};
class Running : public PlayerState
{
public:
Running(Player* player);
void update(Player* player, float dt);
void handleEvents(Player* player, const sf::Event& event);
void hook(Player* player);
void startFalling(Player* player);
void hitGround(Player* player);
private:
float mRunningSpeed;
};
class Sliding : public PlayerState
{
public:
Sliding(Player* player);
void update(Player* player, float dt);
void handleEvents(Player* player, const sf::Event& event);
void hook(Player* player);
void startFalling(Player* player);
void hitGround(Player* player);
private:
float mCurrentTime;
static constexpr float kSlidingTime = 0.50;
static constexpr float kSlidingVelocity = 2000;
static constexpr float kVelocityDecay = 0.99;
};
class Falling : public PlayerState
{
public:
Falling(Player* player);
void update(Player* player, float dt);
void handleEvents(Player* player, const sf::Event& event);
void hook(Player* player);
void startFalling(Player* player);
void hitGround(Player* player);
private:
bool hasJumped = false;
static constexpr float kHorizontalVelocity = 800;
};
class Hooked : public PlayerState
{
public:
static constexpr float kMaxHookOffset = 15;
Hooked(Player* player);
void update(Player* player, float dt);
void handleEvents(Player* player, const sf::Event& event);
void hook(Player* player);
void startFalling(Player* player);
void hitGround(Player* player);
};
class Sitting : public PlayerState
{
public:
Sitting(Player* player);
void update(Player* player, float dt);
void handleEvents(Player* player, const sf::Event& event);
void hook(Player* player);
void startFalling(Player* player);
void hitGround(Player* player);
};

View file

@ -0,0 +1,78 @@
#pragma once
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#include "player.hpp"
#include "player_states.hpp"
class World
{
public:
void addBlock(sf::FloatRect block)
{
mBlocks.push_back(block);
}
void setView()
{
sf::Vector2f playerCenter = mPlayer.getCenter();
float mViewRatio = 0.6;
if (playerCenter.x > mView.getCenter().x + mViewRatio * mView.getSize().x / 2)
mView.move({playerCenter.x - mView.getCenter().x - mViewRatio * mView.getSize().x / 2, 0});
else if (playerCenter.x < mView.getCenter().x - mViewRatio * mView.getSize().x / 2)
mView.move({playerCenter.x - mView.getCenter().x + mViewRatio * mView.getSize().x / 2, 0});
if (playerCenter.y > mView.getCenter().y + mViewRatio * mView.getSize().y / 2)
mView.move({0, playerCenter.y - mView.getCenter().y - mViewRatio * mView.getSize().y / 2});
else if (playerCenter.y < mView.getCenter().y - mViewRatio * mView.getSize().y / 2)
mView.move({0, playerCenter.y - mView.getCenter().y+ mViewRatio * mView.getSize().y / 2});
}
void update(float dt)
{
setView();
mPlayer.applyVelocity({0, mGravity * dt});
mPlayer.update(dt);
mPlayer.handleAllCollisions(mBlocks);
}
void draw(sf::RenderWindow& window)
{
static sf::RectangleShape blockShape;
blockShape.setFillColor(sf::Color(58, 69, 55));
window.setView(mView);
for (const sf::FloatRect& b : mBlocks)
{
blockShape.setPosition(b.left, b.top);
blockShape.setSize({b.width, b.height});
window.draw(blockShape);
}
mPlayer.draw(window);
}
void handleEvents(const sf::Event& event)
{
mPlayer.handleEvents(event);
}
private:
std::vector<sf::FloatRect> mBlocks {};
Player mPlayer {{400, 400}};
float mGravity {3600};
sf::View mView {sf::FloatRect(0, 0, 1200, 900)};
};