Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (476)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (533)
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  
  affine transformation, rotate, area for collision detection..  (Read 2992 times)
0 Members and 1 Guest are viewing this topic.
Offline sakus

Senior Newbie





« Posted 2011-04-12 19:39:05 »

Hi!

I'm making a a top down car game (how original huh?) to teach myself some Java and game programming.. it's going ok so far (in fact, I've already made a lot more I ever thought I could pull off) but I'm now facing my biggest problem so far: collision detection.

I'm trying to use an Area made from the bounds of my car sprite, rotate it the same way as my sprite and move them together and then I would use the Area to check for collisions. My problem I guess is that I don't really understand affine transformations, I originally just rotated the Image holding my sprite but as I started to realize I might have to go for Areas to get the collision detection working, I switched into using an AffineTransform to rotate my car.

I got it rotating the sprite correctly into the correct coordinates by trial and error but for some reason it's not working for the Area - the Area rotates but the coordinates are no where near the car itself.

Here's the code for my car's draw method, I put the area code there as I'm already messing around with affinetransformations. If you forget about all the Area stuff, it works the way it should (i.e. it rotates the car correctly and draws it to the correct location). But the area gets drawn in funny places.. the fact that I don't understand my affine transformation code aside, I further don't understand why it works for the sprite but not for the area..

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  
   // draw onto the Graphics2D object
  public void draw(Graphics2D g) {
     
      // create a new area from the sprite
     carArea = new Area(getBounds());
     
      // transformation, get the coords
     AffineTransform atCar = new AffineTransform();
      atCar.translate((int)this.getX(), (int)this.getY());
     
      // rotate the sprite around the middle of the rear axel
     atCar.rotate(
         Math.toRadians((int)this.getRotation()),
         this.getWidth() / 2,
         this.getHeight() - (this.getHeight() / 4)
      );

      // apply the transformation to the area
     carArea.transform(atCar);
     
      // draw the readily rotated sprite      
     g.drawImage(this.sprite, atCar, null);

      // visualize the Area
     g.setColor(Color.red);
      g.draw(carArea);
     
   }


I don't understand the translate part, but without that the sprite would be in funny places as well..

my getBounds
1  
2  
3  
4  
   // get the car bounds
  public Rectangle getBounds() {
      return new Rectangle((int)getX(), (int)getY(), getWidth(), getHeight());
   }


anything else from my crappy code you need to see, ask  Cool


Offline IronclawsBt

Junior Member


Medals: 1



« Reply #1 - Posted 2011-04-12 20:09:34 »

Could you clarify what you mean by "funny places"? Is there some sort of pattern to where the area is drawn (such as always offset by x or rotated around y). Finding where the area is incorrectly being offset should help you to debug.

It's not what you know, it's what other people think you know.
Just hope you don't get quizzed on it.
Game engine design tutorials
Offline sakus

Senior Newbie





« Reply #2 - Posted 2011-04-12 20:51:40 »

the coordinates are all out of whack.. not the best quality video but take a look: http://www.youtube.com/watch?v=OsYdl_57Q4k
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline avm1979
« Reply #3 - Posted 2011-04-12 21:03:46 »

Hmm - what's the getBounds() call return?  I think maybe you're pre-translating the bounds, resulting in them getting moved twice (once in getBounds(), once by the transform).

"translate" just means "move to location". So you can think of your affine transform as doing this, in order:
1) move to location
2) rotate <some amount of radians> around anchor point (anchor point being relative to location that was moved to).

For it to work correctly on the bounds, the bounds need to be defined relative to the origin.

Offline sakus

Senior Newbie





« Reply #4 - Posted 2011-04-12 21:49:18 »

Hm yes it's something to that effect - commenting out the translate line, the area is initially drawn in the correct place (the sprite however is not) and it moves correctly for as long as I don't rotate, but rotating makes it go haywire.

So, I guess easiest/best would be to separate the sprite and area transformations and figure out the correct translation / rotation for the area so that it behaves correctly..?

Then to figure out what's going wrong with the rotation. The getbounds as far as I can tell returns a rectangle with the x and y coordinates (on the whole "play area") of the sprite's top left corner pixel, and it's width and height in pixels.
Offline avm1979
« Reply #5 - Posted 2011-04-12 22:02:56 »

I see, this is starting to make sense.

The simplest thing would be to put back the translate line and use untranslated bounds for the area, e.g:

1  
carArea = new Rectangle(this.getWidth(), this.getHeight());


