概要
wsl(wsl2じゃない)で、elixirやってみた。
練習問題やってみた。
練習問題
Livebookの、kino.jsで、弾幕を動作させよ。
写真
サンプルコード
defmodule KinoHTML.Bullet do
use Kino.JS
def new(tag) do
Kino.JS.new(__MODULE__, tag)
end
asset "main.js" do
"""
var danmaku0;
var SC_W = 400;
var SC_H = 400;
export async function init(ctx, tag) {
await ctx.importJS("https://cdn.rawgit.com/daishihmr/bulletml.js/master/build/bulletml.min.js")
ctx.root.innerHTML = `
<canvas id="canvas"></canvas>
`;
danmaku0 = bulletml.buildXML(tag);
var scene = new Scene();
var player = new Player().addTo(scene);
var danmakuConfig = {
target: player,
createNewBullet: function(runner, spec) {
var bullet = new Bullet(spec);
bullet.x = runner.x;
bullet.y = runner.y;
runner.onVanish = function() {
bullet.remove();
};
bullet.update = function() {
runner.update();
this.x = runner.x;
this.y = runner.y;
this.areaTest();
};
bullet.addTo(scene);
}
};
var enemyXML = new Hex(40, 30, 15, "hsl(290, 60%, 80%)").addTo(scene);
enemyXML.danmakuRunner = danmaku0.createRunner(danmakuConfig);
enemyXML.update = function(frame) {
this.x = SC_W * .5 - Math.cos(frame * 0.01) * SC_W * .4;
this.y = SC_H * .3 - Math.sin(frame * 0.03) * SC_H * .2;
this.danmakuRunner.x = this.x;
this.danmakuRunner.y = this.y;
this.danmakuRunner.update();
};
scene.start();
}
var keyboard = {
up: false,
down: false,
left: false,
right: false
};
Array.prototype.erase = function(obj) {
var idx = this.indexOf(obj);
if (idx !== -1)
this.splice(idx, 1);
};
CanvasRenderingContext2D.prototype.fillHex = function(w, h) {
this.beginPath();
this.moveTo(Math.sin(Math.PI / 3 * 0) * w * .5, Math.cos(Math.PI / 3 * 0) * h * .5);
for (var i = 1; i < 6; i++)
{
this.lineTo(Math.sin(Math.PI / 3 * i) * w * .5, Math.cos(Math.PI / 3 * i) * h * .5);
}
this.closePath();
this.fill();
};
HTMLCanvasElement.prototype.fitWindow = function() {
var resize = function() {
var rateWidth = this.width / window.innerWidth;
var rateHeight = this.height / window.innerHeight;
var rate = this.height / this.width;
if (rateWidth > rateHeight)
{
this.style.width = innerWidth + "px";
this.style.height = innerWidth * rate + "px";
}
else
{
this.style.width = innerHeight / rate + "px";
this.style.height = innerHeight + "px";
}
}.bind(this);
window.addEventListener("resize", resize, false);
resize();
};
var Scene = function() {
var canvas = document.getElementById('canvas');
canvas.width = SC_W;
canvas.height = SC_H;
canvas.fitWindow();
this.context = canvas.getContext("2d");
this.context.textAlign = "right";
this.context.textBaseline = "top";
this.frame = 0;
this.sceneObjects = [];
};
Scene.prototype.start = function() {
var renderFrame = function() {
this.context.fillStyle = "black";
this.context.globalCompositeOperation = "source-over";
this.context.fillRect(0, 0, SC_W, SC_H);
this.context.globalCompositeOperation = "lighter";
var copied = [].concat(this.sceneObjects);
for (var i = 0, end = copied.length; i < end; i++)
{
copied[i].update(this.frame);
copied[i].draw(this.context);
}
this.context.fillStyle = "white";
this.frame += 1;
requestAnimationFrame(renderFrame);
}.bind(this);
renderFrame();
};
Scene.prototype.addChild = function(obj) {
obj.parent = this;
this.sceneObjects.push(obj);
};
Scene.prototype.removeChild = function(obj) {
obj.parent = null;
this.sceneObjects.erase(obj);
};
var Hex = function(w, h, radius, color) {
this.radius = radius;
this.x = 0;
this.y = 0;
this.color = color || "hsl(0, 60%, 80%)";
this.canvas = document.createElement("canvas");
this.canvas.width = w * 2;
this.canvas.height = h * 2;
var context = this.canvas.getContext("2d");
context.globalCompositeOperation = "lighter";
context.fillStyle = this.color;
context.translate(w, h);
context.globalAlpha = 1.0;
context.fillHex(w, h);
this.parent = null;
};
Hex.prototype = {
constructor: Hex,
update: function() {
},
draw: function(context) {
context.fillStyle = this.color;
context.save();
context.translate(this.x, this.y);
context.drawImage(this.canvas, -this.canvas.width * .5, -this.canvas.height * .5);
context.restore();
},
addTo: function(parent) {
parent.addChild(this);
return this;
},
remove: function() {
if (this.parent)
this.parent.removeChild(this);
return this;
}
};
var Bullet = function(spec) {
Hex.call(this, 8, 8, 2, spec.color);
};
Bullet.prototype = Object.create(Hex.prototype);
Bullet.prototype.areaTest = function() {
if (this.x < 0 || SC_W < this.x || this.y < 0 || SC_H < this.y)
{
this.remove();
return;
}
};
var Player = function() {
Hex.call(this, 10, 10, 5, "hsl(120, 60%, 80%)");
this.x = SC_W * 0.5;
this.y = SC_H * 0.9;
this.speed = 1.5;
};
Player.prototype = Object.create(Hex.prototype);
Player.prototype.update = function(frame) {
if (keyboard.left)
this.x -= this.speed;
else if (keyboard.right)
this.x += this.speed;
if (keyboard.up)
this.y -= this.speed;
else if (keyboard.down)
this.y += this.speed;
this.x = Math.max(0, Math.min(this.x, SC_W));
this.y = Math.max(0, Math.min(this.y, SC_H));
};
"""
end
end
KinoHTML.Bullet.new("""
<bulletml>
<action label="top">
<repeat>
<times>200</times>
<action>
<fire>
<direction type="sequence">23</direction>
<bullet/>
</fire>
<wait>1</wait>
</action>
</repeat>
</action>
</bulletml>
""")
以上。