Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (578)
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  
  Rotation Based Collision Detection  (Read 1389 times)
0 Members and 1 Guest are viewing this topic.
Offline v0rtex

Senior Newbie





« Posted 2012-04-24 20:15:17 »

Hey all, I am using Slick2D atm just to explore it and so far, its pretty awesome. After a couple of tutorials... I am making a 2D TopDown Zombie Shooter and whilst atm, all is okay. I really dislike how I have done collision Detection. I create a 2D Boolean array and set it to true in the following way:
1) Grab the Tiled (.tmx) map properties and check the blocked property of the tile. If its a blocked, tile. Return true else false

After this, I then do my collision detection between the player and the map, my player can rotate 360 degrees and move forward. Here is where I am having odd glitches, my player collides with the tiles correctly however if the user tries to rotate and keep moving forward, eventually the player will move through the tile.
A simple example:
o = tile, x = player

 o
 x //player moves upwards and collides with the tile

player then continues to rotate, upon 180 degree rotation the player glitches and moves through the block.

Any ideas on how to fix my collision detection? I am rather stuck with this.
Here is my code:
SimpleGame.java

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  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.tiled.TiledMap;

public class SimpleGame extends BasicGame {
   Image land = null;
   Image player = null;

   float playerX = 400f;
   float playerY = 300f;
   float scale = 1.0f;
   private TiledMap map;
   private Camera cam;
   private String blockedStr = "free";

   int currentBullet = 0;
   /**
    * The collision map indicating which tiles block movement - generated based
    * on tile properties
    */

   private boolean[][] blocked;
   private static final int SIZE = 32;

   public SimpleGame() {
      super("Window");
   }

   // overriden methods are the 3 methods that we will add code later to, they
  // make up our game loop
  @Override
   public void init(GameContainer gc) throws SlickException {

      map = new TiledMap("res/jungle.tmx");
      player = new Image("res/player.png"); // Note that this is Slick2D's
                                   // Image class not AWT's Image
                                   // class.

      cam = new Camera(gc, map);
      // build a collision map based on tile properties in the TileD map
     blocked = new boolean[map.getWidth()][map.getHeight()];

      for (int xAxis = 0; xAxis < map.getWidth(); xAxis++) {
         for (int yAxis = 0; yAxis < map.getHeight(); yAxis++) {
            int tileID = map.getTileId(xAxis, yAxis, 0);
            String value = map.getTileProperty(tileID, "blocked", "false");
            if (value.equals("true")) {
               blocked[xAxis][yAxis] = true;
            }
         }
      }
   }

   @Override
   public void update(GameContainer container, int delta)
         throws SlickException {
      boolean collision = false;
      Input input = container.getInput();
      float rotation = player.getRotation();
      float speed = 0.1f * delta;
      float newX = 0;
      float newY = 0;

      newX += speed * Math.sin(Math.toRadians(rotation));

      newY -= speed * Math.cos(Math.toRadians(rotation));

      if (input.isKeyDown(Input.KEY_UP)) {
         // Check all directions
        if (!(isBlocked(playerX, playerY - newY)
               || isBlocked(playerX + SIZE - 1, playerY - newY) //1 is a buffer space
              || isBlocked(playerX, playerY + SIZE + newY)
               || isBlocked(playerX + SIZE - 1, playerY + SIZE + newY)
               || isBlocked(playerX - newX, playerY)
               || isBlocked(playerX - newX, playerY + SIZE - 1)
               || isBlocked(playerX + SIZE + newX, playerY) || isBlocked(
                  playerX + SIZE + newX, playerY + SIZE - 1))) {

            // The lower the delta the slowest the sprite will animate.

            blockedStr = "Free";

            playerX += newX;
            playerY += newY;

         } else {
            collision = true;
         
         }
      }
if (collision == false) {
      if (input.isKeyDown(Input.KEY_LEFT)) {

         player.rotate(-0.2f * delta);

      }
      if (input.isKeyDown(Input.KEY_RIGHT)) {

         player.rotate(0.2f * delta);
      }
}
if (collision == true) {
   //reverse the player
  newX = 0;
   newX -= speed * Math.sin(Math.toRadians(rotation));

   newY = 0;
   newY += speed * Math.cos(Math.toRadians(rotation));
   playerY += newY;
   playerX += newX;  
}
   }

   private boolean isBlocked(float x, float y) {
      int xBlock = (int) x / SIZE;
      int yBlock = (int) y / SIZE;
      return blocked[xBlock][yBlock];
   }

   @Override
   public void render(GameContainer gc, Graphics g) throws SlickException {

      // after calculating the positions of all entities
     cam.centerOn(playerX, playerY);

      // in the render()-method
     cam.drawMap();
      cam.translateGraphics();

      // no depth in slick, images are rendered as the code executes so if we
     // render the player before the land, it will be underneath (no
     // transparency)
     player.draw(playerX, playerY);
      g.drawString("blocked:" + blockedStr, playerX - 10, playerY - 10);
   }

