Skip to content

Instantly share code, notes, and snippets.

@zzox
Created July 28, 2022 02:18
Show Gist options
  • Save zzox/f233e530b7376f0f5f404af86fed9c29 to your computer and use it in GitHub Desktop.
Save zzox/f233e530b7376f0f5f404af86fed9c29 to your computer and use it in GitHub Desktop.
First pass at a physics system
package core;
import core.Types;
typedef DirFlags = {
var left:Bool;
var right:Bool;
var up:Bool;
var down:Bool;
}
function clamp (value:Float, min:Float, max:Float) {
if (value < min) {
return min;
} else if (value > max) {
return max;
}
return value;
}
class PhysicsBody {
public var parent:Sprite;
public var size:IntVec2;
public var position:Vec2;
public var lastPos:Vec2;
public var immovable:Bool = false;
public var gravityFactor:Vec2 = new Vec2(1, 1);
public var acceleration:Vec2 = new Vec2(0, 0);
public var velocity:Vec2 = new Vec2(0, 0);
public var maxVelocity:Vec2 = new Vec2(1000000, 1000000); // whats a good value for this?
public var drag:Vec2 = new Vec2(0, 0);
public var bounce:Vec2 = new Vec2(0, 0);
public var touching:DirFlags;
public function new (parent:Sprite, size:IntVec2, position:Vec2, immovable:Bool = false) {
this.parent = parent;
this.size = size.clone();
this.position = position.clone();
this.lastPos = position.clone();
resetTouchingFlags();
}
public function update (elapsed:Float) {
final pos = new Vec2(position.x, position.y);
final gravity = game.physics.gravity;
// calculate increase/decrease velocity based on gravity and acceleration
var newX = velocity.x + elapsed * (acceleration.x + (gravity.x * gravityFactor.x));
var newY = velocity.y + elapsed * (acceleration.y + (gravity.y * gravityFactor.y));
// subtract drag
if (newX > 0) {
newX = Math.max(0, newX - drag.x * elapsed);
}
if (newX < 0) {
newX = Math.min(0, newX + drag.x * elapsed);
}
if (newY > 0) {
newY = Math.max(0, newY - drag.y * elapsed);
}
if (newY < 0) {
newY = Math.min(0, newY + drag.y * elapsed);
}
// configure velocity around max velocity.
velocity.set(
clamp(newX, -maxVelocity.x, maxVelocity.x),
clamp(newY, -maxVelocity.y, maxVelocity.y)
);
// update velocity based on position
position.set(
position.x + velocity.x * elapsed,
position.y + velocity.y * elapsed
);
parent.setPosition(
position.x - parent.offset.x,
position.y - parent.offset.y
);
// reset flags here after the scene and sprites have been updated,
// hopefully after the developer has done what they need with the
// touching flags.
resetTouchingFlags();
lastPos.set(pos.x, pos.y);
// TODO: debug draw here
}
function resetTouchingFlags () {
touching = {
left: false,
right: false,
up: false,
down: false
}
}
}
class Physics {
public var gravity:IntVec2 = new IntVec2(0, 0);
var items:Array<PhysicsBody> = [];
public function add (item:PhysicsBody) {
items.push(item);
}
public function collide (
body1:PhysicsBody,
body2:PhysicsBody,
?callback: (body1:PhysicsBody, body2:PhysicsBody) -> {}
):Bool {
final doesOverlap = overlap(body1, body2);
if (doesOverlap) {
// separate
if (!body1.immovable && body2.immovable) {
checkDirectionalCollision(body1, body2);
separate(body1, body2);
}
if (body1.immovable && !body2.immovable) {
checkDirectionalCollision(body2, body1);
separate(body2, body1);
}
if (!body1.immovable && !body2.immovable) {
// separate moving
// (half each of their separations)
// later, use a mass to calculate the bounce
}
if (callback != null) {
callback(body1, body2);
}
}
return doesOverlap;
}
function overlap (body1:PhysicsBody, body2:PhysicsBody):Bool {
return body1.position.x + body1.size.x >= body2.position.x &&
body1.position.x <= body2.position.x + body2.size.x &&
body1.position.y + body1.size.y >= body2.position.y &&
body1.position.y <= body2.position.y + body2.size.y;
}
// remove fromBody from toBody
function separate (fromBody:PhysicsBody, toBody:PhysicsBody) {
if (fromBody.touching.left) {
fromBody.position.x = toBody.position.x + toBody.size.x;
fromBody.velocity.x = -fromBody.velocity.x * fromBody.bounce.x;
}
if (fromBody.touching.right) {
fromBody.position.x = toBody.position.x - fromBody.size.x;
fromBody.velocity.x = -fromBody.velocity.x * fromBody.bounce.x;
}
if (fromBody.touching.up) {
fromBody.position.y = toBody.position.y + toBody.size.y;
fromBody.velocity.y = -fromBody.velocity.y * fromBody.bounce.y;
}
if (fromBody.touching.down) {
fromBody.position.y = toBody.position.y - fromBody.size.y;
fromBody.velocity.y = -fromBody.velocity.y * fromBody.bounce.y;
}
}
// checks the collision directions and then
// https://gamedev.stackexchange.com/questions/13774/how-do-i-detect-the-direction-of-2d-rectangular-object-collisions
function checkDirectionalCollision (fromBody:PhysicsBody, intoBody:PhysicsBody) {
if (fromBody.lastPos.x >= intoBody.position.x + intoBody.size.x &&
fromBody.position.x < intoBody.position.x + intoBody.size.x) {
fromBody.touching.left = true;
}
if (fromBody.lastPos.x + fromBody.size.x <= intoBody.position.x &&
fromBody.position.x + fromBody.size.x > intoBody.position.x) {
fromBody.touching.right = true;
}
if (fromBody.lastPos.y >= intoBody.position.y + intoBody.size.y &&
fromBody.position.y < intoBody.position.y + intoBody.size.y) {
fromBody.touching.up = true;
}
if (fromBody.lastPos.y + fromBody.size.y <= intoBody.position.y &&
fromBody.position.y + fromBody.size.y > intoBody.position.y) {
fromBody.touching.down = true;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment