2d tilemap collision for SFML - c++

I am currently working on a 2D rpg game with SFML and I am trying to make the player collide with certain tiles. I have gotten to the point where the player can collide with the tiles that I want as well as slide along a wall with the following code:
sf::Vector2f movement(0.f, 0.f);
if (isMovingUp)
movement.y -= 1.f;
if (isMovingDown)
movement.y += 1.f;
if (isMovingLeft)
movement.x -= 1.f;
if (isMovingRight)
movement.x += 1.f;
sf::Vector2f oldpos = Player.getPosition();
sf::Vector2f newpos = Player.getPosition() + movement*speed*dt.asSeconds();
Player.setPosition(newpos); // the player is an sf::sprite that is 16*24px
for (int i = 0; i < tiler.solids.size(); i++) // each solid is an sf::FloatRect that is 16*16px
{
if (Player.getPosition().x + 16 >= tiler.solids[i].left && Player.getPosition().x <= tiler.solids[i].left + tiler.solids[i].width && Player.getPosition().y + 24 >= tiler.solids[i].top && Player.getPosition().y <= tiler.solids[i].top + tiler.solids[i].height) // if the player is touching the tile
{
Player.setPosition(oldpos);
}
}
What it basically does is iterate over every solid and test if the player is intersecting any of them. If there is an intersection, the player will return to their previous position when there was no collision.
The main problem that I have is that when I press two directions at once (for example colliding with a wall to the right while moving right and up) the player gets stuck because it is resetting both the x and y coordinates. Most of the code is based off of This question. The person who asked the question had the same problem. The last comment to an answer suggested that they try separating the vertical from the horizontal movement. I tried that in my code but that didn't work because it then it was basically testing if there was x-collision OR y-collision and would reset the player's position even if the player was in line with a solid.
So I guess my main question is: How can I separate the vertical from the horizontal collision in a better way OR Is there another way of doing tile collisions that allows the player to slide along a wall while holding two directions?

What you want to do is test the collision on both axis seperately. That means you safe the current position of the player and then calculate two bounding boxes. One of which is the players's bounding box with the horizontal offset added and one with the vertical offset added.
sf::FloatRect oldBounds = Player.getPosition();
sf::FloatRect newBoundsH = (oldBounds.left + movement.x *speed*dt.asSeconds(), oldBounds.top, oldBounds.width, oldBounds.height);
sf::FloatRect newBoundsV = (oldBounds.left, oldBounds.top + movement.y *speed*dt.asSeconds(), oldBounds.width, oldBounds.height);
So you can write:
// Test horizontal collision.
if(tiler.intersects(newBoundsH)){
Player.setPosition(oldpos.x, Player.getPositions().y);
}
// Test vertical collision.
if(tiler.intersects(newBoundsV)){
Player.setPosition(Player.getPositions().x, oldpos.y);
}
This method absolutely has its flaws but I think it is easy to understand.

Related

SDL joystick shooting angle c++

I'm having some issues with my shooting mechanics in my c++ 2D platform game using the SDL joystick. When I direct the joystick in any direction the bullet seem to go in the right direction-ish. When I do not direct the joystick in any dirction but press the shoot button I want the bullet to go in a horizontal line in the direction the player is facing( either 0 or -180), but right now the angle seems to be -108ish.
This is the code I use to get the angle of the joystick relative to the players position.
controller_y = SDL_JoystickGetAxis(j, 1); // get controller y axis
controller_x = SDL_JoystickGetAxis(j, 0); // get controller x axis
int deltax = xpos - controller_x; // diference in x pos
int deltay = ypos - controller_y; // diference in Y pos
angle = atan2(-deltay, -deltax) * 180 / PI;
and in my update function I update the bullets position like this:
box.x += speed *(cos(angle*PI / 180)) * delta;
box.y += speed *(sin(angle*PI / 180)) * delta;
When I do not move the joystick in any direction the x and y value of the Joystick is usually lower 500 and bigger than -500. So my question is how I can get the bullet to go in the angle -180 if my player is facing left and I do not move the joystick and the angle 0 if my player is facing right when I do not move the joystick if that makes sense.

My collision resolution for a top down racing game doesn't quite work