   public static void main(String[] args) throws SlickException {
      AppGameContainer app = new AppGameContainer(new SimpleGame());

      app.setDisplayMode(800, 600, false);
      app.start();

   }

}


Camera.java

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  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.tiled.TiledMap;
//Example Camera Class
public class Camera {

   protected TiledMap map;
   protected int numTilesX;
   protected int numTilesY;
   protected int mapHeight;
   protected int mapWidth;
   protected int tileWidth;
   protected int tileHeight;
   protected GameContainer gc;
   protected float cameraX;
   protected float cameraY;
   
   public Camera(GameContainer gc, TiledMap map) {
      this.map = map;
     
      this.numTilesX = map.getWidth();
      this.numTilesY = map.getHeight();
     
      this.tileWidth = map.getTileWidth();
      this.tileHeight = map.getTileHeight();
     
      this.mapHeight = this.numTilesX * this.tileWidth;
      this.mapWidth = this.numTilesY * this.tileHeight;
     
      this.gc = gc;
   }
   
   public void centerOn(float x, float y) {
      //try to set the given position as center of the camera by default
     cameraX = x - gc.getWidth()  / 2;
      cameraY = y - gc.getHeight() / 2;
     
      //if the camera is at the right or left edge lock it to prevent a black bar
     if(cameraX < 0) cameraX = 0;
      if(cameraX + gc.getWidth() > mapWidth) cameraX = mapWidth - gc.getWidth();
     
      //if the camera is at the top or bottom edge lock it to prevent a black bar
     if(cameraY < 0) cameraY = 0;
      if(cameraY + gc.getHeight() > mapHeight) cameraY = mapHeight - gc.getHeight();
   }

   public void centerOn(float x, float y, float height, float width) {
      this.centerOn(x + width / 2, y + height / 2);
   }


   public void centerOn(Shape shape) {
      this.centerOn(shape.getCenterX(), shape.getCenterY());
   }
   
 
   public void drawMap() {
      this.drawMap(0, 0);
   }
   
 
   
   public void drawMap(int offsetX, int offsetY) {
       //calculate the offset to the next tile (needed by TiledMap.render())
      int tileOffsetX = (int) - (cameraX % tileWidth);
       int tileOffsetY = (int) - (cameraY % tileHeight);
       
       //calculate the index of the leftmost tile that is being displayed
      int tileIndexX = (int) (cameraX / tileWidth);
       int tileIndexY = (int) (cameraY / tileHeight);
       
       //finally draw the section of the map on the screen
      map.render(  
             tileOffsetX + offsetX,
             tileOffsetY + offsetY,
             tileIndexX,  
             tileIndexY,
                (gc.getWidth()  - tileOffsetX) / tileWidth  + 1,
                (gc.getHeight() - tileOffsetY) / tileHeight + 1);
   }
   
   /**
    * Translates the Graphics-context to the coordinates of the map - now everything
    * can be drawn with it's NATURAL coordinates.
    */

   public void translateGraphics() {
      gc.getGraphics().translate(-cameraX, -cameraY);
   }
   /**
    * Reverses the Graphics-translation of Camera.translatesGraphics().
    * Call this before drawing HUD-elements or the like
    */

   public void untranslateGraphics() {
      gc.getGraphics().translate(cameraX, cameraY);
   }
   
}


Any help is appreciated,
v0rtex
Offline Antiharpist

Senior Newbie


Medals: 2



« Reply #1 - Posted 2012-04-25 05:46:43 »

You could always rotate the 4 corners of each tile in the opposite rotation of the player and see if the rotated points intersect with the non rotated player rectangle, then also check if any of the corners of the rotated player rectangle intersect with the non-rotated tile rectangle. It's expensive, and not very clever, but I did something like that when messing with rotational based collisions and it worked for me.

I've never used Slick, but would assume it has a rectangle/polygon object you can probably use to wrap up your coordinates for the collision checks.

