Bladeren bron

extended the collision system with collision lines, test ramp for upwards movement

Kajetan Johannes Hammerle 5 jaren geleden
bovenliggende
commit
05268d6f0d

BIN
levels/04-Like_Ice.map


BIN
resources/tiles.png


BIN
resources/tiles.xcf


+ 5 - 2
slot1.txt

@@ -2,8 +2,11 @@ level.00-Tech_Demo.map=true
 level.00-Tech_Demo.map.bottles=9
 level.00-Tech_Demo.map.time=1.5374999999999965
 level.01-Parabola.map=true
-level.01-Parabola.map.bottles=1
+level.01-Parabola.map.bottles=2
 level.01-Parabola.map.time=1.6249999999999962
 level.02-Out_of_Reach.map=true
-level.02-Out_of_Reach.map.bottles=4
+level.02-Out_of_Reach.map.bottles=8
 level.02-Out_of_Reach.map.time=9.674999999999985
+level.04-Like_Ice.map=true
+level.04-Like_Ice.map.bottles=12
+level.04-Like_Ice.map.time=20.69997

+ 14 - 11
src/me/hammerle/supersnuvi/Game.java

@@ -634,26 +634,29 @@ public class Game extends Engine
         registeredTiles.put(210, new DecoShroomTile(0.0625f, 0.21875f, 0.125f, 0.25f));
         registeredTiles.put(211, new DecoShroomTile(0.0625f, 0.1875f, 0.125f, 0.21875f));
         
+        // ramp
+        registeredTiles.put(300, new Ramp());
+        
         // fog, starting late to make length changes possible
-        /*for(int i = 0; i < 16; i++)
-        {
-            registeredTiles.put(16000 + i, new FogTile((i + 1) / 16.0));
-        }*/
+        //for(int i = 0; i < 16; i++)
+        //{
+        //    registeredTiles.put(16000 + i, new FogTile((i + 1) / 16.0));
+        //}
         
         // london stuff
         //registeredTiles.put(224, new BaseBoxTile("london_background/london_background"));
         
         // london streets
-        for(int i = 0; i < 16; i++)
-        {
+        //for(int i = 0; i < 16; i++)
+        //{
             //registeredTiles.put(240 + i, new BaseBoxTile("london_streets/london_streets" + i));
-        }
+        //}
         
         // london street light
-        for(int i = 0; i < 25; i++)
-        {
-            //registeredTiles.put(256 + i, new BaseBoxTile("street_light/street_light" + (i % 5) + "_" + (i / 5)));
-        }
+        //for(int i = 0; i < 25; i++)
+        //{
+        //    registeredTiles.put(256 + i, new BaseBoxTile("street_light/street_light" + (i % 5) + "_" + (i / 5)));
+        //}
     }
     
     public Tile getTile(int id)

+ 36 - 14
src/me/hammerle/supersnuvi/entity/Entity.java

@@ -7,14 +7,17 @@ import me.hammerle.supersnuvi.entity.components.Health;
 import me.hammerle.supersnuvi.entity.components.ItemCollector;
 import me.hammerle.supersnuvi.entity.components.Movement;
 import me.hammerle.supersnuvi.gamelogic.Level;
-import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.Utils;
 
 public final class Entity
 {
-    public static float GRAVITY = 8.0f;
-    public static float STEP = 0.0625f;
+    public static final float GRAVITY = 8.0f;
+    public static final float STEP = 0.0625f;
+    // this one is a little bit bigger to prevent wrong calculation
+    // while joing upwars
+    public static final float UP_STEP = STEP + 0.001953125f;
     
     // the last position is used for interpolation during rendering
     private float lastPosX;
@@ -24,7 +27,7 @@ public final class Entity
     private float posY;
     
     // the collision box of the entity
-    private final CollisionBox box;
+    private final CollisionObject box;
     
     // the motion before the movement collision check
     private float preMotionX;
@@ -49,7 +52,7 @@ public final class Entity
     // face
     private Face face;
     
-    protected Entity(Level level, float x, float y, CollisionBox box)
+    protected Entity(Level level, float x, float y, CollisionObject box)
     {
         lastPosX = x;
         lastPosY = y;
@@ -119,7 +122,7 @@ public final class Entity
                 e.posX + e.box.getWidth() * 0.5f, e.posY + e.box.getHeight() * 0.5f);
     }
     
