I'm trying to make a game of Pong. I have drawn everything to screen, bounce the ball of the walls, and have made sure the paddle can only be in the map. The one problem I am having is with collision detection. The algorithm I am using works great if the ball (which is actually a square) hits the paddle in the middle without having any part of the ball hanging off the edge of the paddle. If the ball hits the corner of the paddle or some of the ball hangs off the paddle then the ball glitches into the paddle and does not bounce back. This is my algorithm...
bool CheckCollision(float Ax,float Ay,float Aw,float Ah,float Bx,float By,
float Bw,float Bh) {
if(Ax>Bx+Bw) {
return(false);
}else if(Ax+Aw<Bx) {
return(false);
}else if(Ay>By+Bh) {
return(false);
}else if(Ay+Ah<By) {
return(false);
}
return(true);
}
the A and the B represent to different objects and the x, y, w, and h stands for x pos, y pos, width, and height of that specific object. On the receiving end I have...
if(CheckCollision(ball->x,ball->y,ball->width,ball->height,paddle->x,paddle->y,
paddle->width,paddle->height)) {
ball->vellx = -ball->vellx;
}
This works as I said if the ball hits the paddle in the middle.
If you understood this (as I know that I am not the best with words) is there any way that I can have it so that the ball does not glitch into the paddle? If not, is there another algorithm I can use? Keep in mind this is only 2D.
This is a screenshot of what is happening
http://gyazo.com/920d529217864cca6480a91b217c9b61
As you can see the ball has no collision on the very sides of it, just the points.
I hope this helps
ball->vellx = -ball->vellx;
That's incorrect.
When collision is detected, you need to invert (x) velocity only if ball moves towards paddle. If ball already moves away from paddle, do nothing, and do not modify velocity.
If you'll blindly invert velocity during every collision, ball will get stuck in paddle.
P.S. There are some other things you could tweak (like making ball properly bounce against the corners, adjusting ball speed based on paddle velocity, etc), but that's another story...
Related
I am learning C++ and I thought I'd make the original asteroids game with a fresh coat of paint using the SFML graphics library. However, for my player sprite, while the origin is at the top left corner of the screen, to the right of it is the negative x axis and downwards is negative y axis (opposite of what it's supposed to be in both cases). Also, no matter what object or rotation, invoking the setRotation function always rotates any object about the top left corner of the screen even if, for that object, I have set the origin to the object's center.
#include<SFML\Graphics.hpp>
using namespace sf;
const int W{ 1200 }, H{ 800 };
const float degToRad = 0.017453f;
int main() {
float x{ -600 }, y{ -400 };
float dx{}, dy{}, angle{};
bool thrust;
RenderWindow app(VideoMode(W, H), "Asteroids!");
app.setFramerateLimit(60);
Texture t1, t2;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
Sprite sPlayer(t1), sBackground(t2);
sPlayer.setTextureRect(IntRect(40, 0, 40, 40));
sPlayer.setOrigin(-600, -400);
while (app.isOpen())
{
app.clear();
app.draw(sPlayer);
app.display();
}
return 0;
}
The above code draws the player (spaceship.png) to the center of my rendered window (app) but notice how I have had to put in negative coordinates. Also, if I further put in the code for taking keyboard inputs and call the setRotation function, instead of rotating my sPlayer sprite about its center (i.e. (-600,-400)), it rotates the sprite about the top left corner of the screen which is (0,0). I can't find any explanation for this in the SFML online documentation. What should I do?
As I mentioned I have tried reading the documentation. I've watched online tutorials but to no avail.
Origin is the point on sprite where you "hold" it.
Position is the point on screen where you put Origin of the sprite.
In short, you take your sprite by Origin and put it so Origin is on Position.
By default, both Origin and Position are (0, 0), so top left of your sprite is put at top left of the screen. What you did was to say "take this point on sprite, which is way to the upper-left that actual visible part of the sprite is and put it to the top left of the screen". This had an effect of moving your sprite to the bottom right.
You probably want something like:
// This is will make sure that Origin, i.e. point which defines rotation and other transformations center is at center of the ship
sPlayer.setOrigin(sprite_width / 2, sprite_height / 2);
// This will put Origin (center of the ship) at center of the screen
sPlayer.setPosition(screen_width / 2, screen_height / 2);
I have been writing a game engine in my free time, but I've been stuck for a couple weeks trying to get collisions working.
Currently I am representing entities' colliders with AABBs, and the collider for a level is represented by a fairly simple (but not necessarily convex) polyhedron. All the drawing is sprite-based but the underlying collision code is 3D.
I've got AABB/triangle collision detection working using this algorithm, (which I can naively apply to every face in the level mesh), but I am stuck trying to resolve the collision after I have detected its existence.
The algorithm I came up with works pretty well, but there are some edge cases where it breaks. For example, walking straight into a sharp corner will always push the player to one side or the other. Or if a small colliding face happens to have a normal that is closer to the players direction of movement than all other faces, it will "pop" the player in that direction first, even though using the offset from a different face would have had a better result.
For reference, my current algorithm looks like:
Create list of all colliding faces
Sort list in increasing order of the angle between face normal and negative direction of entity movement (i.e. process faces with the most "stopping power" first)
For each colliding face in collision list:
scale = distance of collision along face normal
Entity position += face normal * scale
If no more collision:
break
And here's the implementation:
void Mesh::handleCollisions(Player& player) const
{
using Face = Face<int32_t>;
BoundingBox<float> playerBounds = player.getGlobalBounds();
Vector3f negPlayerDelta = -player.getDeltaPos(); // Negative because face norm should be opposite direction of player dir
auto comparator = [&negPlayerDelta](const Face& face1, const Face& face2) {
const Vector3f norm1 = face1.normal();
const Vector3f norm2 = face2.normal();
float closeness1 = negPlayerDelta.dot(norm1) / (negPlayerDelta.magnitude() * norm1.magnitude());
float closeness2 = negPlayerDelta.dot(norm2) / (negPlayerDelta.magnitude() * norm2.magnitude());
return closeness1 > closeness2;
};
std::vector<Face> collidingFaces;
for (const Face& face : _faces)
{
::Face<float> floatFace(face);
if (CollisionHelper::collisionBetween(playerBounds, floatFace))
{
collidingFaces.push_back(face);
}
}
if (collidingFaces.empty()) {
return;
}
// Process in order of "closeness" between player delta and face normal
std::sort(collidingFaces.begin(), collidingFaces.end(), comparator);
Vector3f totalOffset;
for (const Face& face : collidingFaces)
{
const Vector3f& norm = face.normal().normalized();
Point3<float> closestVert(playerBounds.xMin, playerBounds.yMin, playerBounds.zMin); // Point on AABB that is most negative in direction of norm
if (norm.x < 0)
{
closestVert.x = playerBounds.xMax;
}
if (norm.y < 0)
{
closestVert.y = playerBounds.yMax;
}
if (norm.z < 0)
{
closestVert.z = playerBounds.zMax;
}
float collisionDist = closestVert.vectorTo(face[0]).dot(norm); // Distance from closest vert to face
Vector3f offset = norm * collisionDist;
BoundingBox<float> newBounds(playerBounds + offset);
totalOffset += offset;
if (std::none_of(collidingFaces.begin(), collidingFaces.end(),
[&newBounds](const Face& face) {
::Face<float> floatFace(face);
return CollisionHelper::collisionBetween(newBounds, floatFace);
}))
{
// No more collision; we are done
break;
}
}
player.move(totalOffset);
Vector3f playerDelta = player.getDeltaPos();
player.setVelocity(player.getDeltaPos());
}
I have been messing with sorting the colliding faces by "collision distance in the direction of player movement", but I haven't yet figured out an efficient way to find that distance value for all faces.
Does anybody know of an algorithm that would work better for what I am trying to accomplish?
I'm quite suspicious with the first part of code. You modify the entity's position in each iteration, am I right? That might be able to explain the weird edge cases.
In a 2D example, if a square walks towards a sharp corner and collides with both walls, its position will be modified by one wall first, which makes it penetrate more into the second wall. And then the second wall changes its position using a larger scale value, so that it seems the square is pushed only by one wall.
If a collision happens where the surface S's normal is close to the player's movement, it will be handled later than all other collisions. Note that when dealing with other collisions, the player's position is modified, and likely to penetrate more into surface S. So at last the program deal with collision with surface S, which pops the player a lot.
I think there's a simple fix. Just compute the penetrations at once, and use a temporal variable to sum up all the displacement and then change the position with the total displacement.
I'm creating a pong clone for school with C++ and SFML 2.1 and I'm having a little problem when the ball hits the left paddle at sharp angles (It goes through).
The right paddle works fine at all angles, and as far as i can remember they're using the same code.
This is the code I'm using for collision:
for (auto& it : collisionPaddles)
{
if (this->ballShape.getGlobalBounds().intersects(it->getGlobalPaddleBounds()))
{
float deltaDistance = (this->y + this->radius) - (it->y + it->height / 2);
bool fromLeft = true;
if ((ballAngle < (3*myMath::MY_PI/2) && ballAngle > myMath::MY_PI/2))
{
fromLeft = false;
}
else
{
fromLeft = true;
}
ballAngle = static_cast<float>(deltaDistance * (myMath::MY_PI/180));
if (fromLeft)
{
ballAngle = static_cast<float>(myMath::MY_PI - ballAngle);
}
moveBall(2);
}
}
That's not the way a good pong should be implemented. When your model of physics uses some deltas, you get a lot of artefacts. The one of most prominent is objects passing through each other. In such simple cases deltas should be used for animation only.
Here's the way I would solve it. When the ball gets its initial speed and vector of movement, I would calculate the whole path until it hits the player's baseline.
Calculate an initial ray from the current position of the ball, directed the same way it moves.
Intersect that ray with segments that consistute the borders of your field.
Calculate the time that is needed to reach that intersection point.
Calculate the new ray from the intersection point towards the new movement vector.
Repeat steps 2-4 until you hit a player's base line. Sum all the times.
Now you have a relative time of the hit with baseline and the place it will happen. Now on every frame you should
Check if that collision time has been between the previous frame and the current one. If it was,
Calculate the expected position of paddle at that moment.
If the segment of paddle contains that intersection point, it was reflected. Calculate the new path as described before.
This way you'll get true game mechanics that are not limited by most of the aspects of game development, i.e. FPS, sudden lags etc.
I have some collision detection working when my player hits an object. But this only works when my players x & y co-ordinates hit my marker (which is the centre of my character).
Would making a method returning a vector of all of the coordinates that the players texture cover work and what is the best way to implement this?
This is being done in c++ creating a top down game
There are many ways to do it, most simply is probably(depending on you use of classes etc).
This is the simplest, but no where near the best, or infact very good at all. This way means changing your "marker" to the bottom left of the rectangle.
void collisions()
{
//check if the x-coord is between the furthest left and furthest right x coords
if(rect.Getx() > someObject.Getx() && rect.Getx() < someObject.Getx() + someObject.GetWidth())
{
rect.SetMoveSpeed(0);
}
if(rect.Gety() > someObject.Gety() && rect.Gety() < someObject.Gety() + someObject.GetHeight())
{
rect.setMoveSpeed(0);
}
}
You would then have to set the move speed to normal when it is not colliding. That could be done with an else after each if, setting the move speed again. This is a quick fix and is not recommended for use in a game you plan to distribute anywhere.
I am creating a space invaders game for my university assignment. I am breaking the parts down on paper, I thought a good place to start would be to move the sprites across the screen. The issue I have at the moment is this, when the sprite his the right hand side of the screen it would just carry on moving, so I added this code:
if (invadersSprite.getPosition().x > 650)
{
std::cout << "WIDTH_END" << std::endl;
}
I basically used this for debugging for future reference. The output popped up into the console so I knew I was onto something. So, I started moving on from the output to actually moving the sprites back to the left hand side of the screen. I am now having more issues then expected.
I declared some ints for up, left, right and down just for easability when it comes to reading the code.
int spriteWalkSpeed = 50;
int up=-spriteWalkSpeed, down=spriteWalkSpeed, left=-spriteWalkSpeed, right=spriteWalkSpeed;
I've tried using the SFML move commands and also setPosition, but none of them really work as I thought they should.
move:
The issue I have had with this one is it basically stops in the middle of the screen. It still animates the leg movement and such, but it doesn't move left or right.
if (invadersSprite.getPosition().x > 650)
{
invadersSprite.move(left, 00);
}
setPosition:
This is a little closer to what I am after, but still no cigar. The movement pauses for around a second and then pops up at the left hand side of the screen. What I need is to move back down the screen.
if (invadersSprite.getPosition().x > 650)
{
invadersSprite.setPosition(left, 00);
}
I haven't been using SFML for very long so I am a little puzzled by this.
EDIT
At the moment the sprite is moving to the right of the screen, as needed:
if(spriteTimer>delay)
{
invadersSprite.setTextureRect(area);
++count;
invadersSprite.move(right,0);
if(count==SPRITECOLS) //WE HAVE MOVED OFF THE RIGHT OF THE IMAGE
{
area.left=0; //reset texture rect at left
count=0; //reset count
}
else
{
area.left+=spaceWidth; //move texture rect right
}
spriteTimer=0; //we have made one move in the sprite tile - start timing for the next move
}
Your current logic will only move the sprite left once if the x-coord is greater than 650 - but that's not exactly what you want. What you want is for the sprite to continuously move left after reaching the edge.
To do this you'll need to keep track of a current velocity for your sprite. For example, when your sprite starts, it will be moving right:
xVelocity = right;
Then you can have 2 conditions that will switch direction (when hitting edges of screen):
if (invaderSprite.getPosition().x >= screenWidth) {
// hit the right side of screen
xVelocity = left;
} else if (invaderSprite.getPosition().x <= 0) {
// hit the left side of screen
xVelocity = right;
}
Now, on ever game tick, you simply move your sprite according to its current velocity:
invaderSprite.move(xVelocity, yVelocity);
There are some other things that will need to be fixed, like taking the width of the sprite into consideration, changing the y velocity, etc., but this is the main idea.