X-Git-Url: https://git.yukkurigames.com/?p=featherfall2.git;a=blobdiff_plain;f=src%2Fmain.js;h=ecff045199e696e668da38e7b431df441ce53f0a;hp=26ec069c6990c51fb170b8a1b555aaaf271c7c95;hb=5b22796116262ad2de3b7f6cd0fb635796362bbc;hpb=c6b5fcbed00096406ca526ec55f5e945d35c916a diff --git a/src/main.js b/src/main.js index 26ec069..ecff045 100644 --- a/src/main.js +++ b/src/main.js @@ -1,43 +1,268 @@ "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 () { - +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.commands = { + dleftLeft: yuu.propcmd(this, 'dleftLeft'), + dleftRight: yuu.propcmd(this, 'dleftRight'), + drightLeft: yuu.propcmd(this, 'drightLeft'), + drightRight: yuu.propcmd(this, 'drightRight'), + up: yuu.propcmd(this, 'up'), + free: yuu.propcmd(this, 'free'), + }; + }, + + _updatePivots: function () { + var PIVOT_SPEED = 0.05; + var leftSpeed = (this.dleftRight - this.dleftLeft) * PIVOT_SPEED; + var rightSpeed = (this.drightLeft - this.drightRight) * PIVOT_SPEED; + this.leftPivot = yf.clamp(this.leftPivot + leftSpeed, 0, 1); + this.rightPivot = yf.clamp(this.rightPivot + rightSpeed, 0, 1); + }, + + _updateTransforms: function () { + 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 THRUST = 3.5; + var DRAG_FREE = 0.01; + 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 cleft = Math.cos(leftAngle); + var cright = Math.cos(rightAngle); + var sleft = Math.sin(leftAngle); + var sright = Math.sin(rightAngle); + + var thrust = +!this.free * +this.up * THRUST; + 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) * 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); - this.layer0.resize(-0.5, -0.5, 1, 1); + 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 BodyC(bodyFromAABB( + world, [0, 5], new yuu.AABB(0.89, 1.0), 1.0)), + new yuu.QuadC('@player') + .setSize([0.89, 1.0]) + ); - this.player = new yuu.E(new yuu.Transform(), - new yuu.QuadC('@player')); - var leftWing = new yuu.E(new yuu.Transform(), - new yuu.QuadC('@left')); - var rightWing = new yuu.E(new yuu.Transform(), - new yuu.QuadC('@right')); + 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 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, + 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'), new yuu.Material('@right')]); }, + init: function () { + var audio0 = new Audio(); + audio0.src = audio0.canPlayType('audio/ogg') + ? "data/sound/starting-line.ogg" + : "data/sound/starting-line.mp3"; + audio0.autoplay = true; + audio0.loop = true; + document.body.appendChild(audio0); + var source = yuu.audio.createMediaElementSource(audio0); + source.connect(yuu.audio.music); + }, + KEYBINDS: { space: '+up', up: '+up', - q: '+dleft_left', - w: '+dleft_right', - o: '+dright_left', - p: '+dright_right', + q: '+dleftLeft', + w: '+dleftRight', + o: '+drightLeft', + p: '+drightRight', + z: '+free', + x: '+up', } }); @@ -50,6 +275,7 @@ function load () { yuu.audio.storage = storage; var game = new GameScene(); yuu.director.pushScene(game); + return game.ready; } window.addEventListener("load", function() {