-    public CollisionBox getBox()
+    public CollisionObject getBox()
     {
         return box;
     }
@@ -221,8 +224,8 @@ public final class Entity
         }
         else
         {
-            CollisionBox testBox = box.copy().expand(preMotionX, preMotionY);
-            List<CollisionBox> boxes = level.getMovementBoxesAt(testBox, this);
+            CollisionObject testBox = box.copy().expand(preMotionX, preMotionY);
+            List<CollisionObject> boxes = level.getMovementBoxesAt(testBox, this);
             if(!boxes.isEmpty())
             {
                 float mx = preMotionX;
@@ -264,12 +267,20 @@ public final class Entity
                         }
                     }
 
-                    for(CollisionBox cb : boxes)
+                    for(CollisionObject cb : boxes)
                     {
                         if(cb.isColliding(testBox))
                         {
-                            mx = 0.0f;
-                            testBox.reset();
+                            testBox.offsetY(-UP_STEP);
+                            for(CollisionObject cb2 : boxes)
+                            {
+                                if(cb2.isColliding(testBox))
+                                {
+                                    mx = 0.0f;
+                                    testBox.reset();
+                                    break;
+                                }
+                            }
                             break;
                         }
                     }
@@ -303,7 +314,7 @@ public final class Entity
                         }
                     }
 
-                    for(CollisionBox cb : boxes)
+                    for(CollisionObject cb : boxes)
                     {
                         if(cb.isColliding(testBox))
                         {
@@ -316,6 +327,16 @@ public final class Entity
                 
                 motionX = testBox.getMinX() - oldX;
                 motionY = testBox.getMinY() - oldY;
+                
+                /*if(motionX != 0.0f || motionY != 0.0f)
+                {
+                    System.out.println("____________________");
+                    System.out.println(testBox + " " + preMotionX + " " + preMotionY);
+                    System.out.println(box);
+                    boxes.forEach(b -> System.out.println(b));
+                    System.out.println(motionX + " " + motionY);
+                    System.out.println("____________________");
+                }*/
             }
             else
             {
@@ -328,12 +349,13 @@ public final class Entity
         posY += motionY;
         box.reset().offset(posX, posY);
         
-        onGround = preMotionY > 0.0f && motionY == 0;
+        //onGround = preMotionY > 0.0f && motionY == 0;
+        onGround = !level.getCollisionBoxesAt(box.copy().expand(0.0f, STEP * 2)).isEmpty();
         
         move.setInWater(false);
         move.setFrictionFactor(0.6f);
         // apply collision
-        CollisionBox cb = box.copy();
+        CollisionObject cb = box.copy();
         for(Face f : Face.values())
         {
             cb.reset();

+ 1 - 1
src/me/hammerle/supersnuvi/entity/components/DefaultMovement.java

@@ -40,7 +40,7 @@ public class DefaultMovement extends Movement
     {
         if(ent.isOnGround())
         {
-            ent.setMotionY(ent.getMotionY() - getJumpPower());
+            ent.setMotionY(-getJumpPower());
             return true;
         }
         return false;

+ 22 - 17
src/me/hammerle/supersnuvi/gamelogic/Level.java

@@ -15,7 +15,8 @@ import me.hammerle.supersnuvi.entity.EntityBuilder;
 import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.tiles.StartTile;
 import me.hammerle.supersnuvi.tiles.Tile;
-import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.CollisionLine;
+import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Utils;
 
 public final class Level 
@@ -85,11 +86,15 @@ public final class Level
             }
             
             // debug stuff
-            /*if(name.equals("00-Tech Demo"))
+            if(name.equals("00-Tech Demo"))
             {
-                int index = data.getBackgroundIndex() + 1;
-                data.setTile(index, 10, 6, 2);
-                data.setTile(index, 11, 6, 1);
+                int index = data.getBackgroundIndex();
+                for(int i = 0; i < 5; i++)
+                {
+                    data.setTile(index, 3 + i, 7 - i, 300);
+                }
+                data.setTile(index, 8, 3, 2);
+                data.setTile(index, 9, 4, 2);
                 
                 /*for(int layer = 0; layer <= data.getBackgroundIndex(); layer++)
                 {
@@ -127,8 +132,8 @@ public final class Level
                     {
                         data.setTile(index, x + 6, y + 1, 256 + x + (4 - y) * 5);
                     }
-                }
-            }*/
+                }*/
+            }
             // end debug stuff
             
             maxSouls = 0;
@@ -657,19 +662,19 @@ public final class Level
         return Game.get().getTile(i);
     }
     
