Tilemap collision sfml c++ platformer - c++

I have a problem with my 2d platformer. As i have just started out with c++ i'm having trouble with tile collision. I'm able to prevent the player from entering the tile and also being able to move away from it but somehow he cant move along the tile.
This is the function for checking if the new position is inside a solid tile:
void Maps::drawColMap(Player& _player){
for (int i = 0; i < _player.tiles.size(); i++)
{
if (colMap[_player.tiles[i].y][_player.tiles[i].x] == 1) //solid tile = 1
{
_player.willCollide = true;
break;
}
else {
_player.willCollide = false;
}
}
}
And here is the code for moving the player:
void Player::update()
{
sf::Vector2f newPosition;
sf::Vector2f oldPosition;
oldPosition.x = playerImage.getPosition().x; // store the current position
oldPosition.y = playerImage.getPosition().y;
newPosition.x = playerImage.getPosition().x; // store the new position
newPosition.y = playerImage.getPosition().y;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
source.y = Left; //sprite stuff
moving = true;
newPosition.x -= 2;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
source.y = Right;
moving = true;
newPosition.x += 2;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
source.y = Up;
moving = true;
newPosition.y -= 2;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
source.y = Down;
moving = true;
newPosition.y += 2;
}
if (!(sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down)))
{
moving = false;
}
//create corners to check collision
bottom = newPosition.y + 32; //tile size is 32 px
left = newPosition.x;
right = newPosition.x + 32;
top = newPosition.y;
sf::Vector2i topLeft(sf::Vector2i((int)left / 32, (int)top / 32)); // get the corners of the new position
sf::Vector2i topRight(sf::Vector2i((int)right / 32, (int)top / 32));
sf::Vector2i bottomLeft(sf::Vector2i((int)left / 32, (int)bottom / 32));
sf::Vector2i bottomRight(sf::Vector2i((int)right / 32, (int)bottom / 32));
tiles.clear();
tiles.push_back(topLeft);
if (std::find(tiles.begin(), tiles.end(), topRight) == tiles.end()) tiles.push_back(topRight); //check the corners
if (std::find(tiles.begin(), tiles.end(), bottomLeft) == tiles.end()) tiles.push_back(bottomLeft);
if (std::find(tiles.begin(), tiles.end(), bottomRight) == tiles.end()) tiles.push_back(bottomRight);
//if no collision set the position to the new position
if (!willCollide)
playerImage.setPosition(newPosition);
else
playerImage.setPosition(oldPosition); //if collision then set the position to the previous position
}
Any help is appreciated!
//edit 1
I tried logging the collision and it says that the player is still in the collision area even if i dont press anything. But how do i prevent the player from entering? I cant find the problem.
//edit 2
I think i found another problem.
The collision check should probably be run just before moving the player and after moving the new position.

After the player collides, and you change the positions, move it over by 1 pixel in the away from the collision area (the appropriate direction). It's possible that your corner is still within the collision bounds if it wasn't moved out of of the collision area properly.

Related

tilemap collision detection not working properly on slower computers

