-
-
Save pulcheri/426464905e9991d7442048d83c5e7b70 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import js.Browser.*; | |
import js.html.CanvasElement; | |
import js.html.CanvasRenderingContext2D; | |
import js.html.HTMLDocument; | |
import js.html.MouseEvent; | |
import sf.test.Test.Entity; | |
/** | |
* @author YellowAfterlife | |
* http://yal.cc/top-down-bouncing-loot-effects/ | |
*/ | |
class Test { | |
static var canvas:CanvasElement = document.createCanvasElement(); | |
static var context:CanvasRenderingContext2D = canvas.getContext2d(); | |
static var texture:Array<CanvasElement> = []; | |
static var entities:Array<Entity>; | |
static inline var width = 640; | |
static inline var height = 360; | |
static function reset() { | |
entities = [new ClosedChest(width / 2, height / 2)]; | |
} | |
static function render() { | |
context.clearRect(0, 0, width, height); | |
var i:Int = 0, n:Int = entities.length, e:Entity; | |
while (i < n) { | |
e = entities[i]; | |
if (e.remove > 0 && --e.remove <= 0) { | |
entities.splice(i, 1); | |
n--; | |
} else { | |
e.update(); | |
i++; | |
} | |
} | |
entities.sort(Entity.compareY); | |
// pass 1: draw shadows | |
context.save(); | |
context.globalAlpha = 0.7; | |
context.fillStyle = "black"; | |
i = 0; while (i < n) { | |
e = entities[i]; | |
var r = e.getShadowSize(); | |
var x = e.x - r; | |
var y = e.y - r / 2; | |
var w = r * 2; | |
var h = r; | |
var x1 = x + w / 2; | |
var y1 = y + h / 2; | |
var x2 = x + w; | |
var y2 = y + h; | |
var xm = w * 0.275892; | |
var ym = h * 0.275892; | |
// | |
context.beginPath(); | |
context.moveTo(x1, y); | |
context.bezierCurveTo(x1 + xm, y, x2, y1 - ym, x2, y1); | |
context.bezierCurveTo(x2, y1 + ym, x1 + xm, y2, x1, y2); | |
context.bezierCurveTo(x1 - xm, y2, x, y1 + ym, x, y1); | |
context.bezierCurveTo(x, y1 - ym, x1 - xm, y, x1, y); | |
context.closePath(); | |
context.fill(); | |
// | |
i++; | |
} | |
context.restore(); | |
// pass 2: draw objects | |
i = 0; while (i < n) { | |
e = entities[i]; | |
if (e.visible) { | |
context.drawImage(texture[e.frame], Std.int(e.x - 32), Std.int(e.y - e.z - 42)); | |
} | |
i++; | |
} | |
} | |
static function main() { | |
var atlas = document.createImageElement(); | |
atlas.onload = function() { | |
canvas.width = width; | |
canvas.height = height; | |
document.body.appendChild(canvas); | |
for (i in 0 ... 10) { | |
var tex = document.createCanvasElement(); | |
tex.width = tex.height = 64; | |
tex.getContext2d().drawImage(atlas, -i * 64, 0); | |
texture.push(tex); | |
} | |
reset(); | |
canvas.addEventListener("mousedown", function(event) { | |
var x = event.offsetX; if (x == null) x = width / 2; | |
var y = event.offsetY; if (y == null) y = height / 2; | |
var n = entities.length, i = 0; | |
while (i < n) { | |
var e = entities[i]; | |
var action = e.action; | |
if (action != 0) { | |
var ex = e.x, ey = e.y, ez = e.z; // positions | |
var dx = ex - x, dy = (ey - ez) - y; // delta | |
if (dx * dx + dy * dy < 32 * 32) { | |
if (action == 1) { | |
entities.splice(i, 1); | |
var oc = new OpenChest(ex, ey, ez); | |
oc.zspeed = e.zspeed; | |
entities.push(oc); | |
var num = Std.int(10 + Math.random() * 5); | |
while (--num >= 0) entities.push(new Coin(ex, ey, ez)); | |
} | |
break; | |
} | |
} | |
i += 1; | |
} | |
if (i >= n) entities.push(new ClosedChest(x, y)); | |
}); | |
window.setInterval(function() { | |
render(); | |
}, 50); | |
}; | |
atlas.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAABACAMAAAC9ZDaMAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAAFAwccWk/hUww0n0syqwA3r0A374A3bwa374a9ds4/+kA/+oA//862tRe3u7WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjA1oYgAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNvyMY98AAAYpSURBVHhe7ZvtYtpIDEU3IU3abhPe/2mzvroSGht/SON0DUHnB8bDXJnRnNSFtP98fmOeFT1NouEBHSjCPCl6ukIJuIiGB3SgCKP+3b+AakCHAkj9VnoqeLovD5B7PH0h3vn8MnA6bUtYAi5QAvZyQwJyA/ZsnhowKKDDIdqkgTF9OcBcXl8Kguv9K+Su3IIk0NO7AMqdTufzxwcU3JawBJylBOzlSwXU9Xe3T/dONj9XZY8A11ny+hqtMV8h8w5MPiPfQ9TQC6flN1AD6On/xNPTywuk+xh4ecHNeE3CEnCGEnAPXybgvgY8P7+/a3zgdSCzgRoTkAWxvGVfX98VpuPvYaqfryP6DuSCAuT78yevILrX9i+XJvsF7gMCngZMQrAsYQl4RQm4j78g4I8f2SWwgUT3IixgK4BGlUgFpqf6GdsVrvUDeBbLo4JcSulR0Lrn149nnaMEdAVdwuUPJCXghBJwP18iYKvQ1wiYEQhoaMR2BeTH+v38KVEhlufV8ahlBuJ5m8mjKxjvYds9I542vEo+uw8XELQS4uuZsYIl4IQScD9fLmD2o/xUP+R51AkruAAtvp3rNXDlpdsv2crb1fGoZQR7R1trsJXKZCWrIFbR9hBEs45XyGShi6ODaVxBHlELEsrQcKbTBkrAESWgeHdBB9PcjIC6A7ohkSrIUoExtqXrNdp3zflTtvK4NgXUMgLHtvOowFkyWYF8+LGIdhGraFcColnHK0Sz0ISKGBjRF1OYgLjtSqEB1OYXMq2CJeCIElBcufBwAlpSQiOiAtoN+Ncvzp+ylRf/BtlYhXCMCm6tYU5AwEqxPqILbf9BLNniFWJZ3CQhDBFnhD4JoZuW0lo42lkJuEAJaIgzwuECop2Z5XMTLUtim+dXldCIqIBLeWMrL6b9brcf5/xQsp1HBc7Co6OlOgTEdUks6/gaYsmxgEC8EXISYjbydgSogg8hJeBGXva6BBSgCoFKOi2AiWdHgCrdAuYWb5vIDSNUYrsKryqRCbaxOnERVJDALJE8fnhwNVl8IwDWE+nDtYKsBGJ95BqY8C7Gsk5OQNOP/5i+BcJAHuikk1fALFRCBmmcS5mBEjCULwHHQJiDBYw2jrD13DADVTLNnwIteNRpi9jGuwDZX8Vx1bie6cfV4BhZQ6seYD0QywPvvncjmjV6BKSEYw0h39xXyNdQPpsLWMElpIIY0UgJOKUEBDchoLUuL6Btl8EqsRouoG9kXD/gCk5ViL8DYmvgeuJ/EWmvq6UGcBavoKEBKTMQzRpt/3RoBSgCeagJHgk/OjhLCkKy84BOO+G5J01CCtjWKAGvKAFvRkCjT0DbPlMq3vxWIZMPxCpMtXPi7wAbr4u4EO9Bu/WmEs90wiZHCMjbLiSkgHZuCpElBds85yHD/6DJGpRwmi8BZygB8exwAbXvsv2ZxU+TPRWQmJLZvusKGNOXA/gajEzaVqAGDchbSPYAKc/m0sDXEEny4wPl4dGeu3xgWUCJX/JI2bnpiDNIqBGhBJylBKQueLw7AT0rfRPyzdOgktMHIKHhjjTztg480+Ew05totobnZQkD+Q7w3ccFpDI48qsX6mcSmUbLAk7zmG/Y+TRfAi6ATAl4pwIi/fYmXVN6NtAEym6dgyTQ0w725JGkQH01LA3e3no6qBuYEBBAFcrCI8cpD1gTEEi8ybfygRIwwZ48kiVge+TrHQL2tQ+geTjqYBIp0Zm9DfasoNW3bwd0A9O3YAPSQB88Uh7MWRNQgwpy9uX0Ur4E/KvsWcGDC8jm4Q//PgFJX+sKgu7t6T1+/EFUwI8P6AJxIAp1Em8urAvYky8Bbxh074EFBPtaUBwLdy+2f/iCGMIAE+haIfy6bUnAvnwJ+I3h7t21gMUj0UqkxshNk9jovICgJ18CFhdKwOJwINFYGz7aCF7XqbNk8yVgMSIr0JRsvgQsrqBEBOJE5TMy+RKwuCIj0ByZfAlYzAJZcOPMymdE8yVgMUtUoCWi+RKwWIQS5eUzIvkSsFgkItAakXwJWKyyJk+ErXwJWKxSAhbfmhKwOJQSsDiUErA4lBKwOJQSsDiUErA4lBKwOJQSsDiUErA4kM/P/wAXdPS9Q8I7oAAAAABJRU5ErkJggg=="; | |
} | |
} | |
class Entity { | |
public var x:Float; | |
public var y:Float; | |
public var z:Float; | |
public var xspeed:Float = 0; | |
public var yspeed:Float = 0; | |
public var zspeed:Float = 0; | |
public var friction:Float = 0.3; | |
public var gravity:Float = 1.0; | |
public var frame:Int; | |
public var action:Int = 0; | |
public var remove:Int = 0; | |
public var flicker:Int = 0; | |
public var visible:Bool = true; | |
public function new(x:Float, y:Float, z:Float) { | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
} | |
public function update():Void { | |
if (flicker > 0 && --flicker <= 0) { | |
visible = !visible; | |
flicker = 2; | |
} | |
x += xspeed; | |
y += yspeed; | |
// friction: | |
var xyspeed = Math.sqrt(xspeed * xspeed + yspeed * yspeed); | |
if (xyspeed > 0) { | |
var mul = Math.max(0, xyspeed - friction) / xyspeed; | |
xspeed *= mul; | |
yspeed *= mul; | |
} | |
// vertical: | |
if (z > 0) zspeed -= gravity; | |
z += zspeed; | |
if (z < 0) { | |
if (zspeed < 0) zspeed = -zspeed * 0.6 - 0.7; | |
if (zspeed < 1) { | |
zspeed = 0; | |
z = 0; | |
} else z = -z; | |
} | |
} | |
public function getShadowSize():Float { | |
return 24 / (1 + z / 20); | |
} | |
public static function compareY(a:Entity, b:Entity):Int { | |
return a.y < b.y ? -1 : a.y > b.y ? 1 : 0; | |
} | |
} | |
class Coin extends Entity { | |
public function new(x:Float, y:Float, z:Float) { | |
super(x, y, z); | |
frame = Std.int(Math.random() * 8); | |
remove = 40; | |
flicker = 20; | |
var dir = Math.random() * Math.PI * 2; | |
var len = 8 + Math.random() * 2; | |
xspeed = Math.cos(dir) * len; | |
yspeed = Math.sin(dir) * len; | |
zspeed = 8; | |
} | |
override public function update():Void { | |
super.update(); | |
frame = (frame + 1) % 8; | |
} | |
override public function getShadowSize():Float { | |
return 12 / (1 + (z + Math.cos(frame / 8 * 2 * Math.PI)) / 20); | |
} | |
} | |
class Chest extends Entity { | |
override public function getShadowSize():Float { | |
return 24 / (1 + z / 20); | |
} | |
} | |
class ClosedChest extends Entity { | |
public function new(x:Float, y:Float) { | |
super(x, y, 64); | |
zspeed = -16; | |
frame = 8; | |
action = 1; | |
} | |
} | |
class OpenChest extends Entity { | |
public function new(x:Float, y:Float, z:Float) { | |
super(x, y, z); | |
frame = 9; | |
remove = 50; | |
flicker = 30; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment