2D Collision, how to transfer forces? - java

I've got 3 main elements at work here.
Momentum, Mass, and Momentum rotation.
Mass is worked out by:
this.mMass = (this.getHeight() + this.getWidth()) / 2.0f;
I've created a system that automatically creates polygons around transparent pngs and detects collision perfectly (with high performance) but now I'm stuck on how to apply forces when they collide.
I'm using LibsGDX but theres nothing about how to detect collisions using polygons and how to deal with them anywhere (manually without using their world)
Only needs to be basic collision forces, when I look online everything seems to be way too complicated!
What I have so far..
//Collision detection - loop though all physical objects.
for(PhysicalObject physicalObject1 : AllPhysicalObjects)
{
for(PhysicalObject physicalObject2 : AllPhysicalObjects)
{
//Check that it's no trying to collide with it-self.
if(physicalObject1 != physicalObject2 && physicalObject1.mIsCurrentlyPhysical)
{
if(Intersector.overlapConvexPolygons(physicalObject2.mPolygon, physicalObject1.mPolygon))
{
//Functions.out("Class:" + eachPhysicalObject.getClass().toString());
//Now the fucking collision detection...
//Gonna be hard, but worth it when done right.
//Get the amount of momentum from the first physical object.
float physicalObject1Momentum = physicalObject1.mMomentum - physicalObject2.mMass;
float physicalObject2Momentum = physicalObject2.mMomentum - physicalObject2.mMass;
//If the momentum is negative, reverse the direction the object is going in.
if(physicalObject1Momentum < 0.0f)
{
//Reverse the direction of object1.
physicalObject1.setMomentumRotationRotation(Functions.getRotationalMaxMinStatic(180f, physicalObject1.getMomentumRotation()));
//Decrease the momentum of object2.
}
else
{
//Slow down the physical objects 1 momentum.
physicalObject1.increaseMomentum(-physicalObject1Momentum);
physicalObject2.increaseMomentum(physicalObject1.mMaxMomentum);
physicalObject2.setMomentumRotationRotation(Functions.getRotationalMaxMinStatic(180f, physicalObject1.getMomentumRotation()));
}
//Compare both momentums.
//Compare mass.
}
}
}
}
}

Related

Libgdx: How to move objects on X axis without overlapping?

I have a rectangle array holding multiple objects, moving back and forth on X axis.
Iterator<Rectangle> iter = array.iterator();
while ( iter.hasNext() ) {
Rectangle obj = iter.next();
array.get(i).x += speed * Gdx.graphics.getDeltaTime() ;
if (obj.x + obj.width > 800 || obj.x < 0) {
speed = -speed;
}
}
When the speed gets bigger, you'll start noticing the first object in the array overlapping with the other objects and pushing them apart. How to fix that?
Basically each object has
Rectangle obj = new Rectangle();
obj.x = xpos;
obj.y = ypos;
obj.width = width;
obj.height = height;
xpos += width + 4;
And has a texture, image, a sqaure, a rectange, a triangle... And each object is generated at an X position xpos different than the other. All they do is keep moving on the X axis, from x=0 till 800 and back.
What happens is that when the first object gets to 0, it tries to increase its speed again and overlapping with other objects, and then time after time, all objects keep overlapping and get further apart from each other. I want the distance between the objects to stay constant at any speed.
From what you've commented, the questions appears to be "How can I make all these blocks move together, bouncing from one edge to another". The issue being that you're getting bouncing, but they stop acting as a group.
Firstly, if you want to treat them as a group - the simplest way is to consider them as one large bounding box containing lots of smaller (inconsequential) objects. Moving that as a single object from side to side will give you the behaviour you need.
That aside, the direct answer to your question is "you're changing the direction mid-way through iteration". So in any single tick, some objects have moved left and some have moved right - meaning they stop acting as a group.
How you organise it is up to you, but this is the basic idea you need:
// assume "speedForThisFrame" is a float defined outside this function
float speedForNextFrame = speedForThisFrame
// iterate through however you want
Iterator<Rectangle> iter = array.iterator();
while ( iter.hasNext() ) {
Rectangle obj = iter.next();
obj.x += speedForThisFrame * Gdx.graphics.getDeltaTime() ;
// if it's moved out of bounds, we will change direction NEXT fame
if (obj.x + obj.width > 800 || obj.x < 0) {
speedForNextFrame = -speedForThisFrame;
}
}
// now that all movement has finished, we update the speed
speedForThisFrame = speedForNextFrame
The key thing is everything must move by the same amount, in the same direction, every frame. Changing the speed mid-update will cause them to act independently.
Note, you will still have issues when your group is larger than the bounds - or when they go over the bounds in one frame and don't fully get back the next frame. These are separate issues though and can be asked in a separate question.
I think your problem is that, caused by variations in Gdx.graphics.getDeltaTime(), the rectangles exceed your 0/800 borders by different distances.
An example:
First step:
Rect #1 x=790
Rect #2 x=780
Speed=100, DeltaTime=0.11 => DeltaX=11
After this step, Rect#1 would be at 801, Rect#2 at 791, their distance is 10.
Next step:
DeltaTime=0.12 => DeltaX=12
After this step, Rect#1 is at 789, Rect#2 at 803, their distance is 14.
Your rectangles vary their distance because they travel different distances. A possible solution would be to really bounce at the borders. So you should not only invert the speed but also take the distance a rectangle exceeded the border and let it travel this distance in the opposite direction:
So Rect#1 at 790, moving 11 pixels rightwards, should not be at 801 in the end of the step but at 799 (moving 10 pixels to the right and one to the left).