notes:
room1Collisions is a 1D array containing 1s and 0s representing collidable tiles.
+/- 0.1 is used so that the player can still move when against a collidable tile.
size is an SFML vector that holds the width and height of the player.
I understand that this code is not in great shape, but I'm trying to get collisions working to then refactor.
The issue (described below) occurs more frequently when the player collides diagonally
code:
void Player::Update(float dt) {
// 0 is a collidable tile
// change room1Collisions to a pointer of current level
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
// a is top left and b is bottom left
sf::Vector2u a(position.x/tileSize.x, (position.y+size.y/2)/tileSize.y);
sf::Vector2u b(position.x/tileSize.x, (position.y+size.y-0.1)/tileSize.y);
int tileNumberA = room1Collisions[(a.x) + a.y*(tilemapBounds.x/tileSize.x)];
int tileNumberB = room1Collisions[(b.x) + b.y*(tilemapBounds.x/tileSize.x)];
if (tileNumberA != 0 && tileNumberB != 0 && position.x >= 0) {
position.x -= speed * dt;
//animation.resumeAnimation();
}
direction = LEFT;
//animation.resumeAnimation();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
sf::Vector2u a((position.x+size.x)/tileSize.x, (position.y+size.y/2)/tileSize.y);
sf::Vector2u b((position.x+size.x)/tileSize.x, (position.y+size.y-0.1)/tileSize.y);
int tileNumberA = room1Collisions[(a.x) + a.y*(tilemapBounds.x/tileSize.x)];
int tileNumberB = room1Collisions[(b.x) + b.y*(tilemapBounds.x/tileSize.x)];
if (tileNumberA != 0 && tileNumberB != 0 && position.x+size.x <= tilemapBounds.x) {
position.x += speed * dt;
//animation.resumeAnimation();
}
direction = RIGHT;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
sf::Vector2u a((position.x+0.1)/tileSize.x, (position.y+(size.y/3))/tileSize.y);
sf::Vector2u b((position.x+size.x-0.1)/tileSize.x, (position.y+(size.y/3))/tileSize.y);
int tileNumberA = room1Collisions[(a.x) + a.y*(tilemapBounds.x/tileSize.x)];
int tileNumberB = room1Collisions[(b.x) + b.y*(tilemapBounds.x/tileSize.x)];
if (tileNumberA != 0 && tileNumberB != 0 && position.y >= 0) {
position.y -= speed * dt;
//animation.resumeAnimation();
}
direction = UP;
//animation.resumeAnimation();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
{
sf::Vector2u a((position.x+0.1)/tileSize.x, (position.y+size.y)/tileSize.y);
sf::Vector2u b((position.x+size.x-0.1)/tileSize.x, (position.y+size.y)/tileSize.y);
int tileNumberA = room1Collisions[(a.x) + a.y*(tilemapBounds.x/tileSize.x)];
int tileNumberB = room1Collisions[(b.x) + b.y*(tilemapBounds.x/tileSize.x)];
if (tileNumberA != 0 && tileNumberB != 0 && position.y+size.y <= tilemapBounds.y) {
position.y += speed * dt;
//animation.resumeAnimation();
}
direction = DOWN;
}
//animation.setAnimation(direction);
//animation.Update(dt, 0.2f);
//animation.setPosition(position);
box.setPosition(position);
}
The problem is that when the game is run on slower computers, sometimes the player will skip (?) a collision check and it becomes stuck in a collidable tile, preventing it from moving orthogonally with respect to the collidable tile. On faster computers this issue is not present unless the player speed (currently 30) is increased significantly.
Below is an image showing the player inside a collidable tile. Please note that in this example the player is not able to move left/right since those directions are perpendicular to the collidable tile, but can still move up.
What you need is to regulate the FPS so that it is consistent for all computers.
Frame rate regulation example(This uses the SDL2 library for SDL_GetTicks and SDL_Delay but there are probably alternatives to that depending on your graphics library):
const int FPS = 60;
const int frameDelay = 1000/FPS;
Uint32 frameStart;
int frameTime;
//Main game loop
while (true)
{
//Gets the amount of milliseconds elapsed since the program was first run
frameStart = SDL_GetTicks();
//YOUR MAIN GAME LOOP HERE
//Gets the amount of milliseconds that it took to run the game loop
frameTime = SDL_GetTicks() - frameStart;
//Checks if the game loop was run faster than the max frame time
if(frameDelay > frameTime)
{
//Delays the game loop so that it takes that the frame time always is the same
SDL_Delay(frameDelay - frameTime);
}
}
(Keep in mind that this is just an example there are lots of ways to regulate the FPS)
After implementing FPS regulation you might need to tweak the speed and collision sensitivity a little but once you have it it should be consistent on ALL computers

Visual Studio 2017 Exception Unhandled

