Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (577)
games submitted by our members
Games in WIP (498)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  pathfinding, targetting, and collisions when the world wraps  (Read 7833 times)
0 Members and 1 Guest are viewing this topic.
Offline Wildern

Junior Member





« Posted 2008-08-12 03:53:29 »

I am currently working on a game where the world will have a fixed size say 2000x4000, but there will be no boundaries.  When you get to one edge you just wrap around to the other side.

I have visibility of objects working OK.  When you can see 0,0 and 2000,4000 at the same time, objects in all the corners draw in their proper relative positions, but I am sort of brute forcing it.  I check x,y x+2000,y x+2000,y+4000 and x,y+4000 to see if any of those locations are visible.

Do I need to do something similar for collisions?  Right now, if the bounding circle of an object crosses the world boundary, it won't register a collision with an object on the other side, and the two can penetrate and overlap.  I am concerned a bit about performance as I am treating each object as if it was in four different places for drawing and that would cause a lot of collision checks.  For objects to register a collision they would need to check if any one of their four positions collided with any one of another objects four positions.  I might be able to limit that to only doing the extra checks to when the bounding box crosses a boundary.

And that brings me to path finding.  For that, I feel I am going to need four positions for every object all the time in order to find the shortest path between two of them for the computer AI to chase and target properly.

I am hoping I am waaay of base and there is a much less computationally intensive way to solve this problem.

Any help would be appreciated.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 12


Game Engineer


« Reply #1 - Posted 2008-08-12 06:40:05 »

What you need to do is to represent your world differently. Right now you're seeing it sort of as an array - when you get to the edge you fall off. What would probably be better for your purposes would be to create a bunch of links. Have each tile have pointers to its north, south, east, and west, connections, then you can just connect the ends to each other. For pathfinding and whatnot, this will work perfectly. Then for drawing, you simply create a recursive function (or something similar), that draws all grid spaces that are an X distance of i away from the player, and a Y distance of j. In this case, the drawing becomes more processor intensive than it was before, but collision and pathfinding are very fast. You could try combining the two by storing every room in an array but also allowing them to link to their neighbors.

You can also simply turn array access into a function. Instead of saying array[m][n], you would say, getArray(m,n), and then make your function like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
public Object getArray(int i, int j)
{
     try
     {
          return array[i][j];
     }
     catch (ArrayIndexOutOfBoundsException e)
     {
          if (i >= array.length)
               i = 0;
          else if (i < 0)
               i = array.length-1;
          if (j >= array[0].length)
               j = 0;
          else if (j < 0)
               j = array[0].length-1;
          return array[i][j];
     }
     return null;
}

Here any invalid array locations are automatically "wrapped" back to the other side. This is more of a hacky solution, but it might suit your purposes.

See my work:
OTC Software
Offline Wildern

Junior Member





« Reply #2 - Posted 2008-08-12 17:15:02 »

Thanks for the reply, but it looks like I need to be a bit more clear about what I am doing.
The world isn't tile based at all, and there are no rooms.  Think asteroids where you wrap from one side of the monitor to the other.  Except in mine, the world is larger then what is displayed on the monitor.
Have a look at this partial screenshot  The red line is the world boundary.
I can move across the edges just fine.  Collisions don't work properly if the bounding circle overlaps the edge.  ie The objects don't wrap until their centers cross the edge.
All objects have an x,y location (where x and y are doubles) and an associated velocity which has both the angle/magnitude of movement and double x and y components of the movement.
At the moment, I just have the list of objects to work with.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 12


Game Engineer


« Reply #3 - Posted 2008-08-12 23:15:08 »

Okay, got it. You're right that this way things get a bit more complicated, but not too much so.

All you need to do is represent collision as a total geometry - when the ship moves over an edge, subtract the part off the screen from that edge and move it to the other side, then union the two parts. I'm not sure how you're handling collisions, but you can always use Java's Area class to handle the geometry for you.

See my work:
OTC Software
Offline Wildern

Junior Member





« Reply #4 - Posted 2008-08-13 01:52:12 »

Actually, it helps to think about it like the monitor was the entire world.  When I am crossing the edge I am on both sides at the same time (or all four if I hit the corner).
I think I can get away with doing an extra check (or three) if my bounding circle crosses an edge.  Essentially, when it is crossing the edge it is in up to four locations at the same time.  I think the easiest way to do that is going to be to modify the GameObject to support the idea of a temporary object that is only allowed to exist as long as it is overlapping the world bounds.  This temporary object would have a "parent" object that it is linked to and when the either the parent or the temporary object has a collision, both objects would have their movement vectors updated.  It is a big enough world that there shouldn't be more than two or three objects crossing the edge at once, so it shouldn't be a big performance hit.