Slow down enemy movement

I'm making a game using slick2d - and ive created a pathfinding algorithm, that will allow a 'zombie' to find the shortest path to the main hero.
public void findPrey(int characterXPosition,int characterYPosition){
Pathfinder p = new Pathfinder();
n = p.aStar(xPosition,yPosition,characterXPosition,characterYPosition);
//n is a linked list which holds node objects in the path
if(!n.isEmpty()){
xPosition = (n.get(0).x);
yPosition = (n.get(0).y);
}
}
At the moment this works, but it just moves the zombie a tile (32x32) really quickly - like way too quickly!
I tried this instead:
if(xPosition > n.get(0).x){
xPosition -= .1f * delta;
}
else if(xPosition < n.get(0).x){
xPosition += .1f * delta;
}
if(yPosition < n.get(0).y){
yPosition += .1f * delta;
}
else if(yPosition > n.get(0).y){
yPosition -= .1f * delta;
}
Which only works if the zombie goes up or left - even then its really jumpy and if its meant to go down or right it goes mental and just keeps going up out of the screen.
So anyone any idea how I can either slow down the movement of the first technique - or know why the second one isnt working?
Thanks for any help!
EDIT - ANSWER
Okay figured a solution out - was pretty basic , I think just staring at the same code for a few hours did me.
In the render method of my 'world' class where the zombies were getting rendered I just put this change in:
Iterator<Zombie> i = zombies.iterator();
while (i.hasNext()) {
Zombie z = i.next();
if (!z.isDead()) {
z.updateDelta(newDelta);
z.incrementCounter(); //put a counter variable in
if(z.getCounter() % 40 == 0){
z.findPrey((int)shiftX,(int)shiftY);
}
z.render(gc, sbg, g);
}else{
i.remove();
}
}
So i basically just added a counter variable in the Zombie class and then used modular so that it wouldnt get called as frequently.
Thanks for everyone for trying to help.
I suggest you use timers. Then on every computer game will work with the same speed and the movement of your units will be with fixed time.
Units move too fast, because every loop unit is moved. Amount fo loops should be less or decrease the distance moved by units.

Proper Collision

