Awesome
jbump
jbump is a Java port for bump.lua, a 2D AABB collision detection and response library.
Please see the bump.lua README for the original
documentation.
Features
- Simple, fast, and accurate collisions in a lightweight package
- Avoids tunneling with fast AABB calculation
- User has complete control over the movement and physics of entities
- Can be used for Android, iOS (robovm), HTML5 (GWT), and desktop Java applications
- Multiple instances can run on different threads (for server side simulation)
- Entities can be repositioned and resized during the simulation without errors
Use jbump for...
- Tile based games
- Games that entities can be mostly represented by axis-aligned rectangles
- Top-Down Adventures, Shoot 'Em Ups, Tournament Fighters, and Platformers
Do not use jbump for...
- Games that require polygon collision detection
- Realistic physics simulations and multiple fast moving objects colliding against each other
- Simulations where the order in which the collisions are resolved isn't known.
Installation
You can directly download the jar file from JitPack, or you can use a project management tool like Maven or Gradle.
Using Gradle
// NOTE: this is not the "repositories" section inside "buildscript", so make sure to check where you're putting the repository!
allprojects {
repositories {
// ...
maven { url 'https://jitpack.io' }
}
}
dependencies {
// you may need to use "api" instead of "implementation" in a multi-module project, like most libGDX projects
implementation 'com.github.tommyettinger:jbump:v1.0.1' // check JitPack for other versions if you want a specific/newer one
}
Using Maven
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
...
<dependencies>
<dependency>
<groupId>com.github.tommyettinger</groupId>
<artifactId>jbump</artifactId>
<version>v1.0.1</version> <!-- check JitPack for other versions if you want a newer one -->
</dependency>
</dependencies>
HTML5(GWT) is supported by adding the sources dependency to the project:
project(":html") {
...
dependencies {
...
// you may need to use "api" instead of "implementation" in a multi-module project, like most libGDX projects
implementation 'com.github.tommyettinger:jbump:v1.0.1:sources'
}
}
If using GWT, you must also add the inherits line to the GdxDefinition.gwt.xml file of your HTML project:
<module rename-to="html">
...
<inherits name="com.dongbat.jbump" />
</module>
The latest commit version can be looked up here, on JitPack's page for jbump,
under the Commits tab. You can copy any of those 10-hex-digit identifiers for a commit and replace v1.0.1
with your
commit of choice to change version. You can also just look at the green JitPack bar at the top of this README.md .
Usage
You must create a World
instance and add Item
instances to it:
World<Entity> world = new World<Entity>();
Item<Entity> item = world.add(new Item<Entity>(entity), x, y, w, h);
for(Entity obstacle: obstacles) {
world.add(new Item<Entity>(obstacle), obstacle.x, obstacle.y, obstacle.w, obstacle.h);
}
To move an Item
through the World, call world.move()
:
world.move(item, newX, newY, CollisionFilter.defaultFilter);
The above code will simulate movement of the item through the world and generate collisions. The item will be stopped
if it collides with another item. To "teleport" an Item
to a new position without collisions:
world.update(item, newX, newY);
To also update the size of the Item
:
world.update(item, newX, newY, newWidth, newHeight);
You must also remember to remove an entity's associated Item
when you remove it from the game:
world.remove(item)
To determine what may generate collisions and how they interact with other items, write a custom CollisionFilter
:
CollisionFilter bulletCollisionFilter = new CollisionFilter() {
@Override
public Response filter(Item item, Item other) {
if (EntityUtil.isOwner(other.userData, item.userData)) {
return Response.cross;
} else {
return Response.touch;
}
}
};
...
world.move(bulletItem, newX, newY, bulletCollisionFilter);
CollisionFilter
may return Response.slide
, Response.cross
, Response.bounce
, Response.touch
, and null
.
- A value of
null
indicates thatother
will not block movement and will not trigger aCollision
. Use this for entities that do not interact with each other. - A value of
Response.touch
will stop all movement ofitem
and trigger aCollision
. Use this for entities like arrows that get stuck in the entities they hit. - A value of
Response.slide
will trigger aCollision
and stop movement in the direction thatitem
hitsother
, but will allow it to slide across its surface. This is the typical interaction you would see in a platformer game and is the defaultResponse
. - A value of
Response.bounce
will trigger aCollision
and bounceitem
back against the side that it hitsother
. This is typically used in games like Breakout where balls bounce against walls and tiles. - A value of
Response.cross
will trigger aCollision
but will not stopitem
from intersectingother
and passing through it. This is useful for penetrating bullets and area triggers that are turned on when a player passes through them.
Get collided items:
Result result = world.move(item, newX, newY, unitCollisionFilter);
Collisions projectedCollisions = result.projectedCollisions;
Array<Item> touched = new Array<Item>();
for (int i = 0; i < projectedCollisions.size(); i++) {
Collision col = projectedCollisions.get(i);
touched.add(col.other);
}
Store collisions:
Result result = world.move(item, newX, newY, unitCollisionFilter);
Collisions projectedCollisions = result.projectedCollisions;
Collisions collisions = new Collisions();
for (int i = 0; i < projectedCollisions.size(); i++) {
Collision col = projectedCollisions.get(i);
collisions.add(col);
}
Each Collision
reports various data on the contact between the items:
- item = the item being moved / checked.
- other = an item colliding with the item being moved.
- type = the result of
filter(other)
. It's usually "touch", "cross", "slide", or "bounce". - overlaps = boolean. True if item "was overlapping" other when the collision started.
False if it didn't but "tunneled" through other. - ti = float between 0 and 1. How far along the movement to the goal did the collision occur.
- move = The difference between the original coordinates and the actual ones in x and y values.
- normal = The collision normal indicating the side the item hit other; integer -1, 0 or 1 in
x
andy
Useful in detecting if the player hit the ground or is pushing against the side of a wall. - touch = The coordinates where item started touching other
- itemRect = The rectangle item occupied when the touch happened
- otherRect = The rectangle other occupied when the touch happened
Advanced Settings
World is in tileMode
by default. jbump will do additional sorting logic to avoid Item
getting stuck between tiles.
You can disable tileMode
if you are not using tiles to increase performance under certain circumstances.
Otherwise, you can fine tune the cellSize
of each cell used internally: the world is broken up into a grid, which
holds the different objects in cells to reduce the number of collision checks necessary every frame. Use the
following World constructor:
World<Entity> world = new World<Entity>(32f);
cellSize
represents the size of the sides of the squared cells that will be used internally to provide the data.
This value defaults to 64f, which is fine for most use. However, it should be set to a multiple of your tile size in
world units for tile-based games. For example, if you're using pixel units and your tiles are 32x32 pixels, cellSize
could be 32f, 64f, 128f, etc. If you're using meters and your tiles are 1x1 meters, cellSize could be 1f, 2f, 4f. Set
this value lower/higher to tweak performance. Note that you do not have to have a tile-based game to use jbump, but the
cell rules still apply to the world.
Querying the World
Instead of checking for collisions with direct bounding box to bounding box tests, you can also use World
query methods.
Arbitrarily positioned point, rectangle, segment, and ray queries are available:
- Point:
world.queryPoint(x, y, filter, items);
- Rectangle:
world.queryRect(x, y, width, height, filter, items);
- Segment:
world.querySegment(x1, y1, x2, y2, filter, items);
- Ray:
world.queryRay(originX, originY, dirX, dirY, filter, items);
items
is an empty list that will be filled with all the items that collide with the given shape. filter
is the
CollisionFilter
that defines what items would be tested. Use CollisionFilter.defaultFilter
to test all items. If
you create a custom filter, return null to not accept an item. To accept, return any kind of Response
. item
is the
item that would be tested. other
is always null in this case.
More detailed segment and ray queries are available:
- Segment with Coords:
world.querySegmentWithCoords(x1, y1, x2, y2, filter, infos);
- Ray with Coords:
world.querySegmentWithCoords(originX, originY, dirX, dirY, filter, infos);
infos
is a more detailed list of collision information. The (x1, y1) coordinates of ItemInfo
define where the
segment/ray intersects the Item
. This is helpful for drawing particle effects where a bullet enters a body, for example.
The (x2, y2) coordinates define where the segment/ray exits the item, which is great for drawing an exit wound. (ti1, ti2)
are values between 0 and 1 that define how far from the starting point the impact happened. This can be used to
describe an effect that weakens with distance.
jbump Overview Tutorial
Watch the following video on YouTube that summarizes the use of jbump in the context of a platformer game: Jbump AABB Collision Detection and Physics
Also, review these two example games to learn how jbump can be implemented in platformers and shooters: jbumpexample by raeleus
The test class demonstrates the use the query methods, among other important examples: TestBump.java