This is what I am currently doing to test for collisions.  If I add the temporary object, I won't have to change this at all.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
   public double testCollision(GameObject b, double t, List<GameCollision> collisions) {
      GameVector movea = velocity.scale(t);
      GameVector moveb = b.velocity.scale(t);
      double deltaX = b.center.x - center.x;
      double deltaXSquared = deltaX * deltaX;
      double deltaY = b.center.y - center.y;
      double deltaYSquared = deltaY * deltaY;
      int sumRadii = radius + b.radius;
      int sumRadiiSquared = sumRadii * sumRadii;
      double distSquared = deltaXSquared + deltaYSquared;
      GameVector movec = movea.subtract(moveb);
      if (movec.getLength() < Math.sqrt(distSquared) - (double)sumRadii) {
         return t;
      }
      GameVector N = movec.normal();
      GameVector C = center.toGameVector(b.center);
      double D = N.dot(C);
      double lengthC = C.getLength();
      double F = lengthC*lengthC-D*D;
      if (F >= (double)sumRadiiSquared) {
         return t;
      }
      double T = (double)sumRadiiSquared-F;
      if (T < 0.0D) {
         return t;
      }
      double distance = D - Math.sqrt(T);
      double mag = movec.getLength();
      if (mag < distance) {
         return t;
      }
      double collision_t = (distance/mag)*t;
      if (collision_t < 0.0D || collision_t > t) {
         return t;
      } else {
         collisions.add(0, new GameCollision(this, b, collision_t));
         return collision_t;
      }
   }


The problem I was trying to avoid was with the enemies finding their optimum attack vector.  It looks like for that, the ship and the enemies will each need four locations all the time and I will need to find the shortest permutation.  4! = 24, but with symmetry I can drop that to 12.  12 distance calculations per enemy, this is probably going to be my performance bottleneck
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 12


Game Engineer


« Reply #5 - Posted 2008-08-13 03:28:33 »

Yeah, that will be a pretty big hit to performance. Why not improve your actual collision algorithm? You can put everything in "buckets" and then use a sort of quad tree algorithm to speed collision implementation. I can't see a game like that creating much of a collision bottleneck as long as you are using smart algorithms, even if you've got 4 collision tests per object. Simply start with location buckets (like a 10x10 array of lists of objects within those quadrants), then use AABB (Axis Oriented Bounding Box) collision, then use geometry collision. Because you narrow things down along the way, you don't have to do the expensive geometry collision except for very close objects.

See my work:
OTC Software
Offline Wildern

Junior Member





« Reply #6 - Posted 2008-08-13 04:47:57 »

I am not sure that the collision algorithm is going to be the issue.  I am checking every object against every other object to see if they will move to or past collision in the next time tick, but I have not figured out pixel level collision detection yet, so it is simply bounding circle.  I can try the spacial hashing, but I will have to deal with my edge problem in every hash bucket.  I will need to add objects that overlap hash edges to all the buckets they overlap.  Any recommendations on bucket size or number of buckets?  Two few, and I don't get any benefit, too many and I have a lot of overhead checking for non-empty buckets.

I think I may have just thought of a trick I can use to solve the issue of finding the shortest attack vector.  If I translate the player's ship to the center of the map and apply the same relative transformation to the enemy ships, then the distance should be accurate.

So, in the example I posted a picture for, let's assume the asteroid is an enemy  Wink
The ship was at 36, 5936 and the center of the map is at 2400, 3000 so the ship needs to move by 2364, -2936
The "enemy" was at 4653, 44 and applying the same transformation puts it at 7017, -2892.
Bringing those coordinates back into the world gets me 2217, 3108
That's a dx of 183 and a dy of 108 for a distance of 212.492.
Yay! it works.  Grin
So two transformations and two mod operations beats 12 separate distance calculations.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 12


Game Engineer


« Reply #7 - Posted 2008-08-13 10:00:35 »

Yeah, you don't really need to translate anything, though, do you? Just use the absolute value.

As for the buckets, probably a 10x10 grouping would be good. You can add the same object to 4 different buckets if you want, it won't be a big deal. Either way, you're only checking a couple buckets at the maximum so you'll ignore the extra objects.

