phina.jsでSpriteの輪郭を強調

Gyazo

自機や敵やアイテムが視認しづらいめう!

PhinaShooterでは全体をドット調で表現していこうと思っている。
で、そのためにCanvasを480x320という小さなサイズで描いた上で拡大するという方法をとっている。

ただ、当然画面が粗くなって各スプライトの視認性が悪くなってしまった。

そこで上の動画のようにスプライトの輪郭を点滅させて強調表示することにした。

輪郭を強調するめう!

方法としては原始的で、

  1. 画像アセットのロード後に輪郭のみ抽出した画像を作成する
  2. AssetManagerに作成した画像を登録する
  3. スプライトに輪郭のみの画像を重ねて表示する

こんな感じでやってみた。

まず、輪郭抽出の方法だが、元画像からピクセルデータを取り出し、

  • アルファ値が0より大きく
  • 上下左右にアルファ値が0のピクセルがある

…という条件を満たすピクセルだけを白く塗りつぶした新しい画像を作成する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var texture = phina.asset.AssetManager.get("image", textureName);
var w = texture.domElement.width;
var h = texture.domElement.height;
// 元画像
var src = phina.graphics.Canvas().setSize(w, h);
src.context.drawImage(texture.domElement, 0, 0);
// 元画像からピクセルデータを取り出す
var srcData = src.context.getImageData(0, 0, w, h);
// 輪郭のみの画像
var dst = phina.graphics.Canvas().setSize(w, h);
dst.fillStyle = "white";
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
var cIndex = ((y + 0) * w + (x + 0)) * 4 + 3;
var a = srcData.data[cIndex]; // 対象ピクセルのアルファ値
var lIndex = ((y + 0) * w + (x - 1)) * 4 + 3;
var rIndex = ((y + 0) * w + (x + 1)) * 4 + 3;
var tIndex = ((y - 1) * w + (x + 0)) * 4 + 3;
var bIndex = ((y + 1) * w + (x + 0)) * 4 + 3;
var l = (0 <= lIndex && lIndex < srcData.data.length) ? srcData.data[lIndex] : 255; // 左隣のピクセルのアルファ値
var r = (0 <= rIndex && rIndex < srcData.data.length) ? srcData.data[rIndex] : 255; // 右
var t = (0 <= tIndex && tIndex < srcData.data.length) ? srcData.data[tIndex] : 255; // 上
var b = (0 <= bIndex && bIndex < srcData.data.length) ? srcData.data[bIndex] : 255; // 下
// 対象ピクセルが不透明で、上下左右に透明なピクセルがある場合
if (a > 0 && (l == 0 || r == 0 || t == 0 || b == 0)) {
dst.fillRect(x, y, 1, 1); // 1x1の四角を描画
}
}
}

こうして作った画像をAssetManagerに登録する。

1
phina.asset.AssetManager.set("image", textureName + "Outline", dst);

アセット名はもとの名前に後ろに”Outline”をつけてみた。

で、こいつを利用するスプライトだが、通常のSpriteクラスを拡張して作る。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
phina.define("ps.OutlinedSprite", {
superClass: "phina.display.Sprite",
init: function(texture, width, height) {
this.superInit(texture, width, height);
var self = this;
// 輪郭スプライト
this.outline = phina.display.Sprite(texture + "Outline", width, height).addChildTo(this);
this.outline.update = function(app) {
// frameIndexは親と同期させる
this.frameIndex = self.frameIndex;
this.alpha = ps.OutlinedSprite.staticAlpha;
};
},
_static: {
staticAlpha: 1.0
}
});

輪郭スプライトを元スプライトの子要素として追加しておく。

さらにupdateメソッド内でframeIndexが親スプライトと同期するようにする。

また、輪郭スプライトのアルファ値は外部から設定できるようにしておき、MainSceneなどのupdateで更新する。

オブジェクトの種類に応じて色を変えるめう!

これで視認性が良くなったと思うので、さらに自機は青アイテムは緑敵は赤といった具合にオブジェクトの種類に応じて輪郭色を変えてみた。

これで粗い画面でも最低限の識別ができるようになったと思う。