Fizz is a 2D axis-aligned physics library for the Lua programming language. Fizz is useful in environments where external option like Box2D are unavailable.
The source code is available on GitHub and the official documentation is on 2dengine.com
fizz = require("fizzx")
fizz.setGravity(0, 100)
ball = fizz.addDynamic('circle', 300, 100, 10)
wall = fizz.addStatic('rect', 300, 400, 200, 10)
ball.bounce = 0.5
function love.update(dt)
fizz.update(dt)
end
function love.draw()
love.graphics.circle("fill", ball.x, ball.y, ball.r)
love.graphics.rectangle("fill", wall.x - wall.hw, wall.y - wall.hh, wall.hw*2, wall.hh*2)
end
Fizz supports three different shape types: circles, rectangles and line segments. Note that rectangles are represented by a center point and half-width and half-height extents. Rectangles are always axis-aligned and cannot have rotation. Line segments are useful for making slopes and possibly "one-sided" platforms. The direction of line segments affects how they handle collisions. Line segments "push" other intersecting shapes at 90 degrees counter-clockwise of the segment slope.
In addition, there are three classes of shapes: static, kinematic and dynamic. Static shapes are immobile and do not respond to collisions or gravity. Static shapes can be used to represent walls and platforms in your game. Kinematic shapes do not respond to collisions or gravity, but can be moved by manually changing their velocity. Kinematic shapes can be used to simulate moving platforms and doors. Dynamic shapes respond to collisions and gravity. Dynamic shapes can be used to simulate the moving objects in your game.
Shapes have the following properties:
Center position of circle and rectangle shapes. Starting point for line segments.
Ending point for line segments.
Radius for circle shapes.
Half-width and half-height extents for rectangle shapes.
Velocity of a dynamic or kinematic shape.
Accumulated displacement vector of the shape.
Mass and inverse mass of the shape.
Friction value applied when the edges of two shapes are touching. As this value increases, the shape will slow down when sliding on top of other shapes. Must be between 0 and 1.
Bounce or restitution value of the shape. As this value increases, the shape will bounce more elastically when colliding with other shapes. Must be between 0 and 1.
Linear damping of the shape. The damping value slows the velocity of moving shapes over time. Must be between 0 and infinity.
Gravity scale of the shape which is multiplied by the global gravity value. Set this to 0 and your shape will not be affected by gravity. When negative, the shape will levitate.
Collision callback providing the two colliding shapes, the collision normal and the penetration depth. If this callback returns false the collision will be ignored. Shapes should not be moved or destroyed during this callback.
Torque and rotation are not supported. This has several implications, most notably with circle shapes. Circle shapes are axis-aligned, so they never "spin" or "roll". Axis-aligned circles with friction behave like the wheels of a car while the breaks are on. If you want to simulate torque, I would suggest a more sophisticated library, like Box2D. For simple platform games, you could always draw or animate your objects as if they are rotating.
Since the library uses non-continuous collision detection, it's possible for shapes that move at a high velocity to "tunnel" or pass through other shapes. This problem can be accommodated in a few ways. First, make sure fizz.maxVelocity is defined within a reasonable range. Second, set up your game loop to update the simulation with a constant time step. This will make sure that the game will play the same on any machine. Here is an example of how to update the simulation with a constant time step:
accum = 0
step = 1/60
function update(dt)
accum = accum + dt
while accum >= step do
fizz.update(step)
accum = accum - step
end
end
Fizz uses flat grid partitioning to reduce the number of collision tests. You can adjust the default grid cell size if the performance is not up to par.
Manually changing the position of shapes affects the partitioning system. After modifying these values, you have to repartition the particular shape, for example:
shape.x = 100
shape.y = 100
fizz.repartition(shape)
An alternative approach is to use the built-in functions:
fizz.positionShape(100, 100)
Note that changing the size of shapes affects the partition system as well:
circle.r = circle.r + 5
fizz.repartition(circle)
If you explicitly disable partitioning, then you can ignore this limitation.
Piling two or more dynamic shapes results in jittery behavior, because there is no simple way to figure out which collision pair should be resolved first. This may cause one shape to overlap another in the stack even after their collisions have been resolved.
This issue occurs with rows of static rectangle shapes. When a dynamic shape is sliding on top of the row it may get "blocked" by the ledge between rects. The best solution is to use line segments to represent platforms.
This library is based and builds upon the original work of Taehl
Please support our work so we can release more free software in the future.