Akashic Advent Calendar 2019 三日目の記事です。
ゲーム開発中、プレイ内容に応じて演出を加えたくなることがあります。この記事では、そういった演出に利用できそうな紙吹雪エンジン kamihubuki-js を Akashic Engine 上で実行する手順を解説します。
kamihubuki-js とは
kamihubuki-js は2Dの紙吹雪専用物理エンジンです。エンジン自体は描画機能を持っておらず、利用者が実装する必要があります。
動画は kamihubuki-js から転載。
kamihubuki-js の導入
kamihubuki-js は npmjs に publish されていません。GitHubからインストールします。
npm install blackspotbear/kamihubuki-js
Akashic Engine からの利用
kamihubuki-js 付属のサンプルの1つは Akashic Engine への組み込み例となっています。これを参考にします。1つ1つみていきましょう。
紙吹雪の生成
function createConfetti(x: number, y: number): kh.Confetti {
const vx = 0;
const vy = 200;
const angle = g.game.random.get(0, 100) / 100 * Math.PI;
const fins: kh.Fin[] = [
{
angle: g.game.random.get(-100, 100) / 100 * (Math.PI / 2),
size: 30,
armAngle: 0,
armLength: 1
},
{
angle: g.game.random.get(-100, 100) / 100 * (Math.PI / 2),
size: 30,
armAngle: g.game.random.get(50, 100) / 100 * Math.PI,
armLength: 1
}
];
const co = new kh.Confetti({
x: x, y: y,
vx: vx, vy: vy,
angle: angle,
fins: fins,
av: 0,
M: 0.5,
K: 0.02,
I: 3,
RD: 0.99
});
return co;
}
createConfetti()
で kamihubuki-js の Confetti
インスタンスを生成しています。 kamihubuki-js では1つの紙吹雪につき1つの Confetti
インスタンスを用意します。
コンストラクタには初期位置(x、y)、初速度(vx, vy)、空気抵抗(K)などの値が渡されています。 しかしこれだけでは、紙吹雪が回転する様子を扱うことができません。 そこで kamihubuki-js では、質点にフィンを取り付けます。フィンが受ける風の力で質点が回転します。 fins
がフィンのパラメータです。
fins
パラメータによって、質点から任意の数の腕を伸ばし、その先にフィンを取り付けることができます。
図はkamihubuki-jsから転載。
フィンの取り付け方によって、紙吹雪はさまざまな運動をするようになります。
紙吹雪の描画
function createConfettiEntity(scene: g.Scene, co: kh.Confetti, noCollision: boolean): g.E {
const w = 20;
const h = 10;
const w2 = w / 2;
const h2 = h / 2;
const rect = new g.FilledRect({
scene: scene,
width: w,
height: h,
x: co.x - w2,
y: co.y - h2,
cssColor: colors[colorIndex]
});
colorIndex = (colorIndex + 1) % colors.length;
rect.update.add(() => {
co.update(1 / g.game.fps, windX, windY, 0, 200);
// collision detection and resolution.
if (noCollision) {
if (co.x < 0 || co.x > g.game.width || co.y > g.game.height) {
rect.destroy();
}
} else {
const k = 0.5;
if (co.x < 0) {
co.x = 0;
co.vx = Math.abs(co.vx) * k;
} else if (co.x > g.game.width) {
co.x = g.game.width;
co.vx = -Math.abs(co.vx) * k;
}
if (co.y < -g.game.height) {
co.y = -g.game.height;
co.vy = 0;
} else if (co.y > g.game.height) {
co.y = g.game.height;
co.vy = -Math.abs(co.vy) * k;
}
}
rect.x = co.x - w2;
rect.y = co.y - h2;
rect.angle = -co.angle / Math.PI * 180;
rect.modified();
});
return rect;
}
createConfettiEntity()
は紙吹雪を描画するインスタンスを生成する関数です。サンプルでは、紙吹雪の描画に g.FilledRect
を利用しています。色の変更や画面端との衝突処理を省いて、大切なところだけ抜き出します。
rect.update.add(() => {
co.update(1 / g.game.fps, windX, windY, 0, 200);
// 省略
rect.x = co.x - w2;
rect.y = co.y - h2;
rect.angle = -co.angle / Math.PI * 180;
rect.modified();
});
rect
の update
イベントで co.update()
を実行するようにしています。これによって、毎フレーム紙吹雪の状態を更新しています。次に紙吹雪の位置と角度を rect
に反映しています。 kamihubuki-js は角度をラジアンで扱っていますので、Akashicの角度の単位である度に変換していることに注意していください。
Akashic Engine 製コンテンツに紙吹雪を導入する手順は以上となります。
応用: 光沢のある紙吹雪
サンプルに少し手を加えて、光沢のある紙吹雪にしてみましょう。紙吹雪がある角度では白く光るようにします。
白く光る処理は、ここでは簡単に rect
にもう1つ光沢のための g.FilledRect
インスタンスを重ねることします。
// const rect = new g.FilledRect... の後ろに以下を追加する。
// 光沢を表現する g.FilledRect インスタンス。
const specular = new g.FilledRect({
scene: scene,
// rect と同じサイズだとわずかに rect の輪郭が滲み出るので、少し大きくする。
width: rect.width + 2,
height: rect.height + 2,
x: -1,
y: -1,
opacity: 0,
cssColor: "white",
// 加算合成を指定することで、光っている感じを出す。
compositeOperation: g.CompositeOperation.Lighter
});
rect.append(specular);
次に specular
の透明度を変更する処理を追加します。
// co.update( ... ) の後ろに以下を追加する。
let t = Math.abs((co.angle / Math.PI) % 2);
if (t > 1) t = 2 - t;
specular.opacity = Math.pow(t, 5);
specular.modified();
co.angle
から、角度に比例して 0 ~ 1 の間で変化する値 t
を求めています。これを specular.opacity
に与えると、角度に応じて specular
が姿を現して光沢となります。 Math.pow(t, 5)
としているのは t
の変化に緩急をつけるためです。
次のグラフの黒い線、青い線、赤い線は順に Math.pow
の第2引数が 1, 2, 5 の時の specular.opacity
の変化を表しています。第2引数が大きな値であるほど、鋭く光沢が現れることがわかります。
角度に応じて白く光るようになりました(わかりやすくするため、背景色を黒にしています)。
最後に
kamihubuki-js 付属のサンプルでは g.FilledRect
で紙吹雪を描画していますが、桜の花びらや紅葉、雪のような画像を用意して物理量を調整すれば、四季の表現にも使えそうです。
このような演出には akashic-animation を用いることも1つの方法です。 akashic-animation は SpriteStudio で作成したアニメーションの再生を行うことことができます。また、ただアニメーションを再生するだけでなく、当たり判定やプログラムからアニメーションを上書きするといった機能もあります。