Update
BIN
first_person/Assets/aim.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
first_person/Assets/checker_rough.png
Normal file
|
After Width: | Height: | Size: 787 B |
BIN
first_person/Assets/decal1.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
first_person/Assets/decal2.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
first_person/Assets/finger.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
first_person/Assets/grid.png
Normal file
|
After Width: | Height: | Size: 414 B |
BIN
first_person/Assets/grid_b.png
Normal file
|
After Width: | Height: | Size: 672 B |
1
first_person/Bundled/canvas/MyCanvas.files
Normal file
@ -0,0 +1 @@
|
||||
../../Assets/aim.png
|
||||
1
first_person/Bundled/canvas/MyCanvas.json
Normal file
@ -0,0 +1 @@
|
||||
{"name":"untitled","x":0,"y":0,"width":1920,"height":1080,"elements":[{"id":0,"type":1,"name":"Image","event":"","x":0,"y":0,"width":60,"height":60,"rotation":0,"text":"Image","asset":"aim.png","progress_at":0,"progress_total":100,"strength":1,"alignment":0,"anchor":4,"parent":null,"children":[],"visible":true}],"assets":[{"name":"aim.png","file":"../../Assets/aim.png","id":0}],"theme":"Default Light"}
|
||||
BIN
first_person/Bundled/fire0.wav
Normal file
BIN
first_person/Bundled/fire1.wav
Normal file
BIN
first_person/Bundled/step0.wav
Normal file
BIN
first_person/Bundled/step1.wav
Normal file
277
first_person/Sources/lnx/FirstPersonController.hx
Normal file
@ -0,0 +1,277 @@
|
||||
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 FirstPersonController extends CameraController {
|
||||
|
||||
#if (!lnx_physics)
|
||||
public function new() { super(); }
|
||||
#else
|
||||
|
||||
static inline var rotationSpeed = 1.0;
|
||||
|
||||
var stepTime = 0.0;
|
||||
var turnTime = 0.0;
|
||||
|
||||
var soundStep0:kha.Sound = null;
|
||||
var soundStep1:kha.Sound = null;
|
||||
var soundsLoaded = 0;
|
||||
|
||||
var xVec = Vec4.xAxis();
|
||||
var zVec = Vec4.zAxis();
|
||||
var angle = 0.0;
|
||||
var nextFrameRot = 0.0;
|
||||
var armature:Object;
|
||||
var anim:BoneAnimation;
|
||||
var q = new Quat();
|
||||
var mat = Mat4.identity();
|
||||
|
||||
var nextIdle = false;
|
||||
var jumpTime = 0.0;
|
||||
var firingTime = 0.0;
|
||||
var speed = 1.0;
|
||||
var dir = new Vec4();
|
||||
var lastLook:Vec4;
|
||||
var state = "idle";
|
||||
|
||||
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);
|
||||
anim.notifyOnUpdate(updateBones);
|
||||
lastLook = armature.transform.look().normalize();
|
||||
});
|
||||
}
|
||||
|
||||
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 updateBones() {
|
||||
|
||||
// Fetch bone
|
||||
var bone1 = anim.getBone("mixamorig:LeftForeLnx");
|
||||
var bone2 = anim.getBone("mixamorig:RightForeLnx");
|
||||
|
||||
// Fetch bone matrix - this is in local bone space for now
|
||||
var m1 = anim.getBoneMat(bone1);
|
||||
var m2 = anim.getBoneMat(bone2);
|
||||
var m1b = anim.getBoneMatBlend(bone1);
|
||||
var m2b = anim.getBoneMatBlend(bone2);
|
||||
var a1 = anim.getAbsMat(bone1.parent);
|
||||
var a2 = anim.getAbsMat(bone2.parent);
|
||||
|
||||
// Rotate hand bones to aim with gun
|
||||
// Some raw math follows..
|
||||
var tx = m1._30;
|
||||
var ty = m1._31;
|
||||
var tz = m1._32;
|
||||
m1._30 = 0;
|
||||
m1._31 = 0;
|
||||
m1._32 = 0;
|
||||
mat.getInverse(a1);
|
||||
q.fromAxisAngle(mat.right(), -angle);
|
||||
m1.applyQuat(q);
|
||||
m1._30 = tx;
|
||||
m1._31 = ty;
|
||||
m1._32 = tz;
|
||||
|
||||
var tx = m2._30;
|
||||
var ty = m2._31;
|
||||
var tz = m2._32;
|
||||
m2._30 = 0;
|
||||
m2._31 = 0;
|
||||
m2._32 = 0;
|
||||
mat.getInverse(a2);
|
||||
var v = mat.right();
|
||||
v.mult(-1);
|
||||
// Todo: Do inverse kinematics here, right hand moves unnaturally
|
||||
q.fromAxisAngle(v, -1.6);
|
||||
// q.fromAxisAngle(v, angle);
|
||||
m2.applyQuat(q);
|
||||
m2._30 = tx;
|
||||
m2._31 = ty;
|
||||
m2._32 = tz;
|
||||
|
||||
// Animation blending is in progress, we need to rotate those bones too
|
||||
if (m1b != null && m2b != null) {
|
||||
var tx = m1b._30;
|
||||
var ty = m1b._31;
|
||||
var tz = m1b._32;
|
||||
m1b._30 = 0;
|
||||
m1b._31 = 0;
|
||||
m1b._32 = 0;
|
||||
mat.getInverse(a1);
|
||||
q.fromAxisAngle(mat.right(), -angle);
|
||||
m1b.applyQuat(q);
|
||||
m1b._30 = tx;
|
||||
m1b._31 = ty;
|
||||
m1b._32 = tz;
|
||||
|
||||
var tx = m2b._30;
|
||||
var ty = m2b._31;
|
||||
var tz = m2b._32;
|
||||
m2b._30 = 0;
|
||||
m2b._31 = 0;
|
||||
m2b._32 = 0;
|
||||
mat.getInverse(a2);
|
||||
var v = mat.right();
|
||||
v.mult(-1);
|
||||
q.fromAxisAngle(v, -1.6);
|
||||
// q.fromAxisAngle(v, angle);
|
||||
m2b.applyQuat(q);
|
||||
m2b._30 = tx;
|
||||
m2b._31 = ty;
|
||||
m2b._32 = tz;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
var d = mouse.movementY / 250;
|
||||
if (angle + d > 1.5) return;
|
||||
if (angle + d < -0.25) return;
|
||||
angle += d;
|
||||
nextFrameRot = -mouse.movementY / 250 * rotationSpeed;
|
||||
}
|
||||
|
||||
if (mouse.moved) transform.rotate(zVec, -mouse.movementX / 250 * rotationSpeed);
|
||||
body.syncTransform();
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (!body.ready) return;
|
||||
var look = armature.transform.look().normalize();
|
||||
|
||||
if (jump) {
|
||||
if (jumpTime >= 0.4){
|
||||
body.applyImpulse(new Vec4(0, 0, 16));
|
||||
jumpTime = 0;
|
||||
jump = false;
|
||||
}
|
||||
}
|
||||
jumpTime += Time.delta;
|
||||
|
||||
// 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);
|
||||
|
||||
var kb = iron.system.Input.getKeyboard();
|
||||
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 if (state != "fire" || state != "idle" || state != "turn") {
|
||||
var mouse = iron.system.Input.getMouse();
|
||||
if (mouse.down("left")) {
|
||||
firingTime = 0.0;
|
||||
setState("fire", 2.0);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == "fire") firingTime += Time.delta;
|
||||
else firingTime = 0.0;
|
||||
|
||||
// Keep vertical
|
||||
body.setAngularFactor(0, 0, 0);
|
||||
camera.buildMatrix();
|
||||
|
||||
lastLook.setFrom(look);
|
||||
}
|
||||
|
||||
// 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 setState(s:String, speed = 1.0, blend = 0.2) {
|
||||
if (s == state) return;
|
||||
state = s;
|
||||
anim.play(s, null, blend, speed);
|
||||
}
|
||||
#end
|
||||
}
|
||||
71
first_person/Sources/lnx/GunController.hx
Normal file
@ -0,0 +1,71 @@
|
||||
package lnx;
|
||||
|
||||
import iron.Trait;
|
||||
import iron.system.Input;
|
||||
import iron.object.Object;
|
||||
import iron.object.Transform;
|
||||
import iron.system.Audio;
|
||||
import iron.system.Time;
|
||||
import leenkx.trait.physics.RigidBody;
|
||||
|
||||
@:keep
|
||||
class GunController extends Trait {
|
||||
|
||||
#if (!lnx_physics)
|
||||
public function new() { super(); }
|
||||
#else
|
||||
|
||||
@prop
|
||||
public var fireFreq = 0.2;
|
||||
|
||||
var firePoint:Transform;
|
||||
var fireStrength = 25;
|
||||
var lastFire = 0.0;
|
||||
|
||||
var soundFire0:kha.Sound = null;
|
||||
var soundFire1:kha.Sound = null;
|
||||
var soundsLoaded = 0;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
iron.data.Data.getSound("fire0.wav", function(sound:kha.Sound) { soundFire0 = sound; soundsLoaded++; });
|
||||
iron.data.Data.getSound("fire1.wav", function(sound:kha.Sound) { soundFire1 = sound; soundsLoaded++; });
|
||||
|
||||
notifyOnInit(function() {
|
||||
firePoint = object.getChild("ProjectileSpawn").transform;
|
||||
});
|
||||
|
||||
notifyOnUpdate(function() {
|
||||
var mouse = Input.getMouse();
|
||||
lastFire += Time.delta;
|
||||
if ((mouse.down("left") && lastFire > fireFreq) || mouse.started("left")) {
|
||||
shoot();
|
||||
if (soundsLoaded == 2) {
|
||||
Audio.play(Std.random(3) == 0 ? soundFire1 : soundFire0);
|
||||
}
|
||||
lastFire = 0.0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function shoot() {
|
||||
// Spawn projectile
|
||||
iron.Scene.active.spawnObject("Projectile", null, function(o:Object) {
|
||||
o.transform.loc.x = firePoint.worldx();
|
||||
o.transform.loc.y = firePoint.worldy();
|
||||
o.transform.loc.z = firePoint.worldz();
|
||||
o.transform.buildMatrix();
|
||||
// Apply force
|
||||
var rb:RigidBody = o.getTrait(RigidBody);
|
||||
rb.syncTransform();
|
||||
var look = object.transform.look().normalize();
|
||||
rb.setLinearVelocity(look.x * fireStrength, look.y * fireStrength, look.z * fireStrength);
|
||||
// Remove projectile after a period of time
|
||||
kha.Scheduler.addTimeTask(function() {
|
||||
o.remove();
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
#end
|
||||
}
|
||||