-    private CollisionBox getMovementBox(int x, int y)
+    private CollisionObject getMovementBox(int x, int y)
     {
         int i = data.getInteractionTile(x, y);
         if(i == -1)
         {
-            return CollisionBox.NULL_BOX;
+            return CollisionObject.NULL_BOX;
         }
         return Game.get().getTile(i).getMovementBox(x, y).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
     }
     
-    public List<CollisionBox> getMovementBoxesAt(CollisionBox box, Entity not)
+    public List<CollisionObject> getMovementBoxesAt(CollisionObject box, Entity not)
     {
-        List<CollisionBox> boxes;
+        List<CollisionObject> boxes;
         if(not != null)
         {
             boxes = getEntitiesCollidingWith(not, box).stream().map(ent -> ent.getBox()).collect(Collectors.toList());
@@ -687,8 +692,8 @@ public final class Level
         {
             for(int y = startY; y <= endY; y++)
             {
-                CollisionBox cb = getMovementBox(x, y);
-                if(cb.isColliding(box) && cb != CollisionBox.NULL_BOX)
+                CollisionObject cb = getMovementBox(x, y);
+                if(cb.mayCollide(box) && cb != CollisionObject.NULL_BOX)
                 {
                     boxes.add(cb.copy());
                 }
@@ -697,18 +702,18 @@ public final class Level
         return boxes;
     }
     
-    private CollisionBox getCollisionBox(int x, int y)
+    private CollisionObject getCollisionBox(int x, int y)
     {
         int i = data.getInteractionTile(x, y);
         if(i == -1)
         {
-            return CollisionBox.NULL_BOX;
+            return CollisionObject.NULL_BOX;
         }
         Tile tile = Game.get().getTile(i);
         return tile.getCollisionBox(x, y).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
     }
     
-    public List<Location> getCollisionBoxesAt(CollisionBox cb)
+    public List<Location> getCollisionBoxesAt(CollisionObject cb)
     {
         LinkedList<Location> boxes = new LinkedList<>();
         int startX = Utils.toBlock(cb.getMinX());
@@ -729,7 +734,7 @@ public final class Level
         return boxes;
     }
     
-    public List<Entity> getEntitiesCollidingWith(Entity not, CollisionBox cb)
+    public List<Entity> getEntitiesCollidingWith(Entity not, CollisionObject cb)
     {
         return entities.values().stream().filter(ent -> ent != not && ent.getBox().isColliding(cb)).collect(Collectors.toList());
     }

+ 3 - 2
src/me/hammerle/supersnuvi/tiles/BottledSoulTile.java

@@ -3,6 +3,7 @@ package me.hammerle.supersnuvi.tiles;
 import java.util.HashSet;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.SoundUtils;
 
@@ -38,11 +39,11 @@ public class BottledSoulTile extends BaseTile
     }
     
     @Override
-    public CollisionBox getCollisionBox(int x, int y) 
+    public CollisionObject getCollisionBox(int x, int y) 
     {
         if(states.contains(getKey(x, y)))
         {
-            return CollisionBox.NULL_BOX;
+            return CollisionObject.NULL_BOX;
         }
         return super.getCollisionBox(x, y);
     }

+ 5 - 4
src/me/hammerle/supersnuvi/tiles/CrumblingStoneTile.java

@@ -5,6 +5,7 @@ import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.entity.EntityBuilder;
 import me.hammerle.supersnuvi.gamelogic.Level;
 import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.SoundUtils;
 import me.hammerle.supersnuvi.util.Utils;
@@ -22,21 +23,21 @@ public class CrumblingStoneTile extends BaseTile
     }
 
     @Override
-    public CollisionBox getCollisionBox(int x, int y) 
+    public CollisionObject getCollisionBox(int x, int y) 
     {
         if(states.contains(getKey(x, y)))
         {
-            return CollisionBox.NULL_BOX;
+            return CollisionObject.NULL_BOX;
         }
         return super.getCollisionBox(x, y);
     }
 
     @Override
-    public CollisionBox getMovementBox(int x, int y) 
+    public CollisionObject getMovementBox(int x, int y) 
     {
         if(states.contains(getKey(x, y)))
         {
-            return CollisionBox.NULL_BOX;
+            return CollisionObject.NULL_BOX;
         }
         return super.getMovementBox(x, y);
     }

+ 16 - 0
src/me/hammerle/supersnuvi/tiles/Ramp.java

@@ -0,0 +1,16 @@
+package me.hammerle.supersnuvi.tiles;
+
+import me.hammerle.supersnuvi.util.CollisionLine;
+
+public class Ramp extends BaseTile
+{
+    public Ramp()
+    {
+        super(0.375f, 0.0f, 0.4375f, 0.0625f);
+        
+        CollisionLine line = new CollisionLine(0.0f, Tile.SIZE, Tile.SIZE, 0.0f);
+        super.setCollisionBox(line);
+        super.setMovementBox(line);
+    }
+    
+}

+ 1 - 0
src/me/hammerle/supersnuvi/tiles/SpikeTile.java

@@ -4,6 +4,7 @@ import me.hammerle.snuviengine.api.Texture;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.gamelogic.Level;
 import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 
 public class SpikeTile extends BaseTile

+ 13 - 13
src/me/hammerle/supersnuvi/tiles/Tile.java

@@ -1,15 +1,15 @@
 package me.hammerle.supersnuvi.tiles;
 
 import me.hammerle.supersnuvi.entity.Entity;
-import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 
 public abstract class Tile 
 {
     public final static int SIZE = 32;
     
-    private CollisionBox movementCollision;
-    private CollisionBox collisionBox;
+    private CollisionObject movementCollision;
+    private CollisionObject collisionBox;
     
     public Tile()
     {
@@ -22,9 +22,9 @@ public abstract class Tile
      * @param cb a collision box
      * @return the tile which the change was applied to
      */
-    public Tile setMovementBox(CollisionBox cb)
+    public Tile setMovementBox(CollisionObject cb)
     {
-        if(cb == CollisionBox.NULL_BOX)
+        if(cb == CollisionObject.NULL_BOX)
         {
             this.movementCollision = null;
         }
@@ -40,9 +40,9 @@ public abstract class Tile
      * @param cb a collision box
      * @return the tile which the change was applied to
      */
-    public Tile setCollisionBox(CollisionBox cb)
+    public Tile setCollisionBox(CollisionObject cb)
     {
-        if(cb == CollisionBox.NULL_BOX)
+        if(cb == CollisionObject.NULL_BOX)
         {
             this.collisionBox = null;
         }
@@ -59,18 +59,18 @@ public abstract class Tile
      */
     public Tile setDefaultCollisionBox()
     {
-        this.movementCollision = CollisionBox.DEFAULT_TILE_BOX.copy();
-        this.collisionBox = CollisionBox.DEFAULT_TILE_BOX.copy();
+        this.movementCollision = CollisionObject.DEFAULT_TILE_BOX.copy();
+        this.collisionBox = CollisionObject.DEFAULT_TILE_BOX.copy();
         return this;
     }
     
-    public CollisionBox getCollisionBox(int x, int y)
+    public CollisionObject getCollisionBox(int x, int y)
     {
         if(collisionBox != null)
         {
             return collisionBox;
         }
-        return CollisionBox.NULL_BOX.reset();
+        return CollisionObject.NULL_BOX.reset();
     }
     
     public boolean shouldAiUseCollisionBox(int x, int y)
@@ -78,13 +78,13 @@ public abstract class Tile
         return true;
     }
     
-    public CollisionBox getMovementBox(int x, int y)
+    public CollisionObject getMovementBox(int x, int y)
     {
         if(movementCollision != null)
         {
             return movementCollision;
         }
-        return CollisionBox.NULL_BOX.reset();
+        return CollisionObject.NULL_BOX.reset();
     }
     
     public void onEntityCollide(Entity ent, int x, int y, Face face)

+ 2 - 1
src/me/hammerle/supersnuvi/tiles/WaterTile.java

@@ -4,6 +4,7 @@ import me.hammerle.snuviengine.api.Texture.Animation;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.gamelogic.Level;
 import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 
 public class WaterTile extends BaseTile
@@ -22,7 +23,7 @@ public class WaterTile extends BaseTile
         super(0.3125f, 0.0f, 0.375f, 0.00390625f * (level + 1));
         this.level = level;
         super.setCollisionBox(CollisionBox.createScaledTileBox(0.1f, (getOffsetY() / Tile.SIZE) + 0.1f, 0.9f, 1.0f));
-        super.setMovementBox(CollisionBox.NULL_BOX);
+        super.setMovementBox(CollisionObject.NULL_BOX);
     }
 
     @Override

+ 43 - 48
src/me/hammerle/supersnuvi/util/CollisionBox.java

@@ -2,11 +2,8 @@ package me.hammerle.supersnuvi.util;
 
 import me.hammerle.supersnuvi.tiles.Tile;
 
-public class CollisionBox 
+public class CollisionBox extends CollisionObject
 {
-    public final static CollisionBox NULL_BOX = new CollisionBox(0.0f, 0.0f, 0.0f, 0.0f);
-    public final static CollisionBox DEFAULT_TILE_BOX = createScaledTileBox(0.0f, 0.0f, 1.0f, 1.0f);
-    
     private float minX;
     private float maxX;
     private float minY;
@@ -29,16 +26,18 @@ public class CollisionBox
         cMaxY = maxY;
     }
     
-    public static CollisionBox createScaledTileBox(float x1, float y1, float x2, float y2)
+    public static CollisionObject createScaledTileBox(float x1, float y1, float x2, float y2)
     {
         return new CollisionBox(x1 * Tile.SIZE, y1 * Tile.SIZE, x2 * Tile.SIZE, y2 * Tile.SIZE);
     }
     
-    public CollisionBox copy()
+    @Override
+    public CollisionObject copy()
     {
         return new CollisionBox(cMinX, cMinY, cMaxX, cMaxY);
     }
     
+    @Override
     public void save()
     {
         minX = cMinX;
@@ -47,7 +46,8 @@ public class CollisionBox
         maxY = cMaxY;
     }
     
-    public CollisionBox reset()
+    @Override
+    public CollisionObject reset()
     {
         cMinX = minX;
         cMaxX = maxX;
@@ -56,37 +56,44 @@ public class CollisionBox
         return this;
     }
 
+    @Override
     public float getWidth() 
     {
         return maxX - minX;
     }
     
+    @Override
     public float getHeight() 
     {
         return maxY - minY;
     }
     
+    @Override
     public float getMinX() 
     {
         return cMinX;
     }
 
+    @Override
     public float getMaxX() 
     {
         return cMaxX;
     }
 
+    @Override
     public float getMinY() 
     {
         return cMinY;
     }
 
+    @Override
     public float getMaxY() 
     {
         return cMaxY;
     }
    
-    public CollisionBox expand(float x, float y)
+    @Override
+    public CollisionObject expand(float x, float y)
     {
         if(x < 0.0f)
         {
@@ -106,60 +113,42 @@ public class CollisionBox
         }
         return this;
     }
-
-    public CollisionBox grow(float x, float y)
-    {
-        x = Math.abs(x);
-        y = Math.abs(y);
-        cMinX -= x;
-        cMaxX += x;
-        cMinY -= y;
-        cMaxY += y;
-        return this;
-    }
-
-    public CollisionBox grow(float value)
-    {
-        return this.grow(value, value);
-    }
     
-    public CollisionBox offset(float x, float y)
-    {
-        cMinX += x;
-        cMaxX += x;
-        cMinY += y;
-        cMaxY += y;
-        return this;
-    }
-    
-    public CollisionBox offsetX(float x)
+    @Override
+    public CollisionObject offsetX(float x)
     {
         cMinX += x;
         cMaxX += x;
         return this;
     }
     
-    public CollisionBox offsetY(float y)
+    @Override
+    public CollisionObject offsetY(float y)
     {
         cMinY += y;
         cMaxY += y;
         return this;
     }
     
-    public boolean isColliding(float minX, float minY, float maxX, float maxY)
-    {
-        return cMaxX > minX && maxX > cMinX && cMaxY > minY && maxY > cMinY;
-    }
-    
-    public boolean isColliding(CollisionBox cb)
-    {
-        return isColliding(cb.cMinX, cb.cMinY, cb.cMaxX, cb.cMaxY);
-    }
-
-    public CollisionBox intersect(CollisionBox cb)
+    @Override
+    public boolean isColliding(CollisionObject cb)
     {
-        return new CollisionBox(Math.max(cMinX, cb.cMinX), Math.max(cMinY, cb.cMinY), 
-                Math.min(cMaxX, cb.cMaxX), Math.min(cMaxY, cb.cMaxY));
+        switch(cb.getType())
+        {
+            case BOX:
+                return cMaxX > cb.getMinX() && cb.getMaxX() > cMinX && cMaxY > cb.getMinY() && cb.getMaxY() > cMinY;
+            case LINE:
+                float x1 = cb.getMinX();
+                float y1 = cb.getMinY();
+                float x2 = cb.getMaxX();
+                float y2 = cb.getMaxY();
+                return CollisionLine.intersect(x1, y1, x2, y2, cMinX, cMinY, cMaxX, cMinY) || 
+                        CollisionLine.intersect(x1, y1, x2, y2, cMaxX, cMinY, cMaxX, cMaxY) ||
+                        CollisionLine.intersect(x1, y1, x2, y2, cMaxX, cMaxY, cMinX, cMaxY) ||
+                        CollisionLine.intersect(x1, y1, x2, y2, cMinX, cMaxY, cMinX, cMinY);
+        }
+        return false;
+        
     }
 
     @Override
@@ -176,4 +165,10 @@ public class CollisionBox
         sb.append(']');
         return sb.toString();
     } 
+
+    @Override
+    public Type getType()
+    {
+        return Type.BOX;
+    }
 }

+ 213 - 0
src/me/hammerle/supersnuvi/util/CollisionLine.java

@@ -0,0 +1,213 @@
+package me.hammerle.supersnuvi.util;
+
+public class CollisionLine extends CollisionObject
+{
+    private float x1;
+    private float y1;
+    private float x2;
+    private float y2;
+    
+    private float cx1;
+    private float cy1;
+    private float cx2;
+    private float cy2;
+    
+    public CollisionLine(float x1, float y1, float x2, float y2)
+    {
+        this.x1 = x1;
+        this.y1 = y1;
+        this.x2 = x2;
+        this.y2 = y2;
+        
+        this.cx1 = x1;
+        this.cy1 = y1;
+        this.cx2 = x2;
+        this.cy2 = y2;
+    }
+    
+    @Override
+    public Type getType()
+    {
+        return Type.LINE;
+    }
+
+    @Override
+    public CollisionObject copy()
+    {
+        return new CollisionLine(cx1, cy1, cx2, cy2);
+    }
+
+    @Override
+    public void save()
+    {
+        x1 = cx1;
+        y1 = cy1;
+        x2 = cx2;
+        y2 = cy2;
+    }
+
+    @Override
+    public CollisionObject reset()
+    {
+        cx1 = x1;
+        cy1 = y1;
+        cx2 = x2;
+        cy2 = y2;
+        return this;
+    }
+
+    @Override
+    public float getWidth()
+    {
+        return Math.abs(x1 - x2);
+    }
+
+    @Override
+    public float getHeight()
+    {
+        return Math.abs(y1 - y2);
+    }
+
+    @Override
+    public float getMinX()
+    {
+        return cx1;
+    }
+
+    @Override
+    public float getMaxX()
+    {
+        return cx2;
+    }
+
+    @Override
+    public float getMinY()
+    {
+        return cy1;
+    }
+
+    @Override
+    public float getMaxY()
+    {
+        return cy2;
+    }
+
+    @Override
+    public CollisionObject expand(float x, float y)
+    {
+        return this;
+    }
+
+    @Override
+    public CollisionObject offsetX(float x)
+    {
+        cx1 += x;
+        cx2 += x;
+        return this;
+    }
+
+    @Override
+    public CollisionObject offsetY(float y)
+    {
+        cy1 += y;
+        cy2 += y;
+        return this;
+    }
+
+    @Override
+    public boolean isColliding(CollisionObject cb)
+    {
+        switch(cb.getType())
+        {
+            case BOX:
+                return cb.isColliding(this);
+            case LINE:
+                return CollisionLine.intersect(x1, y1, x2, y2, cb.getMinX(), cb.getMinY(), cb.getMaxX(), cb.getMaxY());
+        }
+        return false;
+    }
+    
+    @Override
+    public String toString() 
+    {
+        StringBuilder sb = new StringBuilder("line[x1 = ");
+        sb.append(cx1);
+        sb.append(", y1 = ");
+        sb.append(cy1);
+        sb.append(", x2 = ");
+        sb.append(cx2);
+        sb.append(", y2 = ");
+        sb.append(cy2);
+        sb.append(']');
+        return sb.toString();
+    } 
+    
+    public static boolean intersect(float x11, float y11, float x12, float y12, float x21, float y21, float x22, float y22)
+    {
+        if(compareFloats(x11, x12))
+        {
+            if(compareFloats(x21, x22))
+            {
+                return false;
+            }
+            else
+            {
+                if(!isBetween(x11, x21, x22))
+                {
+                    return false;
+                }
+                float k = (y21 - y22) / (x21 - x22);
+                float d = y22 - k * x22;
+                float y = d + x11 * k;
+                return isBetween(y, y11, y12) && isBetween(y, y21, y22);
+            }
+        }
+        else
+        {
+            if(compareFloats(x21, x22))
+            {
+                if(!isBetween(x21, x11, x12))
+                {
+                    return false;
+                }
+                float k = (y11 - y12) / (x11 - x12);
+                float d = y12 - k * x12;
+                float y = d + x21 * k;
+                return isBetween(y, y11, y12) && isBetween(y, y21, y22);
+            }
+            else
+            {
+                float k1 = (y11 - y12) / (x11 - x12);
+                float k2 = (y21 - y22) / (x21 - x22);
+                if(compareFloats(k1, k2))
+                {
+                    return false;
+                }
+                float d1 = y12 - k1 * x12;
+                float d2 = y22 - k2 * x22;
+                
+                float x = (d1 - d2) / (k2 - k1);
+                if(!isBetween(x, x11, x12) || !isBetween(x, x21, x22))
+                {
+                    return false;
+                }
+                float y = k1 * x + d1;
+                return isBetween(y, y11, y12) && isBetween(y, y21, y22);
+            }
+        }
+    }
+    
+    private static final float ERROR = 1.0f / 512.0f;
+    
+    private static boolean isBetween(float y, float y1, float y2)
+    {
+        float min = Math.min(y1, y2) - ERROR;
+        float max = Math.max(y1, y2) + ERROR;
+        return y >= min && y <= max;
+    }
+    
+    private static boolean compareFloats(float a, float b)
+    {
+        return Math.abs(a - b) < ERROR;
+    }
+}

+ 57 - 0
src/me/hammerle/supersnuvi/util/CollisionObject.java

@@ -0,0 +1,57 @@
+package me.hammerle.supersnuvi.util;
+
+import static me.hammerle.supersnuvi.util.CollisionBox.createScaledTileBox;
+
+public abstract class CollisionObject 
+{
+    public final static CollisionObject NULL_BOX = new CollisionBox(0.0f, 0.0f, 0.0f, 0.0f);
+    public final static CollisionObject DEFAULT_TILE_BOX = createScaledTileBox(0.0f, 0.0f, 1.0f, 1.0f);
+    
+    public enum Type
+    {
+        LINE, BOX
+    }
+    
+    public abstract Type getType();
+    public abstract CollisionObject copy();
+    public abstract void save(); 
+    public abstract CollisionObject reset();
+    
+    public abstract float getWidth();
+    public abstract float getHeight();
+    
+    public abstract float getMinX();
+    public abstract float getMaxX();
+    public abstract float getMinY();
+    public abstract float getMaxY();
+   
+    public abstract CollisionObject expand(float x, float y);
+    
+    public final CollisionObject offset(float x, float y)
+    {
+        offsetX(x);
+        offsetY(y);
+        return this;
+    }
+    
+    public abstract CollisionObject offsetX(float x);
+    public abstract CollisionObject offsetY(float y);
+    
+    public abstract boolean isColliding(CollisionObject cb);
+    
+    public final boolean mayCollide(CollisionObject cb)
+    {
+        // box intersect test, even for lines, prevents failures
+        float minX1 = Math.min(cb.getMinX(), cb.getMaxX());
+        float minY1 = Math.min(cb.getMinY(), cb.getMaxY());
+        float maxX1 = Math.max(cb.getMinX(), cb.getMaxX());
+        float maxY1 = Math.max(cb.getMinY(), cb.getMaxY());
+        
+        float minX2 = Math.min(getMinX(), getMaxX());
+        float minY2 = Math.min(getMinY(), getMaxY());
+        float maxX2 = Math.max(getMinX(), getMaxX());
+        float maxY2 = Math.max(getMinY(), getMaxY());
+        
+        return maxX1 > minX2 && maxX2 > minX1 && maxY1 > minY2 && maxY2 > minY1;
+    }
+}