Is their a better way to do this collision? (SFML 3) (C++) (CMAKE)

3 hours ago 1
ARTICLE AD BOX

I am trying to make a multi-layer sidescroller to imitate a 3d environment with SFML 3 and C++. The movement feels janky and everything is very early in development. The collisions dont seem to work right with the bottom and right sides of the layers for some reason the top and left work perfectly. My initial thought process was use the original place holder player since I have collisions already working with it and just render the character over the collision box basically mimicking a collider property in unity. I even tried cutting the sprite I had for the layer 1 to be a full square thinking that it was thinking it was still in the bounds of the texture but its the same issue.

I want to know if there is a better way to do these collisions.

I am still relatively new to C++ but I have prior experience with Object Oriented Languages. Oh and this is my first time actually posting to stack overflow XD I normally just read past posts when I have a problem I want fixed!

Game.cpp:

#include "Game.hpp" #include "SidescrollingBackground_Layer1.hpp" #include <iostream> Game::Game() : playerSprite(playerTexture){ placeHolderPlayer.setSize(sf::Vector2f(25.f, 10.f)); placeHolderPlayer.setPosition(sf::Vector2f(500.f, 600.f)); placeHolderPlayer.setOrigin(sf::Vector2f(25.f / 2.f, 10.f / 2.f)); if(!playerTexture.loadFromFile("assets/textures/Player_WIP.png")){ if (!playerTexture.loadFromFile("E:/Game/assets/textures/Player_WIP.png")) { std::cerr << "Error: Could not find texture at relative or absolute path." << std::endl; } } playerSprite.setTexture(playerTexture, true); playerSprite.setOrigin(sf::Vector2f(50.f, 200.f)); playerSprite.setPosition(sf::Vector2f(placeHolderPlayer.getPosition().x, placeHolderPlayer.getPosition().y)); } Game::~Game(){ } void Game::handleInput(float dt){ sf::Vector2f dir(0.f, 0.f); if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) dir.y -= 1.f; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) dir.y += 1.f; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) dir.x -= 1.f; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D)) dir.x += 1.f; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::P)){ std::cout << "Player position: (" << placeHolderPlayer.getPosition().x << ", " << placeHolderPlayer.getPosition().y << ")" << std::endl; std::cout << "window.getSize() output: (" << window.getPosition().x << " ," << window.getSize().y << ")" << std::endl; std::cout << "placeHolderPlayer.getSize() output: (" << placeHolderPlayer.getSize().x << " ," << placeHolderPlayer.getSize().y << ")" << std::endl; std::cout << "Resolution: " << sf::VideoMode::getDesktopMode().size.x << " , " << sf::VideoMode::getDesktopMode().size.y << std::endl; } if (dir.x != 0.f || dir.y != 0.f) { dir = dir.normalized(); } if(!collisionHit(dt, dir)){ placeHolderPlayer.move(sf::Vector2f(dir * moveSpeed * dt)); playerSprite.move(sf::Vector2f(dir * moveSpeed * dt)); } } void Game::render(sf::RenderWindow &renderer){ renderer.draw(playerSprite); } bool Game::collisionHit(float dt, sf::Vector2f dir){ sf::Vector2f original(placeHolderPlayer.getPosition()); sf::Vector2f newPosition((original - (sf::Vector2f(placeHolderPlayer.getSize().x / 2, placeHolderPlayer.getSize().y /2))) + (dir * moveSpeed * dt)); sf::Vector2f windowSize(window.getSize()); sf::Vector2f playerSize(placeHolderPlayer.getSize()); SIDESCROLLINGBACKGROUND_LAYER1 Layer1; //if((!(newPosition.y <= windowSize.y) && !(newPosition.x <= windowSize.x)) && (newPosition.x < (sf::VideoMode::getDesktopMode().size.x - playerSize.x )) && (newPosition.y < (sf::VideoMode::getDesktopMode().size.y - playerSize.y))){ if(!Layer1.collision(dt, original, newPosition, playerSize, placeHolderPlayer, dir))return false; else return true; //} //else{ // return true; //} }

Game.hpp:

#ifndef GAME_H #define GAME_H #include <SFML/Graphics.hpp> #include <cmath> #include <iostream> #include <algorithm> class Game{ public: Game(); virtual ~Game(); void render(sf::RenderWindow& renderer); void handleInput(float dt); bool collisionHit(float dt, sf::Vector2f dir); sf::RectangleShape placeHolderPlayer; sf::Sprite playerSprite; sf::Texture playerTexture; private: float moveSpeed = 300; sf::RenderWindow window; }; #endif

Camera.cpp:

#include "Camera.hpp" void Camera::cameraFollow(Game& game){ sf::Vector2f player = game.placeHolderPlayer.getPosition(); view.setCenter(player); view.setSize(sf::Vector2f(sf::VideoMode::getDesktopMode().size)); renderer.setView(view); } Camera::Camera(){ } Camera::~Camera(){ } sf::View Camera::getView(){ return view; }

Camera.hpp:

