Created
July 28, 2022 02:18
-
-
Save zzox/f233e530b7376f0f5f404af86fed9c29 to your computer and use it in GitHub Desktop.
First pass at a physics system
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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