So basically im trying to figure out how to make a proper collision between two Rectangles.
Detecting isn't the problem, but the rectangles begin to clip. I wanted to reset the position, but how do I do that.
I'm trying to use dx and dy to reset, but it won't reset to the right coordinates.
http://i.stack.imgur.com/IU6sK.png (Sorry I can't use images yet)
System.out.println(this.y + this.h + " " + e.getY());
if(this.y + this.h >= e.getY())
{
if(this.dy > 0)
{
this.y -= delta * this.dy + 0.1;
this.dy = 0;
}
else
{
this.y += delta * this.dy;
this.dy = 0;
this.inAir = false;
}
}
This code is just an example to show how I am trying it for the top. (this = the white Rectangle and e = the orange one) I used my class Entity, which extends Rectangle.
I'm checking intersection before I call this. This is a function in the "white" Entity and the intersection is checked in the update function of the main loop.
If I use this, there is like 1px between the rectangles.
Any ideas?
Thanks for any help :)
http://docs.oracle.com/javase/6/docs/api/java/awt/Rectangle.html
Use the Rectangle class.
Here, some code
http://pastebin.com/raw.php?i=TzkST3Hm
The best way to do rectangular collision is to use the Rectangle class to detect collision using the .intersects(Rectangle) method, and then calculate a new variable called displacementX and displacementY.
displacementX = Math.abs(entitiy1.getX() - entity2.getX());
displacementY = Math.abs(entitiy1.getY() - entity2.getY());
So what we have currently is the amount of pixels entity1 is intruding on entity2 (or vice versa due to the absolute value). Then, run some comparisons and move entity1 (or entity2) by the value of the lesser displacement, which should yield perfect-looking collision.
This is at least how I do it. The correct method for rectangular collision is to:
1) Determine if they collide
2) Correct it
3) Render
Simply preventing movement after detecting collision will look horrible (especially on low frame rates).
Hope I helped!
~Izman

Collision Detection - Any Language

I'm writing a simple game right now and I need to be able to detect collisions between many objects (checking if 10 objects collide with any 20 other objects but not between the two groups). I've written some simple code that works now but causes the game to slow down extremely after I detect the collision (in Java by the way):
//Check for collisions between tanks and bullets
ArrayList<Object> objectsToRemove = new ArrayList<Object>();
for (int i = 0; i < tanksOnScreen.size(); i += 1) {
//Get tank
Tank tank = tanksOnScreen.get(i);
for (int e = 0; e < bulletsOnScreen.size(); e += 1) {
//Get bullet
Bullet bullet = bulletsOnScreen.get(e);
//Check for collision
if (tank.collides(bullet.x, bullet.y, 10, 10)) {
System.out.println("Collide");
objectsToRemove.add(bullet);
objectsToRemove.add(tank);
break;
}
}
}
for (Object obj:objectsToRemove) {
if (obj.getClass().equals(Bullet.class)) {
bulletsOnScreen.remove(bulletsOnScreen.indexOf(obj));
} else if (obj.getClass().equals(Tank.class)) {
tanksOnScreen.remove(tanksOnScreen.indexOf(obj));
}
}
And the collides() method of the tank:
public boolean collides(float px, float py, float pwidth, float pheight) {
// If the distance between the two centers of the lines on x and y axis
// Is less than the distance of half the w and h added together, the objects
// Are colliding
float x1 = (px > x) ? x : px, x2 = (px > x) ? px : x, y1 = (py > y) ? y : py, y2 = (py > y) ? py : y;
float w1 = (x1 < x2) ? width : pwidth, w2 = (x1 < x2) ? pwidth : width, h1 = (y1 < y2) ? height : pheight, h2 = (y1 < y2) ? pheight : height;
if ((x2 + w2/2) - (x1 + w1/2) < (w1/2 + w2/2)&&(y2 + h2/2) - (y1 + h1/2) < (h1/2 + h2/2)) {
return true;
} else {
return false;
}
}
I assume the lag is because of the double for loops, to iterate over the objects. I'm not sure how to eradicate these for loops or how to get the bullets in a certain region of the screen and only check these bullet without using another for loop (segregation, I think). Can anyone point me in the right direction for collision detection with many objects? I don't mind what language the answer is in, even if it is pseudo code.
Thanks,
Ben
Edit 1
I'm right now using the Slick graphics API for java, which provides you with an update and render method to add rendering and game logic. I placed this collision detection in the update method (which is getting called every frame - about 60 times a second). The slowness occurs after the collision happens, and the objects are removed from the screen - which I find odd. Maybe a break command after deleting the objects in the for loop will eradicate it?
Edit 2
Thanks for all the answers guys, and the references are a great help for the future. I fixed the problem only by swapping the bullet and tank for loops, so it didn't continue to loop after the bullet was destroyed. I shouldn't have asked the question, as it was quite easy to fix in the end. Dave and Banthar were right in saying that the problem wasn't with the code, and that it should be instantaneous.
One good idea is to use octree. As the bullets will be moving I assume that you will need a kinetic version of it - there are a bunch of articles on kinetic data structures on the internet.
Also if you want to detect collisions between more complex convex polygons, I recommend you to use gjk algorithm which is very fast for such calculation. Note that this will speed up only the collision detection time for a pair of objects, the number of pairs will still remain the same.
Unless I'm missing something, or there's additional code you haven't shown, you're running in O(n^2), where n is 20. It should seem instantaneous to the user.
Try stepping through it in the debugger to see where it's slowing down.
Edit to elaborate: your expensive operation is not in the code you've posted. It's somewhere else, being called by the code you've posted.
Thanks for all the answers guys, and the references are a great help for the future. I fixed the problem only by swapping the bullet and tank for loops, so it didn't continue to loop after the bullet was destroyed. I shouldn't have asked the question, as it was quite easy to fix in the end. Dave and Banthar were right in saying that the problem wasn't with the code, and that it should be instantaneous.

