#ifndef PLAYER_H #define PLAYER_H #include "raylib.h" #include #include "services/AssetService.h" #include "services/MapService.h" #include "services/InputService.h" #include "services/GameService.h" #include "entities/TextureWrapper.h" #include "enums/ECorner.h" #include "entities/Vector2i.h" class Player { public: Player() { this->texture = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER), {32, 32}, {200, 200}); prevFrame = GetTime(); spriteAnimation = {0, 0}; this->gunRight = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_RIGHT), {58, 29}, {0, 0}); this->gunLeft = TextureWrapper(AssetService::getInstance().getSprite(ESprite::PLAYER_GUN_LEFT), {58, 29}, {0, 0}); } ~Player() { } void logic() { rotateGun(); movement(); outofboundsCheck(); } void draw() { drawGun(); DrawTextureRec(this->texture.getTexture(), this->texture.getSourceRect(), this->texture.getPosition(), WHITE); } TextureWrapper &getTexture() { return this->texture; } void setPosition(Vector2 pos) { this->texture.setPosition(pos); } protected: private: TextureWrapper texture; TextureWrapper gunLeft; TextureWrapper gunRight; Vector2 velocity = {0, 0}; float jumpPower = 7.2f; float moveSpeed = 5.0f; float gravity = 0.6f; float animationDelay = 0.2f; // In seconds double prevFrame; Vector2 spriteAnimation; // Keeps track of (1) Sprite cycling and (2) Player direction enum Direction { Down, Left, Right, Up }; // Used in conjuction with spriteAnimation, makes sure sprite direction matches with player direction std::vector tiles; // Holds eight points of the player for accurate collision std::vector timesCollision; // Holds the amount of times the player has collided with a tile, and which corner of the player is touching a tile bool groundCollision; // Keeps track of whether or not the player has collided with the ground bool crateTopCollision; // Whether the player is standing on top of a crate void applyGravity() { if (!groundCollision) { velocity.y += gravity; } else { // velocity.y = 0.0f; } } void movement() { if (InputService::getInstance().isKeyDown(KEY_A)) { spriteAnimation.y = Left; velocity.x = -moveSpeed; animate(); } else if (InputService::getInstance().isKeyDown(KEY_D)) { spriteAnimation.y = Right; velocity.x = moveSpeed; animate(); } else { velocity.x = 0.0f; } positionUpdates(); handleCollision(); if (!groundCollision) { velocity.y += gravity; } else { velocity.y = 0.0f; } if (InputService::getInstance().isKeyPressed(KEY_W) && groundCollision) { velocity.y -= jumpPower; } move(velocity); } void move(Vector2 velocity) { this->texture.move(velocity); } void animate() { if (GetTime() - prevFrame > animationDelay) { spriteAnimation.x = std::fmod(spriteAnimation.x + 1, 3); Rectangle sourceRect = this->texture.getSourceRect(); sourceRect.x = spriteAnimation.x * this->texture.getSize().x; sourceRect.y = spriteAnimation.y * this->texture.getSize().y; // sourceRect.x = std::fmod(sourceRect.x, 3 * this->texture.getSize().x); this->texture.setSourceRect(sourceRect); prevFrame = GetTime(); } } void positionUpdates() { const Vector2 pos = this->texture.getPosition(); const Vector2 size = this->texture.getSize(); const int tileSize = MapService::getInstance().getMap().getTileSize(); const float bottom = pos.y + size.y + velocity.y; const float left = pos.x + velocity.x; const float right = pos.x + size.x + velocity.x; const float top = pos.y + velocity.y; Vector2i topLeft(Vector2i((int)(left + 5) / tileSize, (int)top / tileSize)); // +5 to separate top from side Vector2i topRight(Vector2i((int)(right - 5) / tileSize, (int)top / tileSize)); // -5 to separate top from side Vector2i leftTop(Vector2i((int)left / tileSize, (int)(top + 5) / tileSize)); Vector2i leftBottom(Vector2i((int)left / tileSize, (int)(bottom - 5) / tileSize)); Vector2i rightTop(Vector2i((int)right / tileSize, (int)(top + 5) / tileSize)); Vector2i rightBottom(Vector2i((int)right / tileSize, (int)(bottom - 5) / tileSize)); Vector2i bottomLeft(Vector2i((int)(left + 5) / tileSize, (int)bottom / tileSize)); // +5 to separate bottom from side Vector2i bottomRight(Vector2i((int)(right - 5) / tileSize, (int)bottom / tileSize)); // -5 to separate bottom from side tiles.clear(); tiles.push_back(topLeft); tiles.push_back(topRight); tiles.push_back(leftTop); tiles.push_back(leftBottom); tiles.push_back(rightTop); tiles.push_back(rightBottom); tiles.push_back(bottomLeft); tiles.push_back(bottomRight); } void handleCollision() { timesCollision.clear(); for (int i = 0; i < tiles.size(); i++) { if (MapService::getInstance().getMap().getColMap()[tiles[i].y][tiles[i].x] == 1) { timesCollision.push_back(i); } } groundCollision = false; if (timesCollision.size() > 0) { for (int i = 0; i < timesCollision.size(); i++) { if (timesCollision[i] == 0 || timesCollision[i] == 1) // Topleft and Topright { velocity.y = 0; } else if (timesCollision[i] == 2 || timesCollision[i] == 3 || // Lefttop and Leftbottom timesCollision[i] == 4 || timesCollision[i] == 5) // Righttop and Lefttop { velocity.x = 0; } else if (timesCollision[i] == 6 || timesCollision[i] == 7) // Bottomleft and Bottomright { groundCollision = true; } } } // Set velocity.x based on going right or left // Take current position of all 4 corners // Apply velocity // Calculate the 8 points // Add them to a list // Empty vector of amount of collisions // Loop through the points // Check the collisionmap for intersection // If so, add item to the vector of collisions // Set groundcollision to false // Loop through collisions // Based on their location (TopLeft, RightTop, etc) change the velocity // Apply gravity if not on the floor // If jumping and on the floor, go up // Move using velocity // Vector2 tempPos = this->texture.getPosition(); // this->texture.move(velocity); // groundCollision = false; // std::unordered_map collisions = MapService::getInstance().getMap().checkForCollision(this->texture); // if (collisions[ECorner::BOTTOM_LEFT] || collisions[ECorner::BOTTOM_RIGHT]) // { // velocity.y = 0.0f; // groundCollision = true; // this->texture.setPosition({this->texture.getPosition().x, floorf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize()}); // } // if (collisions[ECorner::LEFT_TOP] && collisions[ECorner::LEFT_BOTTOM]) // { // velocity.x = 0.0f; // this->texture.setPosition({ceilf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); // } // if (collisions[ECorner::TOP_LEFT] || collisions[ECorner::TOP_RIGHT]) // { // velocity.y = 0.0f; // this->texture.setPosition({this->texture.getPosition().x, (ceilf(this->texture.getPosition().y / MapService::getInstance().getMap().getTileSize())) * MapService::getInstance().getMap().getTileSize()}); // } // if (collisions[ECorner::RIGHT_TOP] && collisions[ECorner::RIGHT_BOTTOM]) // { // velocity.x = 0.0f; // this->texture.setPosition({floorf(this->texture.getPosition().x / MapService::getInstance().getMap().getTileSize()) * MapService::getInstance().getMap().getTileSize(), this->texture.getPosition().y}); // } } void outofboundsCheck() { if (this->texture.getPosition().y > StateService::getInstance().getScreenSize().y) { GameService::getInstance().setGameOver(true); AudioService::getInstance().setMusic(EMusic::GAME_OVER); AudioService::getInstance().playMusic(); } } void rotateGun() { if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + (this->gunLeft.getSize().x) && spriteAnimation.y == Right) { float gunRotationRight = atan2(GameService::getInstance().getMouseWorldPos().y - this->texture.getPosition().y, GameService::getInstance().getMouseWorldPos().x - this->texture.getPosition().x); this->gunRight.setRotation(gunRotationRight * 90); } else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) { float gunRotationLeft = atan2(GameService::getInstance().getMouseWorldPos().y - this->texture.getPosition().y, this->texture.getPosition().x - GameService::getInstance().getMouseWorldPos().x); this->gunLeft.setRotation(-gunRotationLeft * 90); } } void drawGun() { if (GameService::getInstance().getMouseWorldPos().x > this->texture.getPosition().x + this->gunLeft.getSize().x && spriteAnimation.y == Right) { DrawTexturePro( this->gunRight.getTexture(), {0, 0, this->gunRight.getSize().x, this->gunRight.getSize().y}, {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, this->texture.getPosition().y + this->texture.getSize().x / 2.0f, this->gunRight.getSize().x, this->gunRight.getSize().y}, {this->gunRight.getSize().x / 2.0f, this->gunRight.getSize().y / 2.0f}, this->gunRight.getRotation(), WHITE); } else if (GameService::getInstance().getMouseWorldPos().x + (this->gunLeft.getSize().x / 2) < this->texture.getPosition().x && spriteAnimation.y == Left) { DrawTexturePro( this->gunLeft.getTexture(), {0, 0, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, {this->texture.getPosition().x + this->texture.getSize().x / 2.0f, this->texture.getPosition().y + this->texture.getSize().x / 2.0f, this->gunLeft.getSize().x, this->gunLeft.getSize().y}, {this->gunLeft.getSize().x / 2.0f, this->gunLeft.getSize().y / 2.0f}, this->gunLeft.getRotation(), WHITE); } } }; #endif