I'm a novice programmer trying to follow a tuturial on recreating a game on Steam called Timber(I believe). The program was working fine and I had almost completed the tutrial, but I ran into trouble when I added the code:
for (int i = 0 < NUM_BRANCHES; i++;)
{
branches[i].setTexture(textureBranch);
branches[i].setPosition(-2000, -2000);
// Set the sprite's origin to dead center
// We can then spin it around without changing its position
branches[i].setOrigin(220, 20);
}
Visual Studio says: branches[i].setPosition(-2000, -2000); Unhandled exception thrown: write access violation.
this was 0x59AF28. occurred
I'll also post the full code, apologies that it's a bit messy.
#include "stdafx.h"
#include <sstream>
#include <SFML\Graphics.hpp>
using namespace sf;
// Function declaration
void updateBranches(int seed);
const int NUM_BRANCHES = 6;
Sprite branches[NUM_BRANCHES];
// Where is the player/branch?
// Left or right
enum class side{ LEFT, RIGHT, NONE };
side branchPositions[NUM_BRANCHES];
int main()
{
// Creates a video mode object
VideoMode vm(1920, 1080);
// Creates and opens a window for the game
RenderWindow window(vm, "Timber!!!", Style::Fullscreen);
// Create a texture to hold a graphic on the GPU
Texture textureBackground;
// Load a graphic into the texture
textureBackground.loadFromFile("graphics/background.png");
// Create a sprite
Sprite spriteBackground;
// Attach the texture to the sprite
spriteBackground.setTexture(textureBackground);
// Set the spriteBackground to cover the screen
spriteBackground.setPosition(0, 0);
// Make a tree sprite
Texture textureTree;
textureTree.loadFromFile("graphics/tree.png");
Sprite spriteTree;
spriteTree.setTexture(textureTree);
spriteTree.setPosition(810, 0);
// Prepare the bee
Texture textureBee;
textureBee.loadFromFile("graphics/bee.png");
Sprite spriteBee;
spriteBee.setTexture(textureBee);
spriteBee.setPosition(0, 450);
// Is the be currently moving?
bool beeActive = false;
// How fast can the bee fly
float beeSpeed = 0.0f;
// Make 3 cloud sprites from 1 texture
Texture textureCloud;
// Load 1 new texture
textureCloud.loadFromFile("graphics/cloud.png");
// 3 new sprites with the same texture
Sprite spriteCloud1;
Sprite spriteCloud2;
Sprite spriteCloud3;
spriteCloud1.setTexture(textureCloud);
spriteCloud2.setTexture(textureCloud);
spriteCloud3.setTexture(textureCloud);
// Position the clouds off screen
spriteCloud1.setPosition(0, 0);
spriteCloud2.setPosition(0, -150);
spriteCloud3.setPosition(0, -300);
// Are the clouds currently on the screen?
bool cloud1Active = false;
bool cloud2Active = false;
bool cloud3Active = false;
// How fast is each cloud?
float cloud1Speed = 0.1f;
float cloud2Speed = 0.2f;
float cloud3Speed = 0.3f;
// Variables to control time itself
Clock clock;
// Time bar
RectangleShape timeBar;
float timeBarStartWidth = 400;
float timeBarHeight = 80;
timeBar.setSize(Vector2f(timeBarStartWidth, timeBarHeight));
timeBar.setFillColor(Color::Red);
timeBar.setPosition((1920 / 2) - timeBarStartWidth / 2, 980);
Time gameTimeTotal;
float timeRemaining = 6.0f;
float timeBarWidthPerSecond = timeBarStartWidth / timeRemaining;
// Track whether the game is running
bool paused = true;
// Draw some text
int score = 0;
sf::Text messageText;
sf::Text scoreText;
// Font
Font font;
font.loadFromFile("fonts/KOMIKAP_.ttf");
// Set the font of our message
messageText.setFont(font);
scoreText.setFont(font);
// Assign the actual message
messageText.setString("Press Enter to Start!");
scoreText.setString("score = 0");
// Make text really big
messageText.setCharacterSize(75);
scoreText.setCharacterSize(100);
// Choose a color
messageText.setFillColor(Color::White);
scoreText.setFillColor(Color::Black);
// Position the text
FloatRect textRect = messageText.getLocalBounds();
messageText.setOrigin(textRect.left +
textRect.width / 2.0f,
textRect.top +
textRect.height / 2.0f);
messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);
scoreText.setPosition(20, 20);
// Prepare 6 branches
Texture textureBranch;
textureBranch.loadFromFile("graphics/branch.png");
// Set the texture for each branch sprite
for (int i = 0 < NUM_BRANCHES; i++;)
{
branches[i].setTexture(textureBranch);
branches[i].setPosition(-2000, -2000);
// Set the sprite's origin to dead center
// We can then spin it around without changing its position
branches[i].setOrigin(220, 20);
}
while (window.isOpen())
{
/*
**************
Handles the player input
**************
*/
if (Keyboard::isKeyPressed(Keyboard::Escape))
{
window.close();
}
// Start the game
if (Keyboard::isKeyPressed(Keyboard::Return))
{
paused = false;
// Reset the time and the score
score = 0;
timeRemaining = 5;
}
/*
**************
Update the scene
**************
*/
if (!paused)
{
// Measure time
Time dt = clock.restart();
// Subtract from the amount of time remaining
timeRemaining -= dt.asSeconds();
// size up the time bar
timeBar.setSize(Vector2f(timeBarWidthPerSecond *
timeRemaining, timeBarHeight));
if (timeRemaining <= 0.0f)
{
// Pause the game
paused = true;
// Change the message shown to the player
messageText.setString("Out of time");
// Reposition the text base on its new size
FloatRect textRect = messageText.getLocalBounds();
messageText.setOrigin(textRect.left +
textRect.width / 2.0f,
textRect.top +
textRect.height / 2.0f);
messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);
}
// Setup the bee
if (!beeActive)
{
// How fast is the bee
srand((int)time(0) * 10);
beeSpeed = (rand() % 400) + 350;
// How high is the bee
srand((int)time(0) * 10);
float height = (rand() % 650) + 850;
spriteBee.setPosition(1921, height);
beeActive = true;
}
else
// Move the bee
{
spriteBee.setPosition(
spriteBee.getPosition().x -
(beeSpeed * dt.asSeconds()),
spriteBee.getPosition().y);
// Has the bee reached the right hand edge of the screen?
if (spriteBee.getPosition().x < -100)
{
// Set it up ready to be a whole new bee next frame
beeActive = false;
}
}
// Manage the clouds
// Cloud 1
if (!cloud1Active)
{
// How fast is the cloud
srand((int)time(0) * 10);
cloud1Speed = (rand() % 150);
// How high is the cloud
srand((int)time(0) * 10);
float height = (rand() % 200);
spriteCloud1.setPosition(-300, height);
cloud1Active = true;
}
else
{
spriteCloud1.setPosition(
spriteCloud1.getPosition().x +
(cloud1Speed * dt.asSeconds()),
spriteCloud1.getPosition().y);
// Has the cloud reached the right hand edge of the screen?
if (spriteCloud1.getPosition().x > 1920)
{
// Set it up ready to be a whole new cloud next frame
cloud1Active = false;
}
}
// Cloud 2
if (!cloud2Active)
{
// How fast is the cloud
srand((int)time(0) * 20);
cloud2Speed = (rand() % 200);
// How high is the cloud
srand((int)time(0) * 20);
float height = (rand() % 300);
spriteCloud2.setPosition(-200, height);
cloud2Active = true;
}
else
{
spriteCloud2.setPosition(
spriteCloud2.getPosition().x +
(cloud1Speed * dt.asSeconds()),
spriteCloud2.getPosition().y);
// Has the cloud reached the right hand edge of the screen?
if (spriteCloud2.getPosition().x > 1920)
{
// Set it up ready to be a whole new cloud next frame
cloud2Active = false;
}
}
// Cloud 3
if (!cloud3Active)
{
// How fast is the cloud
srand((int)time(0) * 30);
cloud3Speed = (rand() % 250);
// How high is the cloud
srand((int)time(0) * 30);
float height = (rand() % 150);
spriteCloud3.setPosition(-100, height);
cloud3Active = true;
}
else
{
spriteCloud3.setPosition(
spriteCloud3.getPosition().x +
(cloud1Speed * dt.asSeconds()),
spriteCloud3.getPosition().y);
// Has the cloud reached the right hand edge of the screen?
if (spriteCloud3.getPosition().x > 1920)
{
// Set it up ready to be a whole new cloud next frame
cloud3Active = false;
}
}
// Update the score text
std::stringstream ss;
ss << "Score = " << score;
scoreText.setString(ss.str());
// Update the branch sprites
for (int i = 0; i < NUM_BRANCHES; i++)
{
float height = i * 150;
if (branchPositions[i] == side::LEFT)
{
// Move the sprite to the left side
branches[i].setPosition(610, height);
// Flip the sprite around the other way
branches[i].setRotation(180);
}
else if (branchPositions[i] == side::RIGHT)
{
// Move the sprite to the right side
branches[i].setPosition(1330, height);
// Set the sprite rotation to normal
branches[i].setRotation(0);
}
else
{
// Hide the branch
branches[i].setPosition(3000, height);
}
}
} // End if(!paused)
/*
**************
Draw the scene
**************
*/
// Clear everything from the last frame
window.clear();
// Draw our game scene here
window.draw(spriteBackground);
// Draw the clouds
window.draw(spriteCloud1);
window.draw(spriteCloud2);
window.draw(spriteCloud3);
// Draw the branches
for (int i = 0; i < NUM_BRANCHES; i++)
{
window.draw(branches[i]);
}
// Draw the tree
window.draw(spriteTree);
// Draw the insect
window.draw(spriteBee);
// Draw the score
window.draw(scoreText);
//Draw the timebar
window.draw(timeBar);
if (paused)
{
// Draw our message
window.draw(messageText);
}
// Show everyting we just drew
window.display();
}
return 0;
}
// Function definition
void updateBranches(int seed)
{
// Move all the branches down on place
for (int j = NUM_BRANCHES - 1; j > 0; j--)
{
branchPositions[j] = branchPositions[j - 1];
}
// Spawn a new branch at postion 0
// LEFt, RIGHT, NONE
srand((int)time(0) + seed);
int r = (rand() % 5);
switch (r)
{
case 0:
branchPositions[0] = side::LEFT;
break;
case 1:
branchPositions[0] = side::RIGHT;
break;
default:
branchPositions[0] = side::NONE;
break;
}
}
setPosition() requires an sf::Vector2f. So you could fix your code by changing that line to:
branches[i].setPosition(sf::Vector2f(-2000, -2000))

