How do I make projectiles? - c++

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);
}

Related

How to make a 3D model follow the mouse movements?

I'm writing a simple version of the game Duck Hunt in C++, using OpenGL. I managed to render the 3D model of a rifle in the center of the screen, and what I want is to rotate it on his y and z axes depending on the mouse movements, so that the rifle is always pointing to the mouse pointer. I have tried to compute the rotation angles using the arctan equation, in order to have the right proportion between the mouse movements and the distance of the pointer from the rifle, but the result is not really good as the sensibility of the rotation seems to be too low. Here there is my current implementation:
void passiveMotion(int x, int y) {
switch (gamePhase) {
case gameStarted:
if(x > 540 && x < 1000)
gunYrot = ((-(atan(abs(y-720) / (double)(x-540)))) * 180 / M_PI);
else if(x > 80 && x < 540)
gunYrot = (((atan(-abs(y - 720) / (double)(x - 540)))) * 180 / M_PI - 180);
if (y > 320 && y < 355) gunZrot = y;
break;
default: break;
}
}
The game screen is this:
Any clues about what could be the best approach to move the rifle?
Bonus question: at the moment, the bullet is shot from the point in which the rifle is rendered (so it starts from its center); how could I dinamically compute the coordinates of where the gun barrel ends to make it the starting point of the bullet?
Thank you for any help with this.

Why is my collision response not stopping my player from going through walls (SDL2, C++)?

