-
Hello everybody!
First of all: Excuse my terrible English ;)
Im new to SFML and I made a little Program that shows some Circles Bouncing around in the window. That works fine.
But now I want that the Circles bounce away when the collide with another Circle.
How can I implement that?
-
You'd want to make it so the circles have boundaries; one of the simplest ways I've worked with collision is box collision - that is, you make a box around the entity you wish to implement collision for, and when two entities collide (for example, when the left boundary of thisCircle is at the same coordinates of the right boundary of thatCircle), you can then add code as to how they behave.
-
You don't need bounding boxes for only circles. Circle to circle collisions detection is easy: if the distance between their centers is longer than the sum of their radiuses, they collide.
Here's a good tutorial: http://gamedevelopment.tutsplus.com/tutorials/when-worlds-collide-simulating-circle-circle-collisions--gamedev-769
-
You don't need bounding boxes for only circles. Circle to circle collisions detection is easy: if the distance between their centers is longer than the sum of their radiuses, they collide.
Here's a good tutorial: http://gamedevelopment.tutsplus.com/tutorials/when-worlds-collide-simulating-circle-circle-collisions--gamedev-769
^ And it's things like this that I need to remember if I plan to get a job programming... Ignore my first post; sorry!
-
Read this: http://www.gamasutra.com/view/feature/131424/pool_hall_lessons_fast_accurate_.php?print=1 (http://www.gamasutra.com/view/feature/131424/pool_hall_lessons_fast_accurate_.php?print=1)
It describes very nicely how to do circle/circle collision detection.
-
Okay, Thanks for the fast replies, I will try to implement the methods from the tutorials later.
I found a new Problem: I use a Clock, a xSpeed and a ySpeed to Calculate the Bouncing-Direction, when a Ball Collides with a Window-Border. But sometimes, a Ball slides along the Window Border, it doesnt Bounce away. :(
Have you any Idea what I can do to solve this Problem?
-
When you detect a collision you need to calculate bounce, the new direction and length of your velocity vector, depending on where on the circle the collision happens and the velocities and mass of the objects involved.
This is all described in great detail in the gamasutra article I linked you to above.
A little physics and vector math is really all that's needed and in 2d it's not too complicated.
Ohh and by the way, a sf::Vector2f is a convenient container for you vectors rather than xSpeed/ySpeed variables, but it makes no real difference, it's just convenient :)
-
Okay, I solved the Sliding-Window-Border-Problem by making some changes with the Clock. :)
-
Okay,
my Project is nearly complete, but I've noticed that my CPU is getting hot when I run my Application.
It uses a lot of the system resources. ???
Is there any way to stop that?
I've got Win7 Ultimate, an i5-CPU and 8GB RAM.
Thanks in advance for your Help!
-
Did you enable vertical synchronization or set a frame rate limit?
-
Oops... Thanks a lot, G!
I enabled Vertical Sync and now it works perfectly without slowing down my system. :)
-
Theres another problem now... I thought I solved it, but now its there again... Sometimes, when a Ball Collides with the Window-Border and should Bounce away, it sticks on the border, and only moves at one Axis. For example, when the Ball reaches the Top Window-Border and should bounce away, it sticks on the border and only moves from one side to the other on the x-Axis.
Has anyone a possible solution?
I appreciate any help and patience with an Beginner like me! :)
-
Theres another problem now... I thought I solved it, but now its there again... Sometimes, when a Ball Collides with the Window-Border and should Bounce away, it sticks on the border, and only moves at one Axis. For example, when the Ball reaches the Top Window-Border and should bounce away, it sticks on the border and only moves from one side to the other on the x-Axis.
Has anyone a possible solution?
I appreciate any help and patience with an Beginner like me! :)
Can you show us the code?
-
Of course, Azaral!
Here it is:
#include <SFML/Graphics.hpp>
#include <windows.h>
#include <iostream>
#include <vector>
using namespace std;
int width = GetSystemMetrics(SM_CXSCREEN); // Get the Screen-Width
int height = GetSystemMetrics(SM_CYSCREEN); //Get the Screen-Height
int ballradius = 50;
vector <float> xSpeed{53.4f, 71.7f, 76.7f, 72.5f, 76.1f, 46.1f, 74.4f, 75.6f, 53.2f, 51.8f}; // Well, I dont know if thats very clever...
vector <float> ySpeed{77.9f, 70.9f, 70.9f, 17.9f, 50.9f, 19.2f, 73.6f, 73.5f, 60.1f, 63.9f};
bool RightBorderTouching(int TempPositionX) // Yep, I know that you can do this shorter, but I like 'comprehensive' code.
{
int RightBorderPosition = TempPositionX + ballradius * 2;
if(RightBorderPosition >= width){return true;}
if(RightBorderPosition < width){return false;}
}
bool LeftBorderTouching(int TempPositionX)
{
int LeftBorderPosition = TempPositionX;
if(LeftBorderPosition <= 0){return true;}
if(LeftBorderPosition > 0){return false;}
}
bool TopBorderTouching(int TempPositionY)
{
int TopBorderPosition = TempPositionY;
if(TopBorderPosition <= 0){return true;}
if(TopBorderPosition > 0){return false;}
}
bool BottomBorderTouching(int TempPositionY)
{
int BottomBorderPosition = TempPositionY + ballradius * 2;
if(BottomBorderPosition >= height){return true;}
if(BottomBorderPosition < height){return false;}
}
int main()
{
cout << "Width: " << width << endl;
cout << "Height: " << height << endl;
vector <sf::CircleShape> Ball(10); // Should I make the Balls within a vector or separately?
for(int i = 0; i < 10; i++ )
{
Ball[i].setRadius(ballradius);
Ball[i].setPointCount(100);
Ball[i].setFillColor(sf::Color(34 , 186 , 186 ));
Ball[i].setOutlineThickness(5);
Ball[i].setOutlineColor(sf::Color(88, 97, 97));
Ball[i].setPosition( 245 , 245);
}
sf::Clock clock;
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(width, height), "Bouncing Balls", sf::Style::Fullscreen, settings);
window.setVerticalSyncEnabled(true);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Escape)
{
window.close();
}
}
}
auto dt = clock.restart().asSeconds();
window.clear(sf::Color::White);
for(int i = 0; i < 10; i++ )
{
Ball[i].move(xSpeed[i] * dt, ySpeed[i] * dt );
}
for(int i = 0; i < 10; i++ )
{
window.draw(Ball[i]);
}
window.display();
for(int i = 0; i < 10; i++ )
{
if(RightBorderTouching(Ball[i].getPosition().x)) {xSpeed[i] = -xSpeed[i];}
if(LeftBorderTouching(Ball[i].getPosition().x)) {xSpeed[i] = -xSpeed[i];}
if(TopBorderTouching(Ball[i].getPosition().y)) {ySpeed[i] = -ySpeed[i];}
if(BottomBorderTouching(Ball[i].getPosition().y)){ySpeed[i] = -ySpeed[i];}
}
}
return 0;
}
-
You forgot to move ball so it doesn't collide anymore. For example ball at {x:50 y:0} and radius 10 should be moved to {x: 50 y:10}.
-
You forgot to move ball so it doesn't collide anymore. For example ball at {x:50 y:0} and radius 10 should be moved to {x: 50 y:10}.
Okay, but what can I do to solve this Problem? First I set every x and y Speed to over 50, but that didnt work. So I think I misunderstood you :-\
Which Values do I need to change?
-
I meant position. If RightBorderTouching returns true, you should move the ball and then change velocity.
-
Wow, thanks krzat! :)
I modified my code and now it looks like this:
for(int i = 0; i < 10; i++ )
{
if(RightBorderTouching(Ball[i].getPosition().x)) {Ball[i].move( -ballradius * dt, 0 * dt); xSpeed[i] = -xSpeed[i];}
if(LeftBorderTouching(Ball[i].getPosition().x)) {Ball[i].move( ballradius * dt, 0 * dt); xSpeed[i] = -xSpeed[i];}
if(TopBorderTouching(Ball[i].getPosition().y)) {Ball[i].move( 0 * dt, ballradius * dt); ySpeed[i] = -ySpeed[i];}
if(BottomBorderTouching(Ball[i].getPosition().y)){Ball[i].move( 0 * dt, -ballradius * dt); ySpeed[i] = -ySpeed[i];}
}
I think the Problem is gone now!
(If I did something wrong please tell me ;) )
-
Ok, today I finally got time to continue working on my project.
I created a bool to check for collisions:
bool Collision(const float& TempPositionY1, const float& TempPositionY2, const float& TempPositionX1, const float& TempPositionX2)
{
int distance = ((TempPositionX1 - TempPositionX2) * (TempPositionX1 - TempPositionX2)) + ((TempPositionY1 - TempPositionY2) * (TempPositionY1 - TempPositionY2));
if (distance < ballradius + ballradius )
{
return true;
}
else return false;
}
Okay, I think this should work.
But now I have to create a loop to check all possible combinations of Collisions.
I created something like that first:
for (int i = 0; i < ballcount; i++)
{
for (int j = i + 1; j < ballcount; j++)
{
if (Collision(Ball[i].getPosition().y, Ball[j].getPosition().y, Ball[i].getPosition().x, Ball[j].getPosition().x))
{
// Calculate new direction
}
}
}
But that doesnt detect all the collisions, for example Ball[4] and Ball[8].
So my question is:
How can I effectively cover all the possible Collisions in my loop?
-
You forgot to square the radiuses.
Short tip: You should abstract your code to make it much more readable.
How I would write the collision:
float SquaredLength(sf::Vector2f vector);
float Square(float value);
// 1. vectors instead of floats
// 2. pass by value instead of reference (for floats for sure, for vectors it's arguable)
// 3. shorter names (lhs, rhs are conventions for "left/right hand side")
bool Collision(sf::Vector2f lhs, sf::Vector2f rhs)
{
// 4. reuse existing functions
// 5. instead of if (...) return true; else return false;
// write return ...;
return SquaredLength(lhs - rhs) < Square(2*ballRadius);
}
If you deal a lot with vector operations, you could reuse the functions I wrote inThor.Vectors (http://www.bromeon.ch/libraries/thor/v2.0/doc/_vector_algebra2_d_8hpp.html). You don't need to build Thor for that, the Vectors module is header-only and can be used directly.
-
Thanks, Nexus!
But when I try to compile my code, it gives me these two errors: undefined reference to `SquaredLength(sf::Vector2<float>)', and
undefined reference to `Square(float)'.
Heres my if-statement:
sf::Vector2f lhs(Ball[1].getPosition().y, Ball[1].getPosition().x);
sf::Vector2f rhs(Ball[2].getPosition().y, Ball[2].getPosition().x);
if (Collision(lhs, rhs))
{
}
What did I do wrong?
-
You need to implement SquaredLength and Square of course. Nexus just gave the deceleration of the function. ;)
-
You did not implement the functions.
-
Im not getting it...
I declared Squared Length and Square at the Beginning:
float SquaredLength(sf::Vector2f vector);
float Square(float value);
Heres my Collision Bool:
bool Collision(sf::Vector2f lhs, sf::Vector2f rhs)
{
return SquaredLength(lhs - rhs) < Square(2*ballradius);
}
And heres my if-statement:
sf::Vector2f lhs(Ball[1].getPosition().y, Ball[1].getPosition().x);
sf::Vector2f rhs(Ball[2].getPosition().y, Ball[2].getPosition().x);
if (Collision(lhs, rhs))
{
}
Okay, and what has to be implemented now?
For me, this seems very plausible.
Oh god, im feeling stupid right now...
-
You have declared the signatures of the functions but you have not implemented them.
They are not pre-existing functions. You have to write their actual implementation yourself.
-
Ahh, thanks Jesper, now I understand... :)
(http://i.imgur.com/aKPkoZZ.jpg)
For me! :D
-
Okay, I've got a function for calculating the Square:
float Square(float value)
{
return value * value;
}
It think this should work.
But what is the Squared lenght?
Which lenght do you mean?
How do I have to calculate that?
-
The squared length is what you get from the dot product of a vector with itself. Its an optimization to not take the squareroot of it when you just need it for comparing it with another length and just square that length instead.
-
As stated earlier:
If you deal a lot with vector operations, you could reuse the functions I wrote in Thor.Vectors (http://www.bromeon.ch/libraries/thor/v2.0/doc/_vector_algebra2_d_8hpp.html). You don't need to build Thor for that, the Vectors module is header-only and can be used directly.
You can also have a look at the documentation and/or implementation in order to understand what these functions do. But they require some basic knowledge about vector algebra.
-
Okay, but where can I find the Header for SFML 2.1?
-
Success!
Okay, I downloaded the Thor library and searched for the SquareLenght function. I found it and Implemented it.
So heres my complete Collision detection:
float dotProduct(sf::Vector2f lhs, sf::Vector2f rhs)
{
return lhs.x * rhs.x + lhs.y * rhs.y;
}
float Square(float value)
{
return value * value;
}
float SquaredLength(sf::Vector2f vector)
{
return dotProduct(vector, vector);
}
bool Collision(sf::Vector2f lhs, sf::Vector2f rhs)
{
return SquaredLength(lhs - rhs) < Square(2*ballradius);
}
Thank you Nexus, for making such an awesome library!
-
Now I got a question about calculating the new velocities and directions.
I read the tutsplus article (http://gamedevelopment.tutsplus.com/tutorials/when-worlds-collide-simulating-circle-circle-collisions--gamedev-769 (http://gamedevelopment.tutsplus.com/tutorials/when-worlds-collide-simulating-circle-circle-collisions--gamedev-769)) , but the comments say that the method to calculate new directions and velocities is wrong.
So how should I calculate the collisions now?
-
Has anyone an Idea?
-
http://en.wikipedia.org/wiki/Elastic_collision
-
Have you tried the techniques in the gamasutra article I linked you to in my first reply in this thread?
I've used what's described in that article myself to calculate collisions between circles and circles and circles and straight surfaces, calculate the bounce and speed after collision etc. and it works very nicely.
-
Okay, I tried to Implement the Collision-Resolving.
But there's a problem: When the balls collide, they stick at each other and don't bounce away.
Here's some Code:
int ballradius = 50;
int mass = ballradius;
float newVelY1(float yVel1, float yVel2)
{
float NEWyVel1 = (yVel1 * (mass - mass) + (2 * mass * yVel2)) / (mass + mass);
return NEWyVel1;
}
float newVelY2(float yVel1, float yVel2)
{
float NEWyVel2 = (yVel2 * (mass - mass) + (2 * mass * yVel1)) / (mass + mass);
return NEWyVel2;
}
float newVelX1(float xVel1, float xVel2)
{
float NEWxVel1 = (xVel1 * (mass - mass) + (2 * mass * xVel2)) / (mass + mass);
return NEWxVel1;
}
float newVelX2(float xVel1, float xVel2)
{
float NEWxVel2 = (xVel2 * (mass - mass) + (2 * mass * xVel1)) / (mass + mass);
return NEWxVel2;
}
// In the main-loop:
for (int i = 0; i < ballcount; i++)
{
for (int j = i + 1; j < ballcount; j++)
{
if (Collision(sf::Vector2f(Ball[i].getPosition().y, Ball[i].getPosition().x), sf::Vector2f(Ball[j].getPosition().y, Ball[j].getPosition().x)))
{
Ball[i].move(newVelX1(xSpeed[i], xSpeed[j]) * dt, newVelY1(ySpeed[i], ySpeed[j]) * dt);
Ball[j].move(newVelX2(xSpeed[i], xSpeed[j]) * dt, newVelY2(ySpeed[i], ySpeed[j])* dt);
xSpeed[i] = newVelX1(xSpeed[i], xSpeed[j]);
xSpeed[j] = newVelX2(xSpeed[i], xSpeed[j]);
ySpeed[i] = newVelY1(ySpeed[i], ySpeed[j]);
ySpeed[j] = newVelY2(ySpeed[i], ySpeed[j]);
}
}
}
Has anybody an idea what I did wrong?
-
(mass - mass)
Why are you now again using separate coordinates instead of vectors? I think we agreed that vectors would make the code simpler and more readable ;)
And don't duplicate code, you can merge the four functions to one.
-
(mass - mass)
Why are you now again using separate coordinates instead of vectors? I think we agreed that vectors would make the code simpler and more readable ;)
And don't duplicate code, you can merge the four functions to one.
Yeah, you're right, but I don't think I have to use vectors, because every ball has the same mass. :)
But are the calculations right?
I don't understand why the balls stick together...
-
The very first line in my post! ::)
Of course you don't have to use vectors, but they simplify everything a lot. If you prefer verbose and badly abstracted code, that's your decision.
-
Ok, Nexus, now I defined the mass as a vector. :)
But how can I solve the sticky-balls problem?
-
I think that mass might be the only value that shouldn't be a vector since it's a single value. :p Vectors (in this case) are for positions, velocities etc.
As for your calculations, Nexus pointed out a tiny bit of your code that you should really study:
(mass - mass)
What does it do?
-
I think that mass might be the only value that shouldn't be a vector since it's a single value. :p Vectors (in this case) are for positions, velocities etc.
As for your calculations, Nexus pointed out a tiny bit of your code that you should really study:
(mass - mass)
What does it do?
Yeah, I thought Nexus meant that I should define mass as a vector... I don't know why is should do this, maybe I misunderstood him :-\
And the mass - mass thing is from the tutsplus article mentioned earlier. http://gamedevelopment.tutsplus.com/tutorials/when-worlds-collide-simulating-circle-circle-collisions--gamedev-769 (http://gamedevelopment.tutsplus.com/tutorials/when-worlds-collide-simulating-circle-circle-collisions--gamedev-769)
Its important when each ball has a different mass.
And yeah, then Vectors would be helpful ;)
-
(mass - mass)
Why are you now again using separate coordinates instead of vectors? I think we agreed that vectors would make the code simpler and more readable ;)
And don't duplicate code, you can merge the four functions to one.
If you are suggesting that mass should be a vector, it can't be as it is a scalar.
However, all of these functions can be combined into a single function and if the vector arithmetic is set up correctly, you do not have to calculate the values of the vectors separately.
Also, beware of changing the velocity of one before you calculate the new velocity of the other. If you do not, then you will not have a correct collision.
-
It's mass1 - mass2. It's the difference between the two masses. At the moment (and I didn't think I'd have to point this out explicitly) you are subtracting something from itself and it will always be zero. If the circles were to have different masses, you would use them there, but since they're all the same, you can remove (mass-mass) since it is 0.
The vectors (i.e. sf::Vector2f) are for the positions and velocities so that x and y can be conveniently in one variable. In some cases, calculation can be applied to the vector rather than directly to its components.
-
Also, beware of changing the velocity of one before you calculate the new velocity of the other. If you do not, then you will not have a correct collision.
I did that, didn't I? ???
-
It's mass1 - mass2. It's the difference between the two masses. At the moment (and I didn't think I'd have to point this out explicitly) you are subtracting something from itself and it will always be zero. If the circles were to have different masses, you would use them there, but since they're all the same, you can remove (mass-mass) since it is 0.
Are you sure that I can just remove it? It would give me a different result...
The vectors (i.e. sf::Vector2f) are for the positions and velocities so that x and y can be conveniently in one variable. In some cases, calculation can be applied to the vector rather than directly to its components.
Well, I know that's more convenient to use vectors, but I think that Im going to that after I solved the problem with the sticky balls
-
Are you sure that I can just remove it? It would give me a different result...
It wouldn't. You're subtracting zero. Removing that subtraction would change nothing. The (mass + mass) must stay, however, and leaving in the (mass - mass) won't harm anything so it's possibly safer to leave it there.
... the problem with the sticky balls
You have the vector values the wrong way around for the collision. X comes before Y.
Use:
if (Collision(
sf::Vector2f(Ball[i].getPosition().x, Ball[i].getPosition().y),
sf::Vector2f(Ball[j].getPosition().x, Ball[j].getPosition().y))
rather than:
if (Collision(
sf::Vector2f(Ball[i].getPosition().y, Ball[i].getPosition().x),
sf::Vector2f(Ball[j].getPosition().y, Ball[j].getPosition().x))
-
The idea of vectors is not to unpack them to single coordinates at every opportunity :(
Why don't you simply write it like this?
if (Collision(Ball[i].getPosition(), Ball[j].getPosition()))
-
Also, beware of changing the velocity of one before you calculate the new velocity of the other. If you do not, then you will not have a correct collision.
I did that, didn't I? ???
I don't know, I was just throwing that out there.
-
Why don't you simply write it like this?
if (Collision(Ball[i].getPosition(), Ball[j].getPosition()))
Or just do that :p Much simpler ;)
-
The idea of vectors is not to unpack them to single coordinates at every opportunity :(
Why don't you simply write it like this?
if (Collision(Ball[i].getPosition(), Ball[j].getPosition()))
Thanks, Nexus, I did that, but the problem is still not solved...
Here's the code so far:
for (int i = 0; i < ballcount; i++)
{
for (int j = i + 1; j < ballcount; j++)
{
if (Collision(Ball[i].getPosition(), Ball[j].getPosition()))
{
Ball[i].move(newVelX1(xSpeed[i], xSpeed[j]) * dt, newVelY1(ySpeed[i], ySpeed[j]) * dt);
Ball[j].move(newVelX2(xSpeed[i], xSpeed[j]) * dt, newVelY2(ySpeed[i], ySpeed[j])* dt);
xSpeed[i] = newVelX1(xSpeed[i], xSpeed[j]);
xSpeed[j] = newVelX2(xSpeed[i], xSpeed[j]);
ySpeed[i] = newVelY1(ySpeed[i], ySpeed[j]);
ySpeed[j] = newVelY2(ySpeed[i], ySpeed[j]);
}
}
}
Or did I something wrong with the order of calculating the new Velocities?
-
I'm not sure I fully understand the code here, but why are you moving the balls in the direction they were travelling before the collision, after finding that they've collided? Could it be possible that the new speeds should be calculated before the balls get moved?
-
I'm not sure I fully understand the code here, but why are you moving the balls in the direction they were travelling before the collision, after finding that they've collided?
The balls are moved to the new direction, not to the old. To make sure that they dont collide anymore.
Could it be possible that the new speeds should be calculated before the balls get moved?
If I understand you right, you mean something like this:
xSpeed[i] = newVelX1(xSpeed[i], xSpeed[j]);
xSpeed[j] = newVelX2(xSpeed[i], xSpeed[j]);
ySpeed[i] = newVelY1(ySpeed[i], ySpeed[j]);
ySpeed[j] = newVelY2(ySpeed[i], ySpeed[j]);
Ball[i].move(newVelX1(xSpeed[i], xSpeed[j]) * dt, newVelY1(ySpeed[i], ySpeed[j]) * dt);
Ball[j].move(newVelX2(xSpeed[i], xSpeed[j]) * dt, newVelY2(ySpeed[i], ySpeed[j])* dt);
or
xSpeed[i] = newVelX1(xSpeed[i], xSpeed[j]);
xSpeed[j] = newVelX2(xSpeed[i], xSpeed[j]);
ySpeed[i] = newVelY1(ySpeed[i], ySpeed[j]);
ySpeed[j] = newVelY2(ySpeed[i], ySpeed[j]);
Ball[i].move(xSpeed[i] * dt, ySpeed[i] * dt );
Ball[j].move(xSpeed[j] * dt, ySpeed[j] * dt );
Good Idea, but this doesn't solve the problem.
It's weird: sometimes, when two balls which stick together, collide with one single ball, the collusion seems to be correct.
If you have time, you can compile the code by yourself and watch it. :)
-
The balls are moved to the new direction, not to the old. To make sure that they dont collide anymore.
Ah, I must've missed the whole "newVel" stuff in the move method. Probably because you used it the same again straight afterwards :P
As for running the code, I might do soon, if I can work out which parts you're still using ???
-
Here's the Code. :)
-
It seems that the problem you were having is that you were assigning the new speeds as soon as you'd calculated them. This means that you calculate a new xSpeed and use the new one in the calculation for xSpeed[j]. It needs to be the same value for both calculations. You have to calculate all of the new velocities before assigning them - at the same time! It does show this in that tutorial :p
if (Collision(Ball[i].getPosition(), Ball[j].getPosition()))
{
const float tempXSpeedI = newVelX1(xSpeed[i], xSpeed[j]);
const float tempYSpeedI = newVelX2(xSpeed[i], xSpeed[j]);
const float tempXSpeedJ = newVelY1(ySpeed[i], ySpeed[j]);
const float tempYSpeedJ = newVelY2(ySpeed[i], ySpeed[j]);
xSpeed[i] = tempXSpeedI;
xSpeed[j] = tempYSpeedI;
ySpeed[i] = tempXSpeedJ;
ySpeed[j] = tempYSpeedJ;
Ball[i].move(xSpeed[i] * dt, ySpeed[i] * dt);
Ball[j].move(xSpeed[j] * dt, ySpeed[j] * dt);
}
It's still not perfect but they bounce now ;)
-
You still haven't fully understood the vector abstraction. You should also use it as return type of your function.
In fact, you should always use vectors to represent 2D coordinates, and fall back to float only in exceptional cases -- not the other way around.
-
His code is line for line from a tutorial so I thought it'd be better for him to actually see how the tutorial code works first so I fixed that. He should definitely start using vectors; not doing so is much more error-prone. However, if he'd have converted everything to use vectors and it didn't work, it would be harder to compare with the original tutorial to see what is wrong.
-
That's true, but maybe the tutorial isn't the best one if it fiddles around with separate floats. I don't see a reason not to introduce vectors from the beginning... They don't add complexity, they reduce it.
Sometimes it's really amazing how simple the code becomes if you use the right abstractions... Same for trigonometry, for example. Since I've written Thor.Vectors, I'm hardly ever using std::sin() and std::cos() :)
-
Your Thor library does look awesome. I'd almost certainly use it if I didn't want to work as low-level as possible to learn what's going on (I like maths etc.).
"Low as possible" means avoiding the lowest form - Windows programming. Thanks, SFML ;)
-
Thanks Golden Eagle, for solving the problem :)
I could have noticed that aswell, I should be more concentrated during programming... ::)
I made some changes with the ball position:
Ball[i].setPosition( i * 70 ,i * 70);
Now the Balls don't stick together when you start the program.
But I noticed something like a glitch:
When a ball, collides with a wall, and at the same moment another ball collides with him, the ball is being pushed into the wall, and the two balls stick together.
It's hard to explain, if you like, you can just see it by yourself.
I think this problem can't be solved, can it?
It's still not perfect
Why is it still not perfect? Now it's the same code as in the tutorial, isn't it?
-
Thanks Golden Eagle, for solving the problem :)
You're welcome.
But I noticed something like a glitch:
Why is it still not perfect? Now it's the same code as in the tutorial, isn't it?
I think you answered your own question ;)
It's not perfect because the tutorial isn't perfect. The tutorial shows how to deal with collisions with discs and which angle and velocity they should be after a collision. One of the things it doesn't cover (if I remember correctly) is when the velocity is so large that they simple pass over one another. Obviously, that's covered by testing in the tiniest of increments at a time, which can mean needing to slow down your discs. Try it with small discs that move quickly and they'll just skip over each other ;)
Also, when the discs go into the window edge, they are just slid back into the window at a tangent of the window's edge rather than calculating where the disc should be after bouncing.
-
Hmmm, and what should I do now?
I didn't know that the tutorial wasn't completely right, I'll have another look at the Gamasutra atricle, although it seems a little bit more compilcated to me.
If anyone has an idea how to solve this, please tell me! :)
-
It's not about "solving" it; it's about the level of accuracy you desire. It can't be 100% physically perfect, ever. It's just how close to that you want to go. The closer to perfect you go, the complicated it can become.
-
But there has to be a way that the balls don't glitch, using the current way to calculate the collision...
-
But there has to be a way that the balls don't glitch, using the current way to calculate the collision...
Using the same calculations, they will "glitch" the same way.
I think, instead of "glitch", you mean "noticably glitch". Generally, the more "glitchless", the more involved the process.
Have a look through that Gamasutra article that was linked earlier; it has some techniques that should "solve" your current problems :)
-
But there has to be a way that the balls don't glitch, using the current way to calculate the collision...
Using the same calculations, they will "glitch" the same way.
I think, instead of "glitch", you mean "noticably glitch". Generally, the more "glitchless", the more involved the process.
Have a look through that Gamasutra article that was linked earlier; it has some techniques that should "solve" your current problems :)
Thanks for pointing that out, I will try to understand the Article ;)
-
Now I've read the Gamasutra Article again, but I find it hard to understand the equations in the 'Calculating Bounce' Section.
So I thought it maybe would be easier to have a look at the Java Code:
// First, find the normalized vector n from the center of // circle1 to the center of circle2Vector n
= circle1.
center - circle2.
center;n.
normalize();// Find the length of the component of each of the movement// vectors along n. // a1 = v1 . n// a2 = v2 . nfloat a1
= v1.
dot(n
);float a2
= v2.
dot(n
);// Using the optimized version, // optimizedP = 2(a1 - a2)// -----------// m1 + m2float optimizedP
= (2.0 * (a1
- a2
)) / (circle1.
mass + circle2.
mass);// Calculate v1', the new movement vector of circle1// v1' = v1 - optimizedP * m2 * nVector v1
' = v1 - optimizedP * circle2.mass * n;
// Calculate v1', the
new movement vector of circle1
// v2' = v2 + optimizedP * m1 * nVector v2
' = v2 + optimizedP * circle1.mass * n;
circle1.setMovementVector(v1');circle2.
setMovementVector(v2
');
But I've got a few questions. :P
I assume that circle1.center
are just the coordinates of the first circle in a vector, aren't they?
What does .normalize()
and .dot()
do and how can I do that in C++?
And my last question:
I assume that the 'movement vector' is just the X- and Y-Velocity saved in one Vector, am I right?
Any help is appreciated! :)
-
I don't want to be mean but it really does tell you those things in the article. If you read it from start to finish, it tells you what normalising is, how to use the dot product etc.. It looks like you skipped to the last "version" of the code to try to understand it but it's not supposed to be alone; the earlier parts of the code are supposed to be involved or, at least, understood.
FYI, I went to the gamasutra article, and searched for normalize and it took me straight to the part that says what normalising a vector is :P
-
... it tells you what normalising is
Yeah, it tells me what it is, but not how to calculate it :P
Anyways, I added a function to normalize a vector.
I forgot that I already implemented a function to calculate the dot product.... ::)
But there remain two questions:
I assume that circle1.center
are just the coordinates of the first circle in a vector, aren't they?
And my last question:
I assume that the 'movement vector' is just the X- and Y-Velocity saved in one Vector, am I right?
-
You know that Thor already provides all those vector functions (normalize/unit vector, dot product, length, ...), and that I've mentioned it at least three times in this thread? :P
Just in case you don't want to reinvent the wheel in productive code. For learning purposes, it's probably not bad if you implement those functions once on your own.
-
You know that Thor already provides all those vector functions (normalize/unit vector, dot product, length, ...), and that I've mentioned it at least three times in this thread? :P
Just in case you don't want to reinvent the wheel in productive code. For learning purposes, it's probably not bad if you implement those functions once on your own.
I definetely will use thor in future projects, but since I'm still a beginner, I think I'ts better that I just write the functions by myself to learn how It works :)
-
I assume that circle1.center
are just the coordinates of the first circle in a vector, aren't they?
That will be the center of the circle :P But yeah, it'll be the position of the centre of the circle which will include all of the coordinates for that. In the case of two dimensions, it will have x and y.
And my last question:
I assume that the 'movement vector' is just the X- and Y-Velocity saved in one Vector, am I right?
Yes, movement is the amount that it has moved. As I said above, it'll be all the dimensions necessary but in 2D, it'll be x and y.
-
I assume that circle1.center
are just the coordinates of the first circle in a vector, aren't they?
That will be the center of the circle :P But yeah, it'll be the position of the centre of the circle which will include all of the coordinates for that. In the case of two dimensions, it will have x and y.
And my last question:
I assume that the 'movement vector' is just the X- and Y-Velocity saved in one Vector, am I right?
Yes, movement is the amount that it has moved. As I said above, it'll be all the dimensions necessary but in 2D, it'll be x and y.
Thank you!
Now It seems to work! :)
But there's one problem: The 'glitches' are still there! :(
What do I have to do to remove them? Look for an 'perfect' tutorial?
(I attached the source code, so I don't have to spam the whole thread)
-
The 'glitches' are still there
Which glitches? It looks fine to me.
The only thing that I can see that you may be noticing is that they still penetrate each other slightly. That is because you're calculating the radius of the filled circle, not including its outline. I believe that outlines expand outwards so you'd have to increase the radius that is tested by half of the outline width. (Not the actual radius otherwise the outline would make it even bigger!)
EDIT: I just removed the outlines and it looks awesome :p
EDIT 2: In slower situations, it glitches. It's because you are moving the discs scaled by the amount of time that has passed. There is not way of knowing how much time is going to pass. It could be five seconds! How far would they move then? You need to break time down into smaller chunks. Try reading up on timesteps. Here's an interesting read (warning: get ready to pay attention to the nitty-gritty!): Fix Your Timestep (http://gafferongames.com/game-physics/fix-your-timestep/)
You may find reading the previous article on integration (http://gafferongames.com/game-physics/integration-basics/) useful before the timestep one.
-
The only thing that I can see that you may be noticing is that they still penetrate each other slightly. That is because you're calculating the radius of the filled circle, not including its outline. I believe that outlines expand outwards so you'd have to increase the radius that is tested by half of the outline width. (Not the actual radius otherwise the outline would make it even bigger!)
EDIT: I just removed the outlines and it looks awesome :p
I know, I'll implement a feature that allows you to add and remove the outlines of the circles by pressing a button soon. :)
EDIT 2: In slower situations, it glitches. It's because you are moving the discs scaled by the amount of time that has passed. There is not way of knowing how much time is going to pass. It could be five seconds! How far would they move then? You need to break time down into smaller chunks. Try reading up on timesteps. Here's an interesting read (warning: get ready to pay attention to the nitty-gritty!): Fix Your Timestep (http://gafferongames.com/game-physics/fix-your-timestep/)
You may find reading the previous article on integration (http://gafferongames.com/game-physics/integration-basics/) useful before the timestep one.
And I thought It couldn't get more complicated anymore... ;)
-
You could also use negative outlines where it goes into the fill.
-
And I thought It couldn't get more complicated anymore... ;)
I did say that the more accurate you want it, the more involved it becomes ;)
-
You could also use negative outlines where it goes into the fill.
Good Idea! Didn't know that this is possible :)
-
You could also use negative outlines where it goes into the fill.
Good Idea! Didn't know that this is possible :)
It's all here (http://sfml-dev.org/documentation/2.1/classsf_1_1Shape.php#a5ad336ad74fc1f567fce3b7e44cf87dc). :P
I'd forgotten about this, actually. I got mixed up with line drawing which expands from the centre ;D
EDIT: fixed the link. Thanks to Giblit for pointing out the error :)
-
Your link is a bit messed up.
http://sfml-dev.org/documentation/2.1/classsf_1_1Shape.php#a5ad336ad74fc1f567fce3b7e44cf87dc
void sf::Shape::setOutlineThickness ( float thickness )
Set the thickness of the shape's outline.
Note that negative values are allowed (so that the outline expands towards the center of the shape), and using zero disables the outline. By default, the outline thickness is 0.
Parameters
thickness New outline thickness
-
Wow, 6 pages for this thread :o
OP, can you please state clearly if you problem is solved, or if there is anything else left to solve? ;)
-
OP, can you please state clearly if you problem is solved, or if there is anything else left to solve? ;)
When everything is solved, I'll post that, no worries :)
-
Hmmm, I got an Interesting solution from Geheim via PM.
The solution is a bit 'dirty' but seems to work.
I added this to my if-collusion-statement:
Ball[i].move(-xSpeed[i] * 0.001f, -ySpeed[i] * 0.001f);
Ball[j].move(-xSpeed[j] * 0.001f, -ySpeed[j] * 0.001f);
Full Source-Code as attachment :)
-
Thats like I said just a "dirty" approach to avoid the visual jumping you experience. For me your code seems to work like it should, however you should test a bit more yourself before posting again. Of course we could give you fully working source code, but thats not what we want. You should manage it with all the help you got now yourself...
-
Just noticed that it doesn't work, glitches are still occurring.
Seems like I have to go through the 'Fix your timestep' Article... :-\
-
Ok, still trying to fix my timestep, but meanwhile I added two features:
1. Outline can be (de-)activated by pressing the 'r' key
2. Circles change color when colliding with a window-border
But there's one thing I don't understand: It seems like the colors are always nearly the same. What did I do wrong? :-\
-
Ok, still trying to fix my timestep, but meanwhile I added two features:
1. Outline can be (de-)activated by pressing the 'r' key
2. Circles change color when colliding with a window-border
But there's one thing I don't understand: It seems like the colors are always nearly the same. What did I do wrong? :-\
It looks like you keep resetting the seed for the random function. Take a look at the <random> part of the stl. IT has a lot better functionality than the old random.
-
It looks like you keep resetting the seed for the random function. Take a look at the <random> part of the stl. IT has a lot better functionality than the old random.
Okay, I now I use the new random:
default_random_engine engine;
uniform_int_distribution<int> distribution(1,255);
auto random = bind ( distribution, engine );
I use this no matter which border is touched:
Ball[i].setFillColor(sf::Color(random(), random(), random()));
Now it definetely looks better, but do I need to set an extra seed for the generator?
-
You only need to seed the generator once.
PRNG use the seed as S0. Since a PRNG is just a sequence. I don't know the exact forumas but they are something like: Sn+1 = ( A * Sn + B ) % M; Where A , B , M are given before hand. So for example a generator that looks like:
Sn+1 = ( 10 * Sn + 11 ) % 12 with a seed of 0 it would look like this: ( repeats really fast since I used really small values )
11 , 1 , 9 , 5 , 1 , ...now it is repeating
Most of the PRNG won't repeat for a very long time
-
It looks like you keep resetting the seed for the random function. Take a look at the <random> part of the stl. IT has a lot better functionality than the old random.
Okay, I now I use the new random:
default_random_engine engine;
uniform_int_distribution<int> distribution(1,255);
auto random = bind ( distribution, engine );
I use this no matter which border is touched:
Ball[i].setFillColor(sf::Color(random(), random(), random()));
Now it definetely looks better, but do I need to set an extra seed for the generator?
The seed is to make the random generator behave differently on each run. If you did not set a seed, then the results would be the same each time you start the program. The balls would change colors in the same sequence every time the program is ran. That is the purpose of the seed, to prevent that.
-
The seed is to make the random generator behave differently on each run. If you did not set a seed, then the results would be the same each time you start the program. The balls would change colors in the same sequence every time the program is ran. That is the purpose of the seed, to prevent that.
Yeah, I know what a seed does, but I didn't know if the new random needs a seed too, or if, for example, the system time is automatically used as seed.
-
You should seed it.
And the system time is a terrible seed. Use std::random_device to get a good seed value.
See: http://en.cppreference.com/w/cpp/numeric/random
-
Okay, I made two different engines with two different seeds. But when I try to use an number created from random_device, it compiles fine, but when I start the program, it closes immediately.
Here's some code:
random_device seedDevice;
uniform_int_distribution<int> seedDist(0, 1000000);
default_random_engine engine(seedDist(seedDevice));
uniform_int_distribution<int> distribution(1,255);
auto random = bind ( distribution, engine );
default_random_engine engine2(seedDist(seedDevice));
auto random2 = bind (distribution, engine2);
-
And the system time is a terrible seed.
Why?
Use std::random_device to get a good seed value.
std::random_device is not guaranteed to use a hardware random source. It may fall back to a deterministic pseudo-number generator, and then you're doing even worse than with the system time.
You should really not make your life too complicated if you're just using random numbers for games. Mostly, you won't even notice the problems of LCGs (using C++11 random engines is still a good idea).
But when I try to use an number created from random_device, it compiles fine, but when I start the program, it closes immediately.
What does that mean? Find out with the debugger where the program closes. More useful than "some code" would also be a minimal complete example (http://en.sfml-dev.org/forums/index.php?topic=5559.msg36368#msg36368).
And something important: Please open new threads for new problems. This one is already far too long, and the current discussion has nothing to do with the original problem.
-
An example:
#include <random>
#include <functional>
#include <iostream>
#include <cstdlib>
int main()
{
std::random_device seedDevice;
std::default_random_engine engine(seedDevice());
std::uniform_int_distribution<int> distribution(1, 255);
auto random = std::bind(distribution, std::ref(engine));
std::cout << "Generating 10 random numbers" << std::endl;
for (int i = 0; i < 10; ++i)
std::cout << "\tRandom number " << i << ": " << random() << std::endl;
return EXIT_SUCCESS;
}
Sample output:
$ g++ -std=c++11 test.cc
$ ./a.out
Generating 10 random numbers
Random number 0: 53
Random number 1: 136
Random number 2: 85
Random number 3: 210
Random number 4: 109
Random number 5: 50
Random number 6: 201
Random number 7: 195
Random number 8: 62
Random number 9: 124
-
And the system time is a terrible seed.
Why?
For a game it is probably fine.
In general it's a way too guessable value. An attacker with knowledge of the fact that you are using the system time as a seed (regardless of resolution) has just narrowed the scope, of the numbers he has to guess in order to guess your seed, greatly.
Use std::random_device to get a good seed value.
std::random_device is not guaranteed to use a hardware random source. It may fall back to a deterministic pseudo-number generator, and then you're doing even worse than with the system time.
True, so for something other than a game I guess you'd want to mix up a few sources; including random_device, system time and more.
You should really not make your life too complicated if you're just using random numbers for games. Mostly, you won't even notice the problems of LCGs (using C++11 random engines is still a good idea).
True.
-
After all I just use the system time as seed :)
-
The seed is to make the random generator behave differently on each run. If you did not set a seed, then the results would be the same each time you start the program. The balls would change colors in the same sequence every time the program is ran. That is the purpose of the seed, to prevent that.
Yeah, I know what a seed does, but I didn't know if the new random needs a seed too, or if, for example, the system time is automatically used as seed.
The seed is just the starting point. Random number generators work off of the previous value it generated to make a number.
-
And something important: Please open new threads for new problems. This one is already far too long, and the current discussion has nothing to do with the original problem.
Okay, I understand that this thread is too long... Currently I have two problems with my project, I'll open new threads for them.
This thread can be closed.