C++ SFML collision is not accurate

I'm making a 2D game with SFML in C++ and I have a problem with collision. I have a player and a map made of tiles. Thing that doesn't work is that my collision detection is not accurate. When I move player up and then down towards tiles, it ends up differently.
I am aware that source of this problem may be calculating player movement with use of delta time between frames - so it is not constant. But it smooths movement, so I don't know how to do it other way. I tried with constant speed valuses and to make collision fully accurate - speed had to be very low and I am not satisfied with that.
void Player::move() {
sf::Vector2f offsetVec;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
offsetVec += sf::Vector2f(0, -10);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
offsetVec += sf::Vector2f(0, 10);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
offsetVec += sf::Vector2f(-10, 0);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
offsetVec += sf::Vector2f(10, 0);
this->moveVec += offsetVec;
}
void Player::update(float dt, Map *map) {
sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
this->moveVec.y * this->playerSpeed * dt);
sf::Sprite futurePos = this->sprite;
futurePos.move(offset);
if (map->isCollideable(this->pos.x, this->pos.y, futurePos.getGlobalBounds())) {
this->moveVec = sf::Vector2f(0, 0);
return;
}
this->sprite.move(offset);
this->pos += offset;
this->moveVec = sf::Vector2f(0, 0);
return;
}
In player position update I create future sprite object, which is object after applying movement, to get it's boundaries and pass it to collision checker. To collision checker I also pass player pos, because my map is stored in 2d array of tile pointers, so I check only these in player range.
bool Map::isCollideable(float x, float y, const sf::FloatRect &playerBounds) {
int startX = int(x) / Storage::tileSize;
int startY = int(y) / Storage::tileSize;
Tile *tile;
for (int i = startX - 10; i <= startX + 10; ++i) {
for (int j = startY - 10; j <= startY + 10; ++j) {
if (i >= 0 && j >= 0) {
tile = getTile(i, j);
if (tile != nullptr && playerBounds.intersects(tile->getGlobalBounds()))
return true;
}
}
}
return false;
}
Full project on Github
My solution
I have changed if statement in update function to while statement, which decreases my offset vector till no collision is present. I still have to make some adjustments, but general idea is:
void Player::update(float dt, Map *map) {
int repeats = 0;
sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
this->moveVec.y * this->playerSpeed * dt);
sf::Sprite futurePos = this->sprite;
while (map->isCollideable(this->pos.x, this->pos.y, futurePos, offset)) {
offset = 0.7f * offset;
repeats++;
if (repeats > 5) {
this->moveVec = sf::Vector2f(0, 0);
return;
}
}
this->sprite.move(offset);
this->pos += offset;
this->moveVec = sf::Vector2f(0, 0);
return;
}
I also had to rework isCollideable method a little, so it accepts sf::Sprite and offset vector so it can calculate boundaries on it's own.
When the player collides with a tile, you should calculate the penetration, that is, the value of "how much the player went into the tile". When you have this value, nudge your player back that much.
This is just a thought but you could have some inaccuracies in your collision detection when you typecast the float x, and y to integers and then divide them. This could cause problems because some of the data in the float could be lost. If the float was 3.5 or 3.3 or 3.9 then it would become 3 which throws off your collision calculations.

