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

186 lines
4.5 KiB
Haxe

package lnx;
import iron.math.Vec4;
import iron.math.Vec2;
import iron.math.RayCaster;
import iron.Scene;
import iron.object.Object;
import iron.object.BoneAnimation;
import iron.system.Time;
import iron.system.Audio;
import iron.system.Input;
import leenkx.trait.physics.RigidBody;
class PlayerController extends iron.Trait {
#if (!lnx_physics)
public function new() { super(); }
#else
var soundStep0:kha.Sound = null;
var soundStep1:kha.Sound = null;
var soundsLoaded = 0;
var mouse:Mouse = null;
var keyboard:Keyboard = null;
var gamepad:Gamepad = null;
var body:RigidBody;
var anim:BoneAnimation;
var armature:Object;
var stepTime = 0.0;
var turnTime = 0.0;
var dir = new Vec4();
var lastLook:Vec4;
var state = "idle";
public function new() {
super();
// Load sounds
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(init);
notifyOnUpdate(update);
}
function init() {
// Get input devices
mouse = Input.getMouse();
keyboard = Input.getKeyboard();
gamepad = Input.getGamepad(0);
// Store references
body = object.getTrait(RigidBody);
armature = object.getChild("Armature");
anim = cast armature.children[0].animation;
lastLook = armature.transform.look().normalize();
}
function update() {
// Movement
dir.set(0, 0, 0);
if (keyboard.down("w")) dir.y = 1.0;
if (keyboard.down("s")) dir.y = -1.0;
if (keyboard.down("a")) dir.x = -1.0;
if (keyboard.down("d")) dir.x = 1.0;
if (gamepad != null && Math.abs(gamepad.leftStick.x) > 0.1) dir.x = gamepad.leftStick.x;
if (gamepad != null && Math.abs(gamepad.leftStick.y) > 0.1) dir.y = gamepad.leftStick.y;
dir.normalize();
// Mouse control
var mouse_pos = new Vec2(mouse.x,mouse.y);
var hit_pos = project_mouse_pos(mouse_pos);
if (hit_pos != null)
{
var center = new Vec4(hit_pos.x,hit_pos.y,0);
var eye = armature.transform.world.getLoc();
eye.set(eye.x,eye.y,0);
var target = center.sub(eye);
armature.transform.rot.fromTo(Vec4.yAxis(), target.normalize());
}
// Gamepad control
if (gamepad != null) {
if (Math.abs(gamepad.rightStick.x) > 0.7 || Math.abs(gamepad.rightStick.y) > 0.7) {
armature.transform.rot.fromTo(Vec4.yAxis(), new Vec4(gamepad.rightStick.x, gamepad.rightStick.y, 0.0));
}
}
armature.transform.buildMatrix();
updateAnim();
updateBody();
}
function project_mouse_pos(input:Vec2){
var camera = Scene.active.camera;
var start = new Vec4();
var end = new Vec4();
var hit_pos = RayCaster.planeIntersect(Vec4.zAxis(),new Vec4(0,0,1),input.x,input.y,camera);
return hit_pos;
}
function getAngle(va:Vec4, vb:Vec4) {
var vn = Vec4.zAxis();
var dot = va.dot(vb);
var det = va.x * vb.y * vn.z +
vb.x * vn.y * va.z +
vn.x * va.y * vb.z -
va.z * vb.y * vn.x -
vb.z * vn.y * va.x -
vn.z * va.y * vb.x;
return Math.atan2(det, dot);
}
function updateAnim() {
var look = armature.transform.look().normalize();
// Move
if (dir.length() > 0) {
var action = "";
// Angle from look direction to move direction
// 0 to PI * 2
var angle = getAngle(dir, look) + Math.PI;
var step = Math.PI / 4;
if (angle < step) action = "back";
else if (angle < step * 3) action = "left";
else if (angle < step * 5) action = "run";
else if (angle < step * 7) action = "right";
else action = "back";
setState(action, 1.0);
// Step sounds
stepTime += Time.delta;
if (stepTime > 0.3) {
stepTime = 0;
if (soundsLoaded == 2) {
Audio.play(Std.random(2) == 0 ? soundStep0 : soundStep1);
}
}
}
// Shoot
else if (mouse.down("left") || (gamepad != null && gamepad.down("r2") > 0.0)) {
setState("fire", 2.0);
}
// Idle
else {
var angle = getAngle(look, lastLook);
if (Math.abs(angle) > 0.01) {
setState("turn", angle > 0 ? 1 : -1, 0);
turnTime = 0;
}
else if (turnTime > 0.25){
setState("idle", 2.0);
}
else turnTime += Time.delta;
}
lastLook.setFrom(look);
}
function updateBody() {
if (!body.ready) return;
body.syncTransform();
body.activate();
var linvel = body.getLinearVelocity();
body.setLinearVelocity(dir.x * 6, dir.y * 6, linvel.z - 1.0); // Push down
body.setAngularFactor(0, 0, 0); // Keep vertical
}
function setState(s:String, speed:Float, blend = 0.2) {
if (s == state) return;
state = s;
anim.play(s, null, blend, speed);
}
#end
}