Sorry for the miserable english,
I'm working on the collision resolution of a top down racing game.
The detection of the collision is made out of an image that contains two colors(white being a wall and black being not a wall). I just look at if the player(the car) middle position is inside the white color on the image.
this part works fine.
When it comes to resolution I'm getting some weird bugs.
I'm suppossed to make sure the car stay in the track and cannot get out of it.
My broken solution goes like this:
1. create a vector named "Distance" that contain the substraction of the current position to the previous
2. normalise the Distance vector between 0 and 1
3. substract this normalised Distance vector to the current position until the player isn't on the white color.
4. cancel the velocity of the player(I don't plan on keeping it like that)
it sounds good in my my head but when I apply it I get some segmentation fault and occassionaly some teleportation.
the following code is in written using the SFML library and it's my attempt on making the collision. the player.update() call at the end only moves and rotate car.
void RacingMode::update(const sf::Time& deltaTime)
{
static sf::Vector2f prevPos(0,0);
static sf::Vector2f currPos(0,0);
currPos = sf::Vector2f(player.getPosition().x, player.getPosition().y);
if(raceTrack.getPixelColor(sf::Vector2u(currPos.x, currPos.y)) != sf::Color::Black){
sf::Vector2f dist;
dist.x = currPos.x - prevPos.x;
dist.y = currPos.y - prevPos.y;
dist.x = dist.x / sqrt(dist.x*dist.x + dist.y*dist.y);
dist.y = dist.y / sqrt(dist.x*dist.x + dist.y*dist.y);
sf::Vector2f newPos(0,0);
float i = 0.0;
do{
newPos = currPos - dist*i;
i+= 0.01;
}while(raceTrack.getPixelColor(sf::Vector2u(newPos.x, newPos.y)) != sf::Color::Black);
currPos = newPos;
player.setPosition(newPos);
player.setVelocity(0);
}
prevPos = currPos;
player.update(deltaTime);
}
I'd be grateful to anyone who can point out how I failed my attempt( or can propose other way of solving the problem)

2D C++ Collision detection almost perfect, but not quite?

Just to preface this question please note I am not asking 'fix my code', rather what techniques would I employ to fix this problem. I also apologise if my spelling is not very good.
Okay so I have a 2D platformer game which compares the players position with all of the tiles (in a loop), the resolves the collision accordingly. This is pretty much the structure of the main game loop:
Check all collisions (And enable jumping if a collision bellow the
player occurred)
Get input and change player velocity accordingly
Add gravity to the Y velocity
Apply velocity and friction to the players position
Draw the game
repeat
But despite this system working there are two minor, but noticeable problems with the collision system (I have provided images to make it easier). There are two problems, the first is not that bad, but the second renderers the game almost unplayable!
Problem 1. When just moving left and right across the floor in the game, occasionally the player looses all the velocity it has gained and then has to re-accumulate that velocity. I think this is because every now and then my collision detection function does not return properly. here is a image:
I hope that was clear, the problem only really becomes apparent when moving across lots of flat land.
Problem 2 (This one is way worse) The problem is that player can essentially jump up walls, because if you say for example hold down left arrow and hold jump, the player will jump up the wall. I am assuming this is because My collision detection function is returning true if the collision is coming from the side (although it should not). Here is another picture (the text is small, sorry):
So here is my collision detection function, which should take in two 'Objects' then return the direction from the first object at which the collision occurred, I think the problem arouses when It comes to determining the direction as this is causing problems, as shown above:
//Find the collision vectors
float vectorX = (a.Position.x + (a.Scale.x / 2)) - (b.Position.x + (b.Scale.x / 2));
float vectorY = (a.Position.y + (a.Scale.y / 2)) - (b.Position.y + (b.Scale.y / 2));
//Find the distance between the two objects
float deltaWidth = (a.Scale.x / 2) + (b.Scale.x / 2);
float deltaHeight = (a.Scale.y / 2) + (b.Scale.y / 2);
//Stores the direction of collision
Direction collisionDir = Direction::None;
//Check if the two objects are intersecting on the x and y axis
if (fabs(vectorX) < deltaWidth && fabs(vectorY) < deltaHeight)
{
//The direction of collision
float directionX = deltaWidth - fabs(vectorX);
float directionY = deltaHeight - fabs(vectorY);
//Check for vertical collision
if (directionX >= directionY)
{
//Check for collisions from the top
if (vectorY > 0)
{
a.Velocity.y = 0;
a.Position.y += directionY;
collisionDir = Direction::Up;
}
//Collisions form the botttom
else
{
a.Velocity.y = 0;
a.Position.y -= directionY;
collisionDir = Direction::Down;
}
}
else if (directionX < directionY / 2)
{
//Check for collisions from the left
if (vectorX > 0 )
{
a.Velocity.x = 0;
a.Position.x += directionX;
collisionDir = Direction::Left;
}
//Collisions form the right side
else
{
a.Velocity.x = 0;
a.Position.x -= directionX;
collisionDir = Direction::Right;
}
}
}
//Return the direction.
return collisionDir;
This will return a direction, My other code also checks if that direction == Bottom, then it will allow jumping.
Thank-you for any help. I am practising for Ludum Dare, because I plan on (probably) making a platformer and If I cant figure out collision detection I don't know how good my game will be.
First thing I would recommend is make yourself a Vector2D class which holds your x and y coordinates and a few overload some operators to allow for addition and subtraction of two Vector2Ds and multiplication and division by ints, floats and doubles. Trust me it will make your life a lot easier as they can hold all your forces and collision points.
Next when I have used the style of collision you are currently using I have always found that it's:
A)Harder to debug.
B)Harder for other people to follow your code.
So I would recommend creating a Rectangle2D class which handles collisions with other Rectangles and other needed functionality.
As a recommendation have the top left corner and the bottom right corner as a vector from the center of the rectangle which makes scaling and collision detection much easier this also means you can derive the other corners without directly needing to store them.
Here's a code example that will probably help what I'm trying to explain:
bool Intersects(Rectangle2D other)
{
//Checks the right, left, bottom then top of the rectangle
//against the other.
if(other.topLeftCorner.x >= bottomRightCorner.x //Checks the right
|| other.bottomRightCorner.x <= topLeftCorner.x //Checks the left
|| other.topLeftCorner.y >= bottomRightCorner.y //Checks the bottom
|| other.bottomRightCorner.y <= topLeftCorner.y) //Checks the top
return false;
else
return true;
}
You can easily manipulate this code to give you the direction of the collision. Hope this helps.

