Files
Leenkx_Templates/platformer/Sources/lnx/ThirdPersonController.hx
2026-05-06 19:12:34 -07:00

154 lines
3.6 KiB
Haxe

package lnx;
import iron.math.Vec4;
import iron.math.Quat;
import iron.math.Mat4;
import iron.system.Input;
import iron.object.Object;
import iron.object.BoneAnimation;
import iron.system.Time;
import iron.system.Audio;
import leenkx.trait.physics.PhysicsWorld;
import leenkx.trait.internal.CameraController;
class ThirdPersonController extends CameraController {
#if (!lnx_physics)
public function new() { super(); }
#else
static inline var rotationSpeed = 1.0;
var stepTime = 0.0;
var soundStep0:kha.Sound = null;
var soundStep1:kha.Sound = null;
var soundsLoaded = 0;
var xVec = Vec4.xAxis();
var zVec = Vec4.zAxis();
var nextFrameRot = 0.0;
var armature:Object;
var anim:BoneAnimation;
var speed = 1.0;
var dir = new Vec4();
var state = "idle";
var jumping = false;
public function new() {
super();
iron.data.Data.getSound("step0.wav", function(sound:kha.Sound) { soundStep0 = sound; soundsLoaded++; });
iron.data.Data.getSound("step1.wav", function(sound:kha.Sound) { soundStep1 = sound; soundsLoaded++; });
notifyOnInit(function() {
PhysicsWorld.active.notifyOnPreUpdate(preUpdate);
notifyOnUpdate(update);
notifyOnRemove(function() {
PhysicsWorld.active.removePreUpdate(preUpdate);
});
armature = object.getChild("Armature");
anim = findAnimation(armature);
});
}
function findAnimation(o:Object):BoneAnimation {
if (o.animation != null) return cast o.animation;
for (c in o.children) {
var co = findAnimation(c);
if (co != null) return co;
}
return null;
}
function preUpdate() {
if (Input.occupied || !body.ready) return;
var mouse = Input.getMouse();
var kb = Input.getKeyboard();
if (mouse.started() && !mouse.locked) mouse.lock();
else if (kb.started("escape") && mouse.locked) mouse.unlock();
if (nextFrameRot != 0.0) {
var origin = object.getChild("CameraOrigin");
origin.transform.rotate(xVec, nextFrameRot);
origin.transform.buildMatrix();
}
nextFrameRot = 0;
if (mouse.moved) {
nextFrameRot = -mouse.movementY / 250 * rotationSpeed;
transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed);
}
body.syncTransform();
}
function update() {
if (!body.ready) return;
var kb = iron.system.Input.getKeyboard();
// Move
dir.set(0, 0, 0);
if (moveForward) dir.add(transform.look());
if (moveBackward) dir.add(transform.look().mult(-1));
if (moveLeft) dir.add(transform.right().mult(-1));
if (moveRight) dir.add(transform.right());
// Push down
var btvec = body.getLinearVelocity();
body.setLinearVelocity(0.0, 0.0, btvec.z - 1.0);
if (moveForward || moveBackward || moveLeft || moveRight) {
var action = moveForward ? "run" :
moveBackward ? "back" :
moveLeft ? "left" : "right";
setState(action);
if (kb.down("shift")) speed = 1.6;
else speed = 1.0;
dir.mult(speed * 5);
body.activate();
body.setLinearVelocity(dir.x, dir.y, btvec.z - 1.0);
stepTime += Time.delta;
if (stepTime > 0.38 / speed) {
stepTime = 0;
if (soundsLoaded == 2) {
Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1);
}
}
}
// Play correct state
else {
setState("idle", 1.0);
}
if (jump && !jumping) {
jumping = true;
state = "jump";
body.applyImpulse(new Vec4(0, 0, 20));
anim.time = 0;
anim.frameIndex = 0;
// anim.play(state, function() { jumping = false; }, 0.0, 1.2);
iron.system.Tween.timer(0.5, () -> jumping = false);
}
// Keep vertical
body.setAngularFactor(0, 0, 0);
camera.buildMatrix();
}
function setState(s:String, speed = 1.0, blend = 0.2) {
if (s == state || jumping) return;
state = s;
anim.play(s, null, blend, speed);
}
#end
}