Moving objects getting stuck in a corner

I am working on a program which simulates objects moving in a field. The field has a boundary of 1024x1024. The object cannot go below 0 in terms of x,y coordinate and it cannot go above 1024. I have a method for each object called "move()" which moves the object in its current direction at its current speed. If the object approaches the boundary, it then turns around with a new direction and same speed.
The problem I am having is that when one of my objects gets close to both the x and y bound (corner of the field), it gets stuck in the corner. It is almost as if it is trying to move away from the corner, but then it turns back. It must love that corner. I looked over my code and to me, my logic seems correct. I check to make sure the new direction is not negative or over 359. I check to make sure the new x,y coordinate with the new direction is within the bounds too. I even have a method to set a new direction.
I have tried re-implementing this method with different logic, but no luck. If anyone could possibly find a flaw in my programming or point out what may be causing it, then that would be much appreciated.
I have tried to debug and step through my program and I see that when it gets to the corner, it changes direction to turn around, moves about 3 spaces, then goes back to the corner. Must be a wonderful corner.
Code for move method is below:
public void move(){
localX = super.getX();
localY = super.getY();
float newX=0, newY=0;
float testX, testY;
boolean acceptX = false, acceptY = false;
testX = (float) (Math.cos(direction)*10) + localX;
testY = (float) (Math.sin(direction)*10) + localY;
int testDirection;
while(!acceptX){
if(testX >= 0 && testX <= bound){
newX = testX;
acceptX = true;
}//end if statement
else{
if(direction+180 > 359){
setDirection(direction-180);
testX = (float) (Math.cos(Math.toRadians(direction))*speed) + localX;
}
else{
setDirection(direction+180);
testX = (float) (Math.cos(Math.toRadians(direction))*speed) + localX;
}
}//end else
}//end while that checks for X value
while(!acceptY){
if(testY >= 0 && testY <= bound){
newY = testY;
acceptY = true;
}//end if statement
else{
if(direction+180 > 359){
setDirection(direction-180);
testY = (float) (Math.sin(Math.toRadians(direction))*speed) + localY;
}
else{
setDirection(direction+180);
testY = (float) (Math.sin(Math.toRadians(direction))*speed) + localY;
}
}//end else
}//end while that checks for Y value
super.setX(newX);
super.setY(newY);
}
and here is the code for setDirection
public void setDirection(int d) {
direction = d;
}
Say you have an object in the upper left corner, going up. Your first test turns it around so it goes down. Then comes your second check, which turns it around again to go up... again.
Your code could also use some more readability. The very first thing I noticed is that you're using the >359 checks to normalize the new direction to go in. Yet all cases include the movement code as well. I would do something like:
setDirection(direction + 180); //turn around
if (direction >= 360) direction -= 360; //normalize
testY = ...; //move
to move the movement code out of the direction checking if/else blocks. 360 is also a better magic number to use; 359 degrees means nothing. As has been suggested, you should ultimately use a vector library and thus throw away most of the math.
I'd really recommend storing your direction as a vector (x, y) instead of calculating that vector from a scalar; I think that would help you immensely with your code.
Issue: When your object hits an edge, you turn it 180 degrees. If it hits both edges, it'll spin in place, and the test coordinates will always be in the wrong spot.
When one of your objects hits an edge, it needs to bounce, not About Face! Angle of incidence == angle of refraction, or some such. In other words, if you're checking the x coordinate and it bounces, negate the x velocity, not both x & y.

Categories