What you're saying should also work, though.  I suggest reading up on transforms, though, or you'll drive yourself crazy.

Offline sakus

Senior Newbie





« Reply #6 - Posted 2011-04-12 22:03:56 »

I think I got it Smiley this seems to work:

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  
   // draw onto the Graphics2D object
  public void draw(Graphics2D g) {
     
      // create a new area from the sprite
     carArea = new Area(this.getBounds());
     
      // transformations, set the coords for the sprite ("car") AT
     AffineTransform atCar = new AffineTransform();
      AffineTransform atArea = new AffineTransform();
      atCar.translate((int)this.getX(), (int)this.getY());
     
      // rotate the sprite around the middle of the rear axel
     atCar.rotate(
         Math.toRadians((int)this.getRotation()),
         this.getWidth() / 2,
         this.getHeight() - (this.getHeight() / 4)
      );

      // rotate the area
     atArea.rotate(
         Math.toRadians((int)this.getRotation()),
         (int)this.getX() + (this.getWidth() / 2),
         (int)this.getY() + (this.getHeight() - this.getHeight() / 4)
      );
     
      // apply the transformation to the area
     carArea.transform(atArea);
     
      // draw the readily rotated sprite      
     g.drawImage(this.sprite, atCar, null);

      // visualize the Area
     g.setColor(Color.red);
      g.draw(carArea);
     
   }


Now I'll have to figure out how to implement it wisely so it's easy to use it for collision detection etc, but now that it's at least working it will be easy.

Thanks for nudging me to the correct direction
Offline sakus

Senior Newbie





« Reply #7 - Posted 2011-04-12 22:18:32 »

my very first "AI" cars re-enabled again, the area stuff does seem to work indeed Smiley http://www.youtube.com/watch?v=ElnEpBAsUAc

next up, use it for collision detection (hm I'm going to want to detect in what angle and what point the collisions happens as well), and when that's done my next bigger goal I think would be a scrolling game world. Having fun so far
Offline avm1979
« Reply #8 - Posted 2011-04-12 22:25:12 »

Cool! Looks very nice.

Btw, for a great resource on various collision computations, check this out.

Offline ra4king

JGO Kernel


Medals: 336
Projects: 2
Exp: 5 years


I'm the King!


« Reply #9 - Posted 2011-04-13 00:23:16 »

Your problem with your earlier code was that your Area was already translated! You were translating it a second time with the AffineTransform. You can still use the same AffineTransform for the car and Area if you set your area to (0,0,width,height) and transforming it with "atCar".

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline sakus

Senior Newbie





« Reply #10 - Posted 2011-04-13 04:57:56 »

I separated it from the draw method totally so that I can just call getArea() when needed (i.e. when I check for collisions)

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
   // get the area
  public Area getArea() {
      // create a new area from the sprite
     this.carArea = new Area(this.getBounds());
     
      AffineTransform atArea = new AffineTransform();

      // rotate the area
     atArea.rotate(
         Math.toRadians((int)this.getRotation()),
         (int)this.getX() + (this.getWidth() / 2),
         (int)this.getY() + (this.getHeight() - this.getHeight() / 4)
      );
     
      // apply the transformation to the area
     this.carArea.transform(atArea);

      return carArea;
   }


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
   // draw onto the Graphics2D object
  public void draw(Graphics2D g) {
           
      // transformation, set the coords
     AffineTransform atCar = new AffineTransform();
      atCar.translate((int)this.getX(), (int)this.getY());
     
      // rotate the sprite around the middle of the rear axel
     atCar.rotate(
         Math.toRadians((int)this.getRotation()),
         this.getWidth() / 2,
         this.getHeight() - (this.getHeight() / 4)
      );
     
      // draw the readily rotated sprite      
     g.drawImage(this.sprite, atCar, null);

      // visualize the Area
     if (debugging) {
         g.setColor(Color.red);
         g.draw(this.getArea());
      }
     
   }

Offline avm1979
« Reply #11 - Posted 2011-04-13 05:06:19 »

Looks good to me.

You *might* run into trouble down the line if your rendering gets out of sync with the collision area (say, you update one but not the other - an example might be if you decide that different cars have a different center, and only update the render).  Probably not worth worrying about now though.

Main thing is to understand exactly what both those methods are doing and why Smiley

Offline sakus

Senior Newbie





« Reply #12 - Posted 2011-04-13 05:25:18 »

Yeah, that crossed my mind too when I realized I now have these hard coded values in two separate places in the code - I shrugged it off with "I'll just make two more integer variables representing the rotation point for the car and these methods can then use those variables", I'll just have to remember to actually do it (takes like 2 minutes but I didn't get around to it yet)  Cool
Offline ra4king