While I found many problems that are similar too mine, none of the solutions solved my problem.
I've been experimenting with SDL2 in C++ (Visual C++) and the entity-component-system (ECS). But I just can't figure out the bug in my collision response.
So here it is: My player sometimes gets set back to its origin when it encounters something like a rock (a simple gray tile). But sometimes it goes right through and gets stuck or ends up on the other side.
I can only assume it has something to do with the data changed in between frames, so it isn't always caught. But for the life of me I can't figure it out.
Here is my rectangular detection method:
bool Collision::RectIntersect(const SDL_Rect& a, const SDL_Rect& b, SDL_Rect& intersect)
{
intersect = { 0, 0, 0, 0 };
int leftX = std::max(a.x, b.x);
int rightX = std::min(a.x + a.w, b.x + b.w);
int topY = std::max(a.y, b.y);
int bottomY = std::min(a.y + a.h, b.y + b.h);
if (leftX < rightX && topY < bottomY)
{
intersect = { leftX, topY, rightX - leftX, bottomY - topY };
return true;
}
return false;
}
Here is my snippet where my inputs are handled and subsequently any collision detections are resolved before the code actually moves anything:
void InputComponent::handleEvents(SDL_Event* e)
{
const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
if (e != nullptr)
{
/*
keyHeld: array of 4 for each direction (+/- x, +/- y (WASD))
hold value true, if pressed down, otherwise false
*/
if (keyboardState[SDL_SCANCODE_A])
{
keyHeld[0] = true;
}
else
{
keyHeld[0] = false;
}
if (keyboardState[SDL_SCANCODE_D])
{
keyHeld[1] = true;
}
else
{
keyHeld[1] = false;
}
if (keyboardState[SDL_SCANCODE_W])
{
keyHeld[2] = true;
}
else
{
keyHeld[2] = false;
}
if (keyboardState[SDL_SCANCODE_S])
{
keyHeld[3] = true;
}
else
{
keyHeld[3] = false;
}
}
/*
tmpVel: Vector to store the assumed velocity in x- and y-direction
*/
Vector2D tmpVel(0.0f, 0.0f);
// left and right (A and D)
if (keyHeld[0] && !keyHeld[1]) // left
{
tmpVel.x = -1.0f;
}
else if (!keyHeld[0] && keyHeld[1]) // right
{
tmpVel.x = 1.0f;
}
else
{
tmpVel.x = 0.0f; // left and right cancel each other out
}
// up and down (W and S)
if (keyHeld[2] && !keyHeld[3]) // up
{
tmpVel.y = -1.0f;
}
else if (!keyHeld[2] && keyHeld[3]) // down
{
tmpVel.y = 1.0f;
}
else
{
tmpVel.y = 0.0f; // up and down cancel each other out
}
/*
check for collision with presumed direction according to tmpVel
*/
SDL_Rect intersection;
// get current player position
SDL_Rect movedPlayer = entity->getComponent<CollisionComponent>().getCollider();
// add trajectory of theoretical movement
movedPlayer.x += static_cast<int>(tmpVel.x * vel_->getSpeed());
movedPlayer.y += static_cast<int>(tmpVel.y * vel_->getSpeed());
bool hasCollided = false;
// collect all collidable objects
for (auto& c : manager_->getGroup(GroupLabel::GR_COLLIDERS))
{
// check player against each collidable tile
//if (SDL_IntersectRect(&movedPlayer, &c->getComponent<CollisionComponent>().getCollider(), &intersection))
if (Collision::RectIntersect(movedPlayer, c->getComponent<CollisionComponent>().getCollider(), intersection))
{
// collision on x-axis
if (intersection.w > 0)
{
// set velocity on x-axis to 0
vel_->setVelocityX(0.0f);
// reset player position back according to width of intersected rectangle
pos_->setPosX(pos_->getPos().x + (static_cast<float>(intersection.w) * (-tmpVel.x)));
}
// collision on y-axis
if (intersection.h > 0)
{
// set velocity on y-axis to 0
vel_->setVelocityY(0.0f);
// reset player position back according to height of intersected rectangle
pos_->setPosY(pos_->getPos().y + (static_cast<float>(intersection.h) * (-tmpVel.y)));
}
hasCollided = true;
}
}
if (!hasCollided)
{
vel_->setVelocity(tmpVel);
}
}
Can anybody put me in the right direction?
What happens when the right edge of the player exactly equals the left edge of the rock? It looks like the collision is not detected, since the test is for (leftX < rightX). So the velocity is updated and the player is moved by the velocity. (It's odd that you simply update the velocity and later move the player instead of just moving them to the new already calculated position.) If you change the check to (leftX <= rightX), does the problem persist?
As far as I can see there are two things wrong with your collision detection. The first is that you're testing (leftX < rightX && topY < bottomY) when you should be testing (leftX <= rightX && topY <= bottomY). If you fix this your code will work in most situations.
The second problem you've got, which may not become apparent straight away, is that your are performing collision detection for discreet points in time. If your player has a large enough velocity vector you may end up with this situation:
Update 1: Player is in front of wall travelling towards it. AABB test shows no collision.
Update 2: Player is behind wall travelling away from it. AABB test shows no collision.
Your AABB test is correct and yet the player has passed through the wall. The naive approach to fixing this is to test more often (update 1.5 may have shown a collision), or to limit player velocity. Both approaches will require a lot of fine tuning especially if you're dealing with objects that can move at different speeds and walls with differing thickness.
A more robust approach is to take account of velocity in your test. Since you know the velocity of your AABB you can project this shape along its velocity vector. If you do this for both AABBs you'll end up with two elongated shapes which you can test against each other. If they overlap then you know that their paths cross and that there may be a collision.
Of course, knowing that there might be a collision is not hugely helpful. The problem is one AABB may be moving very slowly and the other very quickly so even though they both pass through the same space (their elongated shapes intersect) they don't pass through it at the same time.
Figuring out whether they both pass through the same space at the same time is hard, so instead we cheat. If you subtract the velocity of B from the velocity of A and then use this modified velocity to project the elongated shape of A, you can effectively treat B as a stationary object and still get the correct result. Knowing this, your test is now "does B overlap the elongated shape of A?". This is just a simple AABB vs Ngon problem.
While the above will give you a boolean as to whether two moving AABBs collide it will not tell you when they collide which is also useful for calculating things like rebounds.
I would very much recommend the book Real Time Collision Detection by Christer Ericson which is pretty much the go to book on collision detection for any aspiring game developer.
The following is a code snippet from the CD-ROM which accompanies the book. It tests a moving AABB against another moving AABB and also provides a time of first contact.
// Intersect AABBs ‘a’ and ‘b’ moving with constant velocities va and vb.
// On intersection, return time of first and last contact in tfirst and tlast
int IntersectMovingAABBAABB(AABB a, AABB b, Vector va, Vector vb, float &tfirst, float &tlast)
{
// Exit early if ‘a’ and ‘b’ initially overlapping
if (TestAABBAABB(a, b)) {
tfirst = tlast = 0.0f;
return 1;
}
// Use relative velocity; effectively treating ’a’ as stationary
Vector v = vb - va;
// Initialize times of first and last contact
tfirst = 0.0f;
tlast = 1.0f;
// For each axis, determine times of first and last contact, if any
for (int i = 0; i < 3; i++) {
if (v[i] < 0.0f) {
if (b.max[i] < a.min[i]) return 0;
// Nonintersecting and moving apart
if (a.max[i] < b.min[i]) tfirst = Max((a.max[i] - b.min[i]) / v[i], tfirst);
if (b.max[i] > a.min[i]) tlast = Min((a.min[i] - b.max[i]) / v[i], tlast);
}
if (v[i] > 0.0f) {
if (b.min[i] > a.max[i]) return 0;
// Nonintersecting and moving apart
if (b.max[i] < a.min[i]) tfirst = Max((a.min[i] - b.max[i]) / v[i], tfirst);
if (a.max[i] > b.min[i]) tlast = Min((a.max[i] - b.min[i]) / v[i], tlast);
}
// No overlap possible if time of first contact occurs after time of last contact
if (tfirst > tlast) return 0;
}
return 1;
}
The following attribution is required by Elsevier's Software License Agreement:
“from Real-Time Collision Detection by Christer Ericson, published by Morgan Kaufmann Publishers, © 2005 Elsevier Inc”

2d tilemap collision for SFML

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.

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.

Circular collision rebound not working properly

I'm writing a little physics simulation in C++ that basically moves circles across the screen and when two circles collide, they should ricochet in the same manner as billiard balls would. When the circles do collide with each other, most of the time they will practically slow down infinitely/they appear to stick to each other and become static. Sometimes only one ball will rebound in the collision and the other will retain it's trajectory. This is just a simple 2D simulation.
So here's what I have for the detection/ricochet logic:
bool Ball::BallCollision(Ball &b2)
{
if (sqrt(pow(b2.x - x, 2) + pow(b2.y - y, 2)) <= b2.radius + radius) // Test for collision
{
normal[0] = (x - (x + b2.x) / 2) / radius; // Finds normal vector from point of collision to radius
normal[1] = (y - (y + b2.y) / 2) / radius;
xvel = xvel - 2 * (xvel * normal[0]) * normal[0]; // Sets the velocity vector to the reflection vector
yvel = yvel - 2 * (yvel * normal[1]) * normal[1];
////x = xprev; // These just move the circle back a 'frame' so the collision
////y = yprev; // detection doesn't detect collision more than once.
// Not sure if working?
}
}
I can't figure out what is wrong with my function. Thanks for any help in advance!
Edit:
Every variable is declared as a float
The functions:
void Ball::Move()
{
xprev = x;
yprev = y;
x += xvel;
y += yvel;
}
void Ball::DrawCircle()
{
glColor3ub(100, 230, 150);
glBegin(GL_POLYGON);
for (int i = 0; i < 10; i++)
{
angle = i * (2*3.1415/10);
newx = x + r*cos(angle);
newy = y + r*sin(angle);
glVertex2f(newx, newy);
}
glEnd();
}
The loop:
run_prev.clear(); // A vector, cleared every loop, that holds the Ball objects that collided
for (int i = 0; i < num_balls; i++)
{
b[i].Move();
}
for (int i = 0; i < num_balls; i++)
{
b[i].WallCollision(); // Just wall collision detecting, that is working just fine
}
//The loop that checks for collisions... Am I executing this properly?
for (int i = 0; i < num_balls; i++)
{
for (int j = 0; j < num_balls; j++)
{
if (i == j) continue;
if (b[i].BallCollision(b[j]) == true)
{
run_prev.push_back(b[i]);
}
}
}
for (int i = 0; i < num_balls; i++)
{
b[i].DrawCircle();
}
//xprev and yprev are the x and y values of the frame before for each circle
for (int i = 0; i < run_prev.size(); i++)
{
run_prev[i].x = run_prev[i].xprev;
run_prev[i].y = run_prev[i].yprev;
}
Makes balls collide (reflect movement vector) only if they're moving towards each other. Do not process collision if they're moving away from each other. Break this rule, and they'll be glued together.
When processing collision, update both balls at once. Do not update one ball at a time.
Your move vector adjustment is incorrect. Balls don't reflect against each other, because they can be moving at different speeds.
Correct movement adjustment (assuming balls have equal mass) should look something like that:
pos1 and pos2 = positions;
v1 and v2 are movement vector (speed);
n is collision normal == normalize(pos1 - pos2);
collisionSpeed = dot((v2-v1), n);
collisionSpeed *= elasticy; (0.0..1.0);
v1 = v1 - dot(v1, n);
v2 = v2 - dot(v2, n);
v1 -= scale(n, collisionSpeed * 0.5);
v2 += scale(n, collisionSpeed * 0.5);
To understand the formula, check newtons law (impulses in particular). Or check Chris Hecker's papers on game physics.
It's not clear how you're calling this function, but I think I see the problem.
Say you have Ball ballA and Ball ballB, which are colliding in the current frame, and then you run ballA.BallCollision(ballB).
This will update the member variables of ballA, and move it back a frame. But it doesn't update the position or trajectory of ballB.
Even if you call the converse as well (ballB.BallCollision(ballA)), it won't detect the collision because when you called ballA.BallCollision(ballB), it moved ballA back a frame.
I haven't looked at your code in detail, but it doesn't take into consideration that this type of collision can only work in center of momentum frames. Now, I assume your balls are of equal masses. What you do is take the average of the two momentums (or velocities since they have the same masses) and subtract that average from the velocities. Perform your calculations, and add the average back. Here is the question I asked that may relate to this.
I know this question is quite old, but it's still relevant, especially to students. Something that wasn't mentioned in the answers made me want to contribute.
One thing that I ran into when solving a similar problem was overlap. That is, if the moving balls overlap by any amount at all, the collision detection will trigger continuously, giving the sticking behavior the OP referred to.
There was an attempt here to prevent this by moving the balls to the previous frame, but that can occasionally fail if the movement was enough that the balls enmeshed more than a single frame can account for, or if the movement velocity is just right so that the frame before doesn't trigger collision but the frame after is too far overlapped.
Since the original check was for center distance less than or equal to the sum of the radii, the detection triggers on both collision AND overlap.
One way to fix this is to separate the test into checking for collision (equals only) or overlap (less than only). For the collision, proceed as normal. But for the overlap condition, you can physically move one ball or the other (or both by half) the amount of overlap. This positions them at correct "collision" position, which allows for the correct behavior of the bounce function.
An overlap function that only moves one ball at a time might look something like this(not real code):
if (distanceBetweenBallCenters < sumOfRadii){
currentPosition = oldPosition - (distanceBetweenBallCenters - sumOfRadii) * (unitVectorFromSecondBallToFirstBall);
}
One could easily move both balls by half, but I found that moving one at a time gave satisfactory results for my uses, and allowed me to keep the parameter as a const.
I hope this helps future students! (I am also a student, and new to this, so take my advice with the proverbial grain of salt)
Your way of calculating the normal is wrong. (x + b2.x)/2 doesn't have to be the point of collision, if the radii of the balls aren't equal.