hexr-shit-physics
v1.2.0
Published
A Terrible Arcade Physics Subsystem
Downloads
2
Maintainers
Readme
# Hexr Shit Physics
What is it?
A rather crude, hacked together 2D arcade "physics" system designed to work with objects generated by the Hexr engine, which uses Three.JS. Objects can be sprites, or arbitrary rectangles, or collision regions automatically derived from tile maps.
Works like this:
- Every object involved in collision detection gets a bounding box, a precise SAT polygon and an actually 3D cube mesh. There are helper
- Each object needs adding with
.addObject()
. Every frame you call.test()
and this returns a list of all the possible collisions in the entire world - Pass this list of collisions to every entity interested in collision, and use helper functions to narrow down the list to ones involving the entity itself
Once you have this list you can:
- test to see if points in space collide with specific types of objects.
- let the SAT collision detection automatically resolve collisions, if you dare.
You can also:
- check to see if A can see B (line of sight checks)
Why you shouldn't even think about using this
It's just a hacked together mess of thrown together things with an ill-concieved API. It's adapted from code written during game jams. It's not pretty.
API.
This module is pretty tightly coupled with the hexr engine and the data generated by the hexr editor. It's incredibly opinionated about what sort of data it expects going on and unyielding in its opinion of what should be output. Sorry about that.
That being said, at least for the sake of my own sanity, I'm going to document the API as it currently stands.
Please note:
var shitPhysics = require('hexr-shit-physics');
Adding/Updating bodies
shitPhysics.addShitPhysicsBody (object
)
Adds a body to a sprite, which has the following data structure:
{
meta : {
position : [x, y],
size : [x, y],
collisionVerts : [
[tl.x, tl.y],
[bl.x, bl.y],
[br.x, br.y],
[tr.x, tr.y]
]
},
instanceId : 'unique-identifyier-for-me',
prototypeId : 'what-type-of-thing-am-id'
}
... where collisionVerts
are relative to the position
of the sprite. The position is fixed to the center of the sprite. size is basically scale. By default
Hexr sprites have a size of 1 THREE.JS unit x 1 THREE.JS unit. The size is the size relative to other sprites.
It can add a body to a /box instance/. A box instance is basically just a rectangular object, typically used for making invisible collision triggers etc.
{
meta : {
position : [x, y]
verticies : [
[tl.x, tl.y],
[bl.x, bl.y],
[br.x, br.y],
[tr.x, tr.y]
]
},
instanceId : 'etc',
prototypeId : 'etc'
}
In this case, the position
array is meant to be the top left, not the centre. The verticies for the box are in the same order and remain relative to the position.
In both cases it adds the following structure to the object:
{
meta : { ... },
body : {
collider : SAT.Polygon,
AABB : {
min[bottomLeftX, bottomLeftY],
max[topRightX, topRightY]
},
rayMesh : THREE.Mesh // box geometry with actual depth so raycasting intersects correctly
}
}
... and adds this body to the broad phase collision detection
shitPhysics.updatePhysicsBody(object
)
An object with a body previously generated with addShitPhysicsBody()
can have the raycast mesh, the bounding box and the SAT polygon updated by this helper. For this to work it expects that the object will have a mesh
property pointing at a THREE.Mesh object. The position of this mesh is what will be used to update the rest of the body.
You pretty much need to call this function for any object whenever it moves. Eventually it will be possible to pick up changes to rotation and scale but this is out of scope for now. Honestly just switching to Box2D will be a lot easier.
shitPhysics.removeBodyFromWorld(object
)
An object that has previously been added to the world with addShitPhysicsBody()
or addObject()
can be removed from the world (in effect, it is removed from the broad phase collision detection system and therefore will never show up on a list of possible collisions). The body data remains, along with the SAT polygon.
TODO: Should also remove the rayMesh from the list of possible intersections...
shitPhysics.addObject(object
)
You can add an object directly to the broad phase collision detection system. As a minimum such an object must be:
{
body : {
AABB : {
min [bottomLeftX, bottomLeftY],
max [topRightX, topRightY]
},
static : true || false
prototypeId : 'the-type-of-thing',
instanceId : 'unique-identifier'
}
The broad phase will happily detect possible collisions on objects without prototypeId or instanceId but the helper functions which narrow down the list of possible collisions will not work without them.
shitPhysics.reset()
Empties the broadphase grid and drops the list of raycast meshes. Useful when you switch level in game and you need to start again from scratch.
shitPhysics.makeBodyFromHexrTilemapCollisionBox (object
)
Adds a body to "collision boxes" generated by the Hexr level parser. This takes a tilemap and attempts to merge the tiles into the minimum number of collision boxes. They're now just one of many different types of bounding boxes that exist inside this software right now.
{
tl : {
x : 0,
y : 0
},
br : {
x : 1,
y : 1
}
}
It adds the following structure to the object:
{
meta : { ... },
body : {
collider : SAT.Polygon,
AABB : {
min[bottomLeftX, bottomLeftY],
max[topRightX, topRightY]
},
rayMesh : THREE.Mesh // box geometry with actual depth so raycasting intersects correctly
}
}
Working with collision pairs
shitPhysics.test()
Returns an array of all possible collisions at this moment. Note that static object->static object collisions will never appear here. You should pass this list of collisions to all entities interested in collision detection.
var possibleCollisionsArray = shitPHysics.test()
shitPhysics.findAandB(possibleCollisionsArray
, object
, prototypeId
)
Okay, so this is a helper function which filters down a full list of collisions down to just collisions that involve object
and objects with a matching prototypeId
. For example, all objects added via makeBodyFromHexrTilemapCollisionBox()
are automatically assigned a prototypeId of 'static-block'.
object
must be the same object that you used addShitPhysicsBody
or addObject
on.
Typical use-case would be
// want to find just the collisions between the player and the environment:
var list = shitPhysics.findAandB( worldCollisions, player, 'static-block');
// right, now I want to just find collisions with chickens..
var list = shitPhysics.findAandB( worldCollisions, player, 'chicken');
shitPhysics.findAnotB(possibleCollisionsArray
, object
, prototypeId
)
Same as findAandB
except now you want to exclude collisions involving prototypeId
. The idea is that you probably want one list of collisions with the environment and other list of collisions with other entities/objects in the world.
// all collisions involving the player EXCEPT for 'static-block'
var entityCollisions = shitPhysics.findAnotB( worldCollisions, player, 'static-block')
shitPhysics.isPointInBox (collisionsArray
, x
, y
)
Pass this a list of possible collisions and a co-ordinate and this will tell you if the point is inside any of the boxes. Typical usecase for this is figuring out if a player is standing on the ground or touching a wall etc.
shitPhysics.getFinalPosition(collisionsArray
, object.body
, targetX, targetY)
The SAT stuff has a naive collisions solver which, if you like, you can let loose on your objects after you've applied whatever movement you need to. This gives mostly okay results but it only does one pass so it's not brilliant. Returns a vector with a suggested position.
shitPhysics.canAseeB(objectA
, objectB
, farDistance
)
This abstracts THREE's Raycaster and uses the non-rendered box geometry meshes to test line of sight and returns true or false.
License
MIT
Contributing
Not really expected but obviously this is desperately bad stuff that needs all the help it can get.