Even so, if you're just doing radial collision than I'm not sure you even have to worry about it all. Doing the absolute worst algorithm with a bunch of wasted comparisons will probably still be plenty fast for a game of this size unless you've got hundreds or thousands of objects on the screen at a time.

See my work:
OTC Software
Offline Wildern

Junior Member





« Reply #8 - Posted 2008-08-13 14:00:44 »

Yeah, you don't really need to translate anything, though, do you? Just use the absolute value.
Not sure what you mean here.  Using original positions and absolute value I get the following:
dx = 4617
dy = 5892
which is a distance of 7485.4761 and an attack vector going in pretty much the opposite direction of what it should.

As for the buckets, probably a 10x10 grouping would be good. You can add the same object to 4 different buckets if you want, it won't be a big deal. Either way, you're only checking a couple buckets at the maximum so you'll ignore the extra objects.

Even so, if you're just doing radial collision than I'm not sure you even have to worry about it all. Doing the absolute worst algorithm with a bunch of wasted comparisons will probably still be plenty fast for a game of this size unless you've got hundreds or thousands of objects on the screen at a time.
That is what I was thinking as my object count is going to be low, probably less than 100.  But I can try it out and see if I get any benefit.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 12


Game Engineer


« Reply #9 - Posted 2008-08-13 21:24:17 »

Maybe I don't entirely understand you exact project. I just don't understand why screen wrapping changes the distance from one object to another.

See my work:
OTC Software
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Wildern

Junior Member





« Reply #10 - Posted 2008-08-13 22:11:56 »

Trying two more pictures.
In this image, the red lines are the edges of the world where they have wrapped together.  The green box is the view of that world seen in the game.


And this one is just of the world.


Can you see how wrapping impacts the distance/direction calculations now?
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 12


Game Engineer


« Reply #11 - Posted 2008-08-13 23:12:43 »

Yes! That's obvious, I just wasn't using my brains.  Tongue

Seems like your solution is a good one, then. You can also base the distance calculation upon which edge the ship is closest to, but the end result and processor required is the same.

See my work:
OTC Software
Offline timfoden

Junior Member


Projects: 2



« Reply #12 - Posted 2008-08-14 11:16:51 »


This is what I am currently doing to test for collisions.  If I add the temporary object, I won't have to change this at all.