If not, here's an inelegant one I wrote when I was playing around with doing rotated collisions in a non-clever way.
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  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
        
    //Wrap coords in rectangles
   CollRect tile_rect = new CollRect(tile_x, tile_y, tile_width, tile_height);
    CollRect player_rect = new CollRect(player_x, player_y, player_width, player_height);

     //Check for rotation
    player_rect.setDegrees(player_rotation_degrees);
     boolean collision = player_rect.dumbRotateIntersection(tile_rect);


   public class CollRect {
   public double x, y;
   public double x2, y2;
   public double width;
   public double height;
   public double mid_x;
   public double mid_y;
   public double degrees=0;

   
   public CollPoint2D[] rect_points;
   private CollRect() {}
   
   public CollRect(double x, double y, double width, double height) {
      this.width = width;
      this.height = height;
      this.x = x;
      this.y = y;
      x2 = x+width-1;
      y2 = y+height-1;
      mid_x = x+(width/2);
      mid_y = y+(height/2);
     
      //Make the rect_points
     rect_points = new CollPoint2D[4];
      resetPoints();

   }
   
   public void resetPoints() {
      rect_points[0] = new CollPoint2D(x,y);
      rect_points[1] = new CollPoint2D(x2,y);
      rect_points[2] = new CollPoint2D(x2,y2);
      rect_points[3] = new CollPoint2D(x,y2);
   }
   
   public void setDegrees(double degrees) {
      this.degrees = degrees;
   }
   
   
   public boolean dumbRotateIntersection(CollRect cr2) {
      if (rotatedIntersect(cr2,this)) {
         return true;
      }
      if (rotatedIntersect(this,cr2)) {
         return true;
      }

      return false;
   }
   
   
   private static boolean rotatedIntersect(CollRect cr1, CollRect cr2) {
                //Reset points so there is no rotation
     cr1.resetPoints();
      cr2.resetPoints();

                //keep rectangle 1 unrotated, but rotate rectangle 2
     cr2.rotatePoints(cr2.degrees);

                //Since rectangle 1 is unrotated, we need to adjust rectangle to by the opposite of the rotation that didn't happen
     cr2.rotatePoints(-cr1.degrees,cr1.mid_x,cr1.mid_y);

      //Check if any of the points in cr2 intersect cr1
     double cx = cr1.rect_points[0].x;
      double cy = cr1.rect_points[0].y;
      double cx2 = cr1.rect_points[2].x;
      double cy2 = cr1.rect_points[2].y;

     
      for (int i=0;i<4;i++) {
         CollPoint2D p = cr2.rect_points[i];
         if (p.x>=cx&&p.x<=cx2&&
            p.y>=cy&&p.y <=cy2) {
            return true;
         }
      }
      return false;
   }
   
   
   
   public void rotatePoints(double angle) {
      rotatePoints(angle,mid_x,mid_y);
   }
   
   
   public void rotatePoints(double angle, double mid_x, double mid_y) {
   
      double radians = Math.toRadians(angle);
        double sin = Math.sin(radians);
        double cos = Math.cos(radians);
        double new_x;
        double new_y;
       
        for (int i=0;i<4;i++) {
            new_x = (rect_points[i].x-mid_x)*cos-(rect_points[i].y-mid_y)*sin+mid_x;
            new_y = (rect_points[i].x-mid_x)*sin+(rect_points[i].y-mid_y)*cos+mid_y;
            rect_points[i].x = (int) Math.round(new_x);
            rect_points[i].y = (int) Math.round(new_y);
        }
   }
   
   public boolean intersects(CollRect cr2) {
      CollRect cr = this;
      if (((cr.x>=cr2.x&&cr.x<=cr2.x2)||(cr2.x>=cr.x&&cr2.x<=cr.x2))&&
      ((cr.y>=cr2.y&&cr.y<=cr2.y2)||(cr2.y>=cr.y&&cr2.y<=cr.y2))) {
         return true;
      }
      return false;
   }
   
   
   public CollRect intersection(CollRect cr) {
      CollRect ir = new CollRect();
      if (!intersects(cr)) {return ir;}
     
      //Always use the greatest left edge
     if (cr.x>=x) {
         ir.x = cr.x;
      } else {
         ir.x = x;
      }
     
      //Always use the least right edge
     if (cr.x2<=x2) {
         ir.x2 = cr.x2;
      } else {
         ir.x2 = x2;
      }
     
      //Always use the greatest top edge
     if (cr.y>=y) {
         ir.y = cr.y;
      } else {
         ir.y = y;
      }
     
      //Always use the least bottom edge
     if (cr.y2<=y2) {
         ir.y2 = cr.y2;
      } else {
         ir.y2 = y2;
      }
     
      //Set the size
     ir.width = ir.x2-ir.x;
      ir.height = ir.y2-ir.y;
     
      return ir;
   }
   
}

public class CollPoint2D {
   public double x;
   public double y;
     
   public CollPoint2D(double x, double y) {
      this.x=x;
      this.y=y;
   }
   
   public double magnitude() {
      return Math.sqrt((x*x)+(y*y));
   }
   
   public void normalize() {
      double mag = magnitude();
      x = x / mag;
      y = y / mag;
   }
   
   public double dotProduct(CollPoint2D c2) {
      return ((x*c2.x)+(y*c2.y));
   }
   
}


No, no, it's greater than keen; it's cugat.
Offline v0rtex

Senior Newbie





« Reply #2 - Posted 2012-04-25 07:07:10 »

Thanks antiharpist. I am a little confused by your code, do you have a demo application for it? Unsure of what you mean by rotating the map tile's :/
Any help is appreciated,
v0rtex
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 (27 views)
2014-04-15 18:08:23

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

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

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

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

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

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

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

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

CJLetsGame (193 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!