How to move an entity with a mouse click?

Ok i now manage to figure out the problem but now another issue appear, my robot seem to move on its own to another point which i have no idea where its from. Here my code
His code make this robot move to the location i click on the terrain.
bool DemoApp::nextLocation(void){
mDestination = mtoward;
mRobotDir = mDestination - mRobotNode[0]->getPosition();
mDistance = mRobotDir.normalise();
return true;
}
bool DemoApp::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
if (mRobotDir == Ogre::Vector3::ZERO) {
if (nextLocation()) {
// Set walking animation
mAnimationState = mRobot[0]->getAnimationState("Walk");
mAnimationState->setLoop(true);
mAnimationState->setEnabled(true);
}//if
}else{
Ogre::Real move = mWalkSpeed * evt.timeSinceLastFrame;
mDistance -= move;
if (mDistance <= 0.0f){
mRobotNode[0]->setPosition(mDestination);
mRobotDir = Ogre::Vector3::ZERO;
// Set animation based on if the robot has another point to walk to.
if (!nextLocation()){
// Set Idle animation
mAnimationState = mRobot[0]->getAnimationState("Idle");
mAnimationState->setLoop(true);
mAnimationState->setEnabled(true);
}else{
// Rotation Code will go here later
Ogre::Vector3 src = mRobotNode[0]->getOrientation() * Ogre::Vector3::UNIT_X;
if ((1.0f + src.dotProduct(mRobotDir)) < 0.0001f) {
mRobotNode[0]->yaw(Ogre::Degree(180));
}else{
Ogre::Quaternion quat = src.getRotationTo(mRobotDir);
mRobotNode[0]->rotate(quat);
} // else
}//else
}else{
mRobotNode[0]->translate(mRobotDir * move);
} // else
} // if
mAnimationState->addTime(evt.timeSinceLastFrame);
}
Here is the raycast code for my mouse click
Ogre::Terrain* pTerrain = mTerrainGroup->getTerrain(0, 0);
Ogre::Viewport* vp = this->mWindow->getViewport(0);
Ogre::Ray mouseRay = mCamera->getCameraToViewportRay(static_cast<float>(mMouse->getMouseState().X.abs)/mMouse->getMouseState().width, static_cast<float>(mMouse->getMouseState().Y.abs)/mMouse->getMouseState().height);
std::pair <bool, Ogre::Vector3> result;
result = pTerrain->rayIntersects(mouseRay, true, 0);
if (result.first = true)
{
mtoward = result.second - mRobotNode[0]->_getDerivedPosition();
mRobotNode[0]->translate(mtoward, Ogre::Node::TS_LOCAL);
}