Can I make CCFollow follow more naturally?

I want to build a platform game with cocos2d/Box2D. I use CCFollow to follow the player sprite but CCFollow constantly puts it in the center of the screen. I want CCFollow to follow more naturally, like a human turning a camcorder with an acceptable lag, a small overshoot ...etc.
Here is a method of mine that didn't work: I attached (via a distance joint) a small physics body to the player that doesn't collide with anything, represented by a transparent sprite. I CCFollow'ed the transparent sprite. I was hoping this ghost body would act like a balloon attached to the player, hence a smooth shift in view. The problem is distance joint breaks with too heavy - too light objects. The balloon moves around randomly, and of course, it pulls the player back a little no matter how light it is.
What is a better way of following a moving sprite smoothly?
Try add this to CCActions in cocos2d libs.
-(void) step:(ccTime) dt
{
#define CLAMP(x,y,z) MIN(MAX(x,y),z)
CGPoint pos;
if(boundarySet)
{
// whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
if(boundaryFullyCovered) return;
CGPoint tempPos = ccpSub(halfScreenSize, followedNode_.position);
pos = ccp(CLAMP(tempPos.x,leftBoundary,rightBoundary), CLAMP(tempPos.y,bottomBoundary,topBoundary));
}
else {
// pos = ccpSub( halfScreenSize, followedNode_.position );
CCNode *n = (CCNode*)target_;
float s = n.scale;
pos = ccpSub( halfScreenSize, followedNode_.position );
pos.x *= s;
pos.y *= s;
}
CGPoint moveVect;
CGPoint oldPos = [target_ position];
double dist = ccpDistance(pos, oldPos);
if (dist > 1){
moveVect = ccpMult(ccpSub(pos,oldPos),0.05); //0.05 is the smooth constant.
oldPos = ccpAdd(oldPos, moveVect);
[target_ setPosition:oldPos];
}
#undef CLAMP
}
i get this from cocos2d forums.
Perhaps this http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:actions_ease can help you get an "acceleration" effect with CCFollow.

How do I make projectiles?

I am totally stumped on this one. I'm using C++ and SFML 1.6 for a game I'm developing, and I have no bloody idea. How do I make projectiles (like bullets)? I just don't understand it. It could be my lack of sleep but I don't know.
So my question is how do I create a Sprite that moves in a definite direction based on where the mouse is? (Think of a top down shooter with mouse aiming)
Easiest solution:
If the mouse is at Mx,My and the ship is at Sx,Sy then calculate the direction from the ship to the mouse:
Dx=Sx-Mx
Dy=Sy-My
Now normalise D (this means scale it so that it's length is one):
DLen=sqrt(Dx*Dx + Dy*Dy)
Dx/=DLen;
Dy/=DLen;
Now Dx is the distance you want to move the bullet on the x axis in order to get bullet speed of 1.
Thus each frame you move the bullet like so (position of bullet: Bx,By Speed of bullet: Bs [in pixels per millisec] Frame time Ft[in millisec])
Bx=Bx+Dx*Bs*Ft
By=By+Dy*Bs*Ft
This give you a bullet that moves towards the mouse position at a speed independent of the direction of the mouse or framerate of the game.
EDIT: As #MSalters says you need to check for the DLen==0 case when the mouse is directly above the ship to avoid division by zero errors on the normalise
One way to do it is to make the bullet face the mouse and then move it across the x and y axis by using trigonometry to find the hypotinuse from the angle. I don't think i explained this very well, so here the code to make a sprite move from its rotation:
void sMove(sf::Sprite& input,float toMove, int rotation){
bool negY = false;
bool negX = false;
int newRot = rotation;
if (rotation <= 90){
negY = true;
}
else if (rotation <= 180){
//newRot -= 90;
negY = true;
}
else if (rotation <= 360){
newRot -= 270;
negY = true;
negX = true;
}
float y = toMove*cos(newRot*PI/180);
float x = toMove*sin(newRot*PI/180);
if (negY){
y = y*-1;
}
if (negX){
x = x*-1
}
input.move(x, y);
}