X-Git-Url: https://git.yukkurigames.com/?p=featherfall2.git;a=blobdiff_plain;f=src%2Fmain.js;h=ecff045199e696e668da38e7b431df441ce53f0a;hp=d252c7441884e39f2b28026aaf8a0184f8fd3119;hb=5b22796116262ad2de3b7f6cd0fb635796362bbc;hpb=d669e1ceaeacca9b7342d62c7caf998fb545aaa7 diff --git a/src/main.js b/src/main.js index d252c74..ecff045 100644 --- a/src/main.js +++ b/src/main.js @@ -1,20 +1,96 @@ "use strict"; +var b2Vec2 = yf.argcd( + function (a) { + return b2Vec2.call(this, a[0] || a.x || 0.0, a[1] || a.y || 0.0); + }, + Box2D.b2Vec2 +); + + +yf.ipairs(function (k, v) { Box2D[k] = Box2D[v]; }, { + DYNAMIC_BODY: 'b2_dynamicBody' +}); + +function camelizeCase (s) { + return s.replace(/_([a-z])/g, function (m) { + return m[1].toUpperCase(); + }); +} + +function wrapEmscriptenType (T) { + var P = T.prototype; + var keys = Object.keys(P); + var members = keys.filter(function (k) { return k.startsWith("get_"); }); + var accessors = keys.filter(function (k) { return k.startsWith("Get"); }); + members.forEach(function (k) { + var name = camelizeCase(k.slice(4)); + if (!name || name in P) return; + Object.defineProperty(P, name, { + get: P[k], set: P['s' + k.slice(1)] + }); + + }); + accessors.forEach(function (k) { + var name = k.slice(3); + name = name[0].toLowerCase() + name.slice(1); + if (!name || name in P) return; + Object.defineProperty(P, name, { + get: P[k], set: P['S' + k.slice(1)] + }); + + }); +} + +yf.each(wrapEmscriptenType, [ + Box2D.b2Vec2, Box2D.b2BodyDef, Box2D.b2Body, + Box2D.b2JointDef, Box2D.b2RevoluteJointDef, + Box2D.b2Joint, Box2D.b2DistanceJoint, Box2D.b2RevoluteJoint, +]); + +yT.defineProperties(Box2D.b2Vec2.prototype, { + 0: { alias: 'x' }, + 1: { alias: 'y' }, +}); + + var storage; -var PlayerController = new yT(yuu.C, { - constructor: function (body, left, right) { +var BodyC = yT(yuu.C, { + SLOTS: ['transform'], + + constructor: function (body, size) { + this.body = body; + this._matrix = mat4.create(); + }, + + position: { alias: 'body.position' }, + linearVelocity: { alias: 'body.linearVelocity' }, + ApplyForce: { proxy: 'body.ApplyForce' }, + + matrix: { get: function () { + var mat = this._matrix; + mat4.identity(mat); + var pos = this.body.position; + mat4.translate(mat, mat, [pos.x, pos.y, 0]); + mat4.rotateZ(mat, mat, this.body.angle); + return mat; + } } +}); + +var PlayerController = yT(yuu.C, { + constructor: function (body, left, right, leftJoint, rightJoint) { this.body = body; this.left = left; this.right = right; + this.leftJoint = leftJoint; + this.rightJoint = rightJoint; this.dleftLeft = this.dleftRight = this.drightLeft = this.drightRight = 0; this.up = 0; this.free = 0; this.leftPivot = 0; this.rightPivot = 0; - this.x = this.lastX = body.x; - this.y = this.lastY = body.y; this.commands = { dleftLeft: yuu.propcmd(this, 'dleftLeft'), dleftRight: yuu.propcmd(this, 'dleftRight'), @@ -34,105 +110,132 @@ var PlayerController = new yT(yuu.C, { }, _updateTransforms: function () { - this.left.yaw = -this.leftPivot * Math.PI / 2; - this.right.yaw = this.rightPivot * Math.PI / 2; - this.body.x = this.x; - this.body.y = this.y; + var gain = 1.0; + var leftTarget = this.leftPivot * Math.PI / 2; + var rightTarget = -this.rightPivot * Math.PI / 2; + var leftError = this.leftJoint.jointAngle - leftTarget; + var rightError = this.rightJoint.jointAngle - rightTarget; + this.leftJoint.motorSpeed = -gain * leftError; + this.rightJoint.motorSpeed = -gain * rightError; }, tick: function () { this._updatePivots(); - var GRAVITY = -0.0004; - var THRUST = 0.00035; - var FRICTIONK = 0.6; - var FRICTIONS = 2.0; + var THRUST = 3.5; var DRAG_FREE = 0.01; - var DRAG_OPEN = 5.0; - var DRAG_LOCK = 10.0; + var DRAG_OPEN = 0.5; + var DRAG_LOCK = 1; + var CORRECTION = 1; var leftAngle = (1 - this.leftPivot) * Math.PI / 2; var rightAngle = (1 - this.rightPivot) * Math.PI / 2; - var vx = this.x - this.lastX; - var vy = this.y - this.lastY; - var cleft = Math.cos(leftAngle); var cright = Math.cos(rightAngle); var sleft = Math.sin(leftAngle); var sright = Math.sin(rightAngle); - var ax = 0; - var ay = GRAVITY; - var thrust = +!this.free * +this.up * THRUST; - ax += thrust * (cleft - cright); - ay += thrust * (sright + sleft); + var ax = thrust * (cleft - cright); + var ay = thrust * (sright + sleft); + var v = this.body.linearVelocity; var drag = this.up ? DRAG_OPEN : this.free ? DRAG_FREE : DRAG_LOCK; - ax += drag * Math.max(cleft, cright) * vx * vx * -Math.sign(vx); - ay += drag * (sleft + sright) * vy * vy * -Math.sign(vy); - - var origX = this.x; - var origY = this.y; - this.y += vy + ay; - var collided = this.y < 0; - if (collided) { - var friction = -Math.sign(vx) * Math.abs(ay) - * (Math.abs(vx) < 0.001 ? FRICTIONS : FRICTIONK); - ax += Math.sign(friction) * Math.min(Math.abs(friction), Math.abs(vx)); - } - this.x += vx + ax; - this.y = Math.max(0, this.y); - this.lastX = origX; - this.lastY = origY; + ax += drag * Math.max(cleft, cright) * v.x * v.x * -Math.sign(v.x); + ay += drag * (sleft + sright) * v.y * v.y * -Math.sign(v.y); + + if (!this.up || this.free) + ax += CORRECTION * (cleft - cright) * v.y * v.y * Math.sign(v.y); + + this.body.ApplyForce(new b2Vec2(ax, ay), this.body.position); + this._updateTransforms(); }, TAPS: ['tick'], }); +function bodyFromAABB (world, position, aabb, density) { + var bd = new Box2D.b2BodyDef(); + var shape = new Box2D.b2PolygonShape(); + shape.SetAsBox(aabb.hw, aabb.hh); + if (density) + bd.type = Box2D.DYNAMIC_BODY; + bd.position = new b2Vec2(position[0], position[1]); + var body = world.CreateBody(bd); + body.CreateFixture(shape, density || 0); + return body; +} + +function bodyFromLine (world, p0, p1) { + var bd = new Box2D.b2BodyDef(); + var shape = new Box2D.b2EdgeShape(); + shape.Set(new b2Vec2(p0), new b2Vec2(p1)); + var body = world.CreateBody(bd); + body.CreateFixture(shape, 0); + return body; +} + +function pinJoint (world, bodyA, bodyB, anchor) { + var dfn = new Box2D.b2RevoluteJointDef(); + dfn.Initialize(bodyA, bodyB, new b2Vec2(anchor)); + dfn.maxMotorTorque = 10.0; + dfn.motorSpeed = 0.0; + dfn.enableMotor = true; + return Box2D.castObject(world.CreateJoint(dfn), Box2D.b2RevoluteJoint); +} + var GameScene = yT(yuu.Scene, { constructor: function () { yuu.Scene.call(this); - var zoom = 2; + var zoom = 10; this.layer0.resize( zoom * -1.3333333333/2, zoom * -0.2, zoom * 1.3333333333, zoom * 1); + var world = new Box2D.b2World(new b2Vec2(0, -5)); + var body, left, right; - this.player = new yuu.E(body = new yuu.Transform() - .setScale([0.081, 0.091, 1]), - new yuu.QuadC('@player') - .setAnchor('bottom') - .setPosition([0, 0])); - var leftWing = new yuu.E(left = new yuu.Transform() - .setPosition([-0.3, 0.65, 0]) - .setScale([0.45, 0.22, 1]), - new yuu.QuadC('@left') - .setZ(-1) - .setAnchor("right") - .setPosition([0, 0])); - var rightWing = new yuu.E(right = new yuu.Transform() - .setPosition([0.3, 0.65, 0]) - .setScale([0.45, 0.22, 1]), - new yuu.QuadC('@right') - .setZ(-1) - .setAnchor('left') - .setPosition([0, 0])); + this.player = new yuu.E( + body = new BodyC(bodyFromAABB( + world, [0, 5], new yuu.AABB(0.89, 1.0), 1.0)), + new yuu.QuadC('@player') + .setSize([0.89, 1.0]) + ); + + var leftWing = new yuu.E(left = new BodyC( + bodyFromAABB(world, [-0.50, 5.15], new yuu.AABB(0.45, 0.22), 1.0)), + new yuu.QuadC('@left') + .setZ(-1) + .setSize([0.45, 0.22])); + var leftJoint = pinJoint(world, left.body, body.body, [-0.1, 5.15]); + var rightWing = new yuu.E(right = new BodyC( + bodyFromAABB(world, [0.50, 5.15], new yuu.AABB(0.45, 0.22), 1.0)), + new yuu.QuadC('@right') + .setZ(-1) + .setSize([0.45, 0.22])); + var rightJoint = pinJoint(world, right.body, body.body, [0.1, 5.15]); this.player.addChildren(leftWing, rightWing); this.entity0.addChild(this.player); - var ground = new yuu.E(new yuu.Transform() - .setPosition([0, -10, 1]) - .setScale([100, 20, 1]), - new yuu.QuadC() - .setColor([0, 0.5, 0, 1])); + var ground = new yuu.E( + new BodyC(bodyFromLine(world, [-100, 0], [100, 0])), + new yuu.QuadC() + .setAnchorAtPosition('top') + .setSize([100, 1]) + .setColor([0, 0.5, 0, 1])); this.entity0.addChild(ground); this.player.attach( - this.controller = new PlayerController(body, left, right)); + this.controller = new PlayerController(body, left, right, + leftJoint, rightJoint)); Object.assign(this.commands, this.controller.commands); + this.entity0.attach(new yuu.Ticker(function () { + world.Step(1/60, 8, 8); + return true; + }, 1)); + this.ready = yuu.ready([ new yuu.Material('@player'), new yuu.Material('@left'),