#ifndef CAMERA_H #define CAMERA_H #include <SFML/Graphics.hpp> #include "Game.hpp" class Camera{ public: void cameraFollow(Game& game); Camera(); virtual ~Camera(); sf::View getView(); private: sf::View view; sf::RenderWindow renderer; }; #endif

SidescrollingBackground_Layer1.cpp:

#include "SidescrollingBackground_Layer1.hpp" SIDESCROLLINGBACKGROUND_LAYER1::SIDESCROLLINGBACKGROUND_LAYER1() : Layer1(texture) { if (!texture.loadFromFile("assets/textures/placeholder_texture.png")) { if (!texture.loadFromFile("E:/Game/assets/textures/placeholder_texture.png")) { std::cerr << "Error: Could not find texture at relative or absolute path." << std::endl; } } Layer1.setTexture(texture, true); Layer1.setScale(sf::Vector2f(.5f ,.5f)); Layer1.setPosition(sf::Vector2f(300.f, 500.f)); } SIDESCROLLINGBACKGROUND_LAYER1::~SIDESCROLLINGBACKGROUND_LAYER1(){ } bool SIDESCROLLINGBACKGROUND_LAYER1::collision(float dt, sf::Vector2f oldPosition, sf::Vector2f newPosition, sf::Vector2f playerSize, sf::RectangleShape player, sf::Vector2f dir){ /* if (Layer1.getGlobalBounds().findIntersection(player.getGlobalBounds())) { return false; } else { player.move(sf::Vector2f(dir.x - (dir.y * 2), dir.y - (dir.y * 2))); return true; } */ sf::FloatRect layerBounds = Layer1.getGlobalBounds(); sf::FloatRect layerMaxBounds = Layer1.getLocalBounds(); if(layerBounds.contains(newPosition) && layerMaxBounds.contains((newPosition - playerSize))){ return false; } else return true; } void SIDESCROLLINGBACKGROUND_LAYER1::scrollingBackground(sf::RenderWindow renderer, Game game, float dt){ } void SIDESCROLLINGBACKGROUND_LAYER1::render(sf::RenderWindow& renderer){ renderer.draw(Layer1); }

SidescrollingBackground_Layer1.hpp:

#ifndef SIDESCROLLINGBACKGROUND_LAYER1_H #define SIDESCROLLINGBACKGROUND_LAYER1_H #include <SFML/Graphics.hpp> #include "Game.hpp" #include <iostream> class SIDESCROLLINGBACKGROUND_LAYER1 { public: SIDESCROLLINGBACKGROUND_LAYER1(); ~SIDESCROLLINGBACKGROUND_LAYER1(); bool collision(float dt, sf::Vector2f oldPosition, sf::Vector2f newPosition, sf::Vector2f playerSize, sf::RectangleShape player, sf::Vector2f dir); void scrollingBackground(sf::RenderWindow renderer, Game game, float dt); void render(sf::RenderWindow& renderer); sf::Texture texture; sf::Sprite Layer1; private: Game game; sf::RenderWindow renderer; }; #endif

Window.cpp:

#include "Window.hpp" #include "Game.hpp" #include "SidescrollingBackground_Layer1.hpp" #include "Camera.hpp" Window::Window() { renderer.create(sf::VideoMode::getDesktopMode(), "Game", sf::State::Fullscreen); renderer.setVerticalSyncEnabled(true); } Window::~Window() { } void Window::run() { Camera camera; SIDESCROLLINGBACKGROUND_LAYER1 Layer1; Game game; sf::Clock clock; while (renderer.isOpen()) { camera.cameraFollow(game); renderer.setView(camera.getView()); float dt = clock.restart().asSeconds(); while (const std::optional<sf::Event> event = renderer.pollEvent()) { if (event->is<sf::Event::Closed>()) { renderer.close(); } if (const sf::Event::KeyPressed* keyPress = event->getIf<sf::Event::KeyPressed>()) { if (keyPress->code == sf::Keyboard::Key::Escape) { renderer.close(); } } } update();//update game situations like unlocking smth game.handleInput(dt);//this handles well input renderer.clear(sf::Color::Blue);//background is blue Layer1.render(renderer);//top envirement lvl game.render(renderer);//Player lvl renderer.display();//Display all of the new renders } } void Window::handleInput(sf::Event& event) { } void Window::render() { } void Window::update() { } sf::RenderWindow& Window::getRenderer() { return renderer; } bool Window::isOpen() const { return renderer.isOpen(); }

Window.hpp:

#ifndef WINDOW_H #define WINDOW_H #include <SFML/Graphics.hpp> #include <iostream> #include "Game.hpp" class Window { public: Window(); virtual ~Window(); virtual void update(); virtual void handleInput(sf::Event& event); virtual void render(); virtual void run(); sf::RenderWindow& getRenderer(); bool isOpen() const; private: sf::RenderWindow renderer; }; #endif

main.cpp:

#include "Window.hpp" int main() { Window window; window.run(); return 0; }

This is everything that is hooked up rn in the game engine. Your time and evaluation is greatly appreciated!

If you need a clip of the game running to see what I mean here you go: clip of the collision

Read Entire Article