Prevent moving a sprite onto another sprite

I have some sprites (2d-boxes) of the same type stored in a formation vector. Now i want to move them around with the mouse, that works well. But the code should prevent the player to move one sprite onto another already existing sprite of the vector.
My solution is quite ugly and does not work. Whenever a sprite is moved around, i test with the spriteoverlap function if the sprite is moved onto another. The Problem:
Whenever the sprite is directly close to the it stops moving, which is what wanted.
But i can't move it anymore afterwards because the overlapfunction sets the bool always to false.
while (App.pollEvent(Event))
{
//Moving the playerbuttons on the formation screen
for (size_t k = 0; k < formation.size(); k++)
{
bool drag_onto_otherplayer = false;
if (isMouseOver(formation[k], App) == true)
{
//The next loop tests if the sprite being moved with the mouse overlaps with another sprite from the formation vector
for (size_t j = 0; j < formation.size(); j++)
{
if (spriteOverlap(formation[j], formation[k], App) == true && k!=j)
{
std::cout << drag_onto_otherplayer << std::endl;
drag_onto_otherplayer = true;
std::cout << drag_onto_otherplayer <<std::endl;
}
if (drag_onto_otherplayer == false)
//(If the sprite does not overlap getting the new sprite position from the mouseposition)
{
Mouseposition =sf::Vector2f(sf::Mouse::getPosition(App));
Mouseposition.x = Mouseposition.x - formation[k].getLocalBounds().width / 2;
Mouseposition.y = Mouseposition.y - formation[k].getLocalBounds().height / 2;
formation[k].setPosition(sf::Vector2f(Mouseposition));
Formation_playernames.clear();
Formation_playerinformation.clear();
Formation_Playernames(Font, Formation_playernames, formation, playerlist);
Formation_Playerinformation(Font, Formation_playerinformation, formation, playerlist);
}
So the problem are my loops and the bool test i guess, but i don't know how to solve it.
Any ideas ?
Here is my spriteoverlap function:
bool spriteOverlap(sf::Sprite &sprite1, sf::Sprite &sprite2, sf::RenderWindow &App)
{
float x_min1 = sprite1.getPosition().x;
float x_max1 = sprite1.getPosition().x + sprite1.getLocalBounds().width;
float y_min1 = sprite1.getPosition().y;
float y_max1 = sprite1.getPosition().y + sprite1.getLocalBounds().height;
float x_min2 = sprite2.getPosition().x;
float x_max2 = sprite2.getPosition().x + sprite2.getLocalBounds().width;
float y_min2 = sprite2.getPosition().y;
float y_max2 = sprite2.getPosition().y + sprite2.getLocalBounds().height;
if (x_min1 > x_max2 | x_max1 < x_min2 | y_min1 > y_max2 | y_max1 < y_max2)
return false;
else
return true;
};
And my isMouseover function:
bool isMouseOver(sf::Sprite &sprite, sf::RenderWindow &App)
{
float pos_x = sprite.getPosition().x;
float pos_y = sprite.getPosition().y;
if (sf::Mouse::getPosition(App).x > pos_x && sf::Mouse::getPosition(App).x < pos_x+sprite.getLocalBounds().width &&
sf::Mouse::getPosition(App).y >pos_y && sf::Mouse::getPosition(App).y < pos_y + sprite.getLocalBounds().height)
{
return true;
}
else
return false;
};
Check for collision is already included somewhat in sfml:
bool spriteOverlap(sf::Sprite& sprite1, sf::Sprite& sprite2) // possibly const, dunno
{
return sprite1.getGlobalBounds().intersects(sprite2.getGlobalBounds());
}
Generally try this: Only move, if the position of the next frame is valid. This prevents objects being stuck, because you already moved them into an invalid position, thus preventing any further movement
edit:
//untested
bool spritesWillOverlap(sf::Sprite& sprite1, sf::Sprite& sprite2, sf::Vector2f vel)
{
top1 = sprite1.getGobalBounds().top + vel.y;
left1 = sprite1.getGlobalBounds().left + vel.x;
right1 = left1 + sprite1.getGlobalBounds().width;
bottom1 = top1 + sprite1.getGlobalBounds().height;
top2 = sprite2.getGobalBounds().top + vel.y;
left2 = sprite2.getGlobalBounds().left + vel.x;
right2 = left2 + sprite1.getGlobalBounds().width;
bottom2 = top2 + sprite1.getGlobalBounds().height;
sf::FloatRect rect1(top1, left1, right1 - left1, bottom1 - top1);
sf::FloatRect rect2(top2, left2, right2 - left2, bottom2 - top2);
return rect1.intersects(rect2);
}
vel: velocity -> an object is moved by this 2D vector every frame
If the concept of "frames" is unfamiliar, read up on framerates/fixed framerate and/or timestep. here's an example article to get started: Fix Your Timestep!