1  
2  
3  
4  
5  
6  
7  
   public double testCollision(GameObject b, double t, List<GameCollision> collisions) {
      GameVector movea = velocity.scale(t);
      GameVector moveb = b.velocity.scale(t);
      double deltaX = b.center.x - center.x;
      double deltaXSquared = deltaX * deltaX;
      double deltaY = b.center.y - center.y;
      double deltaYSquared = deltaY * deltaY;


To avoid having to do this 4 times (or to have temporary objects) you just need to normalise the deltas.  As long as the sum of the radii of the objects is less than half the dimension length (call this L), then you can just normalise the delta values to be in the range -L/2 ... L/2.

e.g.

1  
2  
3  
4  
5  
   double deltaX = b.center.x - center.x;
   if( deltaX < -L/2 )
      deltaX += L;
   else if( deltaX > L/2 )
      deltaX -= L;


This will end up choosing the closest point of the 4 that are possible.  Then you just need do the hit test once as described.

Cheers, Tim.

Try Pipe Extreme -- can you get to the end of the pipe?
Offline Wildern

Junior Member





« Reply #13 - Posted 2008-08-14 20:31:29 »

Awesome, thanks for that one.

I had to change another spot in a similar fashion.  The actual centers of the objects were being used to make a vector. It works like a charm now.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
      GamePosition cTemp = center;
      GamePosition bTemp = b.center;
      if (cTemp.x > (worldBounds.width / 2)) {
         cTemp.x -= worldBounds.width;
      }
      if (cTemp.y > (worldBounds.height / 2)) {
         cTemp.y -= worldBounds.height;
      }
      if (bTemp.x > (worldBounds.width / 2)) {
         bTemp.x -= worldBounds.width;
      }
      if (bTemp.y > (worldBounds.height / 2)) {
         bTemp.y -= worldBounds.height;
      }
      GameVector C = cTemp.toGameVector(bTemp);
Offline timfoden

Junior Member


Projects: 2



« Reply #14 - Posted 2008-08-15 10:50:27 »

Awesome, thanks for that one.

I had to change another spot in a similar fashion.  The actual centers of the objects were being used to make a vector. It works like a charm now.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
      GamePosition cTemp = center;
      GamePosition bTemp = b.center;
      if (cTemp.x > (worldBounds.width / 2)) {
         cTemp.x -= worldBounds.width;
      }
      if (cTemp.y > (worldBounds.height / 2)) {
         cTemp.y -= worldBounds.height;
      }
      if (bTemp.x > (worldBounds.width / 2)) {
         bTemp.x -= worldBounds.width;
      }
      if (bTemp.y > (worldBounds.height / 2)) {
         bTemp.y -= worldBounds.height;
      }
      GameVector C = cTemp.toGameVector(bTemp);


I'm not convinced that the above code will actually work I'm afraid.  It's important that it's the DELTAS that are normalised.  I.e. the values AFTER the subtraction.

I think you'll find that your code above will now fail if, for example, A.x is just smaller than L/2 and B.x is just greater than L/2.  A would end up heading left to get to B, when it should be heading right.  You've just moved the problem from the boundaries of the space to the centre. Smiley

Perhaps this would be more correct:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
      GameVector C = center.toGameVector(b.center);
      if (C.x > (worldBounds.width / 2)) {
         C.x -= worldBounds.width;
      }
      if (C.x < (-worldBounds.width / 2)) {
         C.x += worldBounds.width;
      }
      if (C.y > (worldBounds.height / 2)) {
         C.y -= worldBounds.height;
      }
      if (C.y < (-worldBounds.height / 2)) {
         C.y += worldBounds.height;
      }


Cheers, Tim.

Try Pipe Extreme -- can you get to the end of the pipe?
Offline Wildern

Junior Member





« Reply #15 - Posted 2008-08-15 15:44:40 »

You are correct in that I now have issues at the center of the world.
The values for center.x and center.y will never be negative, so the additional checks you suggest will never happen.
And the direction of that vector is important as it is being used to determine if the collision will happen within the given time tick.
Maybe I need to work on copies of the objects and translate one of them to world center and apply the same transformation to the other.  I don't think actual position will impact the calculations as long as relative positions remain constant.
Offline timfoden

Junior Member


Projects: 2



« Reply #16 - Posted 2008-08-15 16:46:43 »

The values for center.x and center.y will never be negative,

True. (well, this is what I had assumed anyway Smiley )

so the additional checks you suggest will never happen.

False -- assuming that
1  
GameVector C = center.toGameVector(b.center)
is calculating the difference of the two vectors involved.

i.e.  doing the same kind of thing as the following pseudocode...

1  
2  
C.x = b.center.x - center.x;
C.y = b.center.y - center.y;


The code isn't checking center.x against -L/2 .... its checking C.x [which I assume is (b.center.x - center.x)] against -L/2.

Given two variables, A & B, whose range is [0..N], (A-B) has range [-N..N], which can definitely be negative if N > 0.


And the direction of that vector is important as it is being used to determine if the collision will happen within the given time tick.

So long as the subtractions to calculate the deltas (i.e. relative vectors -- both position and velocity) are both done the same way around, the relative signs should be correct.  And as long as the delta-velocity isn't too high (i.e. abs(deltaV.x) * t < L/2, etc.) then the collision time calc should work as expected too.

Cheers, Tim.

Try Pipe Extreme -- can you get to the end of the pipe?
Offline Wildern

Junior Member





« Reply #17 - Posted 2008-08-15 17:02:10 »

Ah, I had missed that you were working on the vector.
Solution actually got much simpler, I can just re-use the previously calculated deltas to make the vector.
1  
      GameVector C = new GameVector(deltaX,deltaY);


And now all works like a charm, edges and center.

Thanks again.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (24 views)
2014-04-15 18:08:23

BurntPizza (19 views)
2014-04-15 03:46:01

UprightPath (33 views)
2014-04-14 17:39:50

UprightPath (17 views)
2014-04-14 17:35:47

Porlus (33 views)
2014-04-14 15:48:38

tom_mai78101 (59 views)
2014-04-10 04:04:31

BurntPizza (117 views)
2014-04-08 23:06:04

tom_mai78101 (217 views)
2014-04-05 13:34:39

trollwarrior1 (184 views)
2014-04-04 12:06:45

CJLetsGame (191 views)
2014-04-01 02:16:10
List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:05:20
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!