JGO Kernel


Medals: 336
Projects: 2
Exp: 5 years


I'm the King!


« Reply #13 - Posted 2011-04-13 07:30:35 »

Fine, ignore me Sad

Offline sakus

Senior Newbie





« Reply #14 - Posted 2011-04-13 07:40:57 »

Aw no, it was kind of meant as a reply to you - I already separated the two, as I want the area "detection" (for a lack of a better word) to be separate from the drawing anyway. I guess I could create the AT elsewhere and use it in both of the methods though, would at least save me one very frequently used "new AT" call bu I don't think efficiency will play too big of a role in a game like this (although I have big plans and a vivid imagination but I'm sure my programming skills will be a huge bottleneck in what I can actually do)
Offline sakus

Senior Newbie





« Reply #15 - Posted 2011-04-13 10:05:34 »

Hrm ok so contrary to what I thought (without reading enough about it beforehand) it would appear you can't simply do "if(oneAreaObject.intersects(anotherAreaObject))" ...  Lips Sealed

back to the drawing board
Offline dishmoth
« Reply #16 - Posted 2011-04-13 12:25:31 »

Hrm ok so contrary to what I thought (without reading enough about it beforehand) it would appear you can't simply do "if(oneAreaObject.intersects(anotherAreaObject))" ...  Lips Sealed

back to the drawing board

Something like:
1  
2  
3  
4  
5  
boolean intersects(Area a1, Area a2) {
  Area result = a1.clone();
  result.intersect(a2);
  return ( !result.isEmpty() );
}


Simon

Offline sakus

Senior Newbie





« Reply #17 - Posted 2011-04-13 16:37:00 »

Right on, thanks. Seems the result of clone() needs to be cast as an Area (returned Object otherwise) but otherwise it works just like that, small modifications and it's another method for my car class:

1  
2  
3  
4  
5  
6  
7  
   // collides with another car?
  public boolean collidesWith(Car otherCar) {
      Area check = (Area)this.getArea().clone();
      Area otherArea = otherCar.getArea();
      check.intersect(otherArea);
      return(!check.isEmpty());
   }

Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #18 - Posted 2011-04-13 19:26:37 »

Urgh, Area is so slooow. I highly recommend not doing this.

Instead look up line intersection and go with that.

See my work:
OTC Software
Offline dishmoth
« Reply #19 - Posted 2011-04-13 21:06:47 »

Urgh, Area is so slooow. I highly recommend not doing this.

It makes sense to do a quick bounding circle or bounding box check(*) before intersecting Areas if you're checking for collisions between lots of entities.  But either way, if it works and it doesn't show up as significant in the profiler, why worry about it?

(*) E.g.,
1  
2  
3  
4  
5  
6  
7  
public boolean collidesWith(Car otherCar) {
  if ( distanceSquared(this.position, otherCar.position) > maxCarDiameter*maxCarDiameter ) {
    return false;
  } else {
    // do a proper check with Areas
 }
}


Simon

Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #20 - Posted 2011-04-17 20:07:20 »

Urgh, Area is so slooow. I highly recommend not doing this.

It makes sense to do a quick bounding circle or bounding box check(*) before intersecting Areas if you're checking for collisions between lots of entities.  But either way, if it works and it doesn't show up as significant in the profiler, why worry about it?

(*) E.g.,
1  
2  
3  
4  
5  
6  
7  
public boolean collidesWith(Car otherCar) {
  if ( distanceSquared(this.position, otherCar.position) > maxCarDiameter*maxCarDiameter ) {
    return false;
  } else {
    // do a proper check with Areas
 }
}


Simon

Yes, good point. :-)

See my work:
OTC Software
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.

pw (18 views)
2014-07-24 01:59:36

Riven (17 views)
2014-07-23 21:16:32

Riven (14 views)
2014-07-23 21:07:15

Riven (17 views)
2014-07-23 20:56:16

ctomni231 (45 views)
2014-07-18 06:55:21

Zero Volt (40 views)
2014-07-17 23:47:54

danieldean (32 views)
2014-07-17 23:41:23

MustardPeter (36 views)
2014-07-16 23:30:00

Cero (51 views)
2014-07-16 00:42:17

Riven (50 views)
2014-07-14 18:02:53
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
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!