Phaser3のGraphicsクラスについて
はじめに
Graphics
クラスについて、改めて使い方についてまとめてみます。
Phaser API Documentation | Phaser.GameObjects.Graphics
この記事のソースはCodePenで確認できます。
主な改定履歴
- 2024/05/22 「
Graphics
クラスとマウス操作」を追加しました
基本的な使い方 その1
基本的な流れとしては、まず線のスタイル(lineStyle
)と塗りのスタイル(fillStyle
)を設定し、そのあと各メソッドを使って図形を描画していきます。
次のコード例では2つの三角形を描画しています。
メソッド名から分かる通り、始点から終点の座標を順番に指定し、最後にfill
、stroke
メソッドで塗と線を描画しています。
(両メソッドはそれぞれfillPath
、strokePath
メソッドのエイリアス(別名)です。)
各スタイルは途中で変更できます。
変更するとそれ以降の描画のみに反映され、すでに描画されている図形には影響しません。
コードの例では1つめ図形の描画後に、線と塗のスタイルを変更し、2つめの図形を描画しています。
なお、同じパスに対し線と塗りを一緒に描画する場合、パスの内側部分は線と塗りが重なるのでメソッドの使用順序に注意してください。
stroke
メソッドの後にfill
メソッドを使用すると、線の幅の半分が塗りで隠れます。
const LINE_WIDTH = 5; // ラインの太さ
// (1) コンストラクタで線と塗りのスタイルを指定
const grph = this.add.graphics({
lineStyle: { width: LINE_WIDTH, color: 0xE00000, alpha: 1 },
fillStyle: { color: 0xFFFFFF, alpha: 1 }
});
grph.beginPath()
.moveTo(150, 100)
.lineTo(100, 200)
.lineTo(200, 200)
.closePath()
.fill()
.stroke();
// 途中で、ペンと塗のスタイルの変更
grph.lineStyle(LINE_WIDTH + 15, 0x00A000, 1)
.fillStyle(0xA0F0A0, 1);
grph.beginPath()
.moveTo(150, 300)
.lineTo(100, 400)
.lineTo(200, 400)
.closePath()
.fill()
.strokePath();
描画をリセットするにはclear
メソッドを使用します。このとき、線と塗りのスタイルも初期化されます。
また、線と塗りのスタイルのデフォルト値を変更するにはsetDefaultStyles
メソッドを使用します。
パラメータはコンストラクタと同じです。
基本的な使い方 その2
四角形、三角形、楕円のように、よく使う図形については、もっと簡単に描画するメソッドが用意されています。
始めに代表例として、四角形のメソッドを取り上げます。
const WIDTH = 200;
const HEIGHT = 100;
const LINE_WIDTH = 5; // ラインの太さ
let posX = 50;
let posY = 50;
const grph = this.add.graphics({
lineStyle: { width: LINE_WIDTH, color: 0xE00000, alpha: 1 },
fillStyle: { color: 0xFFFFFF, alpha: 1 }
});
grph.fillRect(posX, posY, WIDTH, HEIGHT).strokeRect(posX, posY, WIDTH, HEIGHT);
posY += 150;
const rect = new Phaser.Geom.Rectangle(posX, posY, WIDTH, HEIGHT);
grph.fillRectShape(rect).strokeRectShape(rect);
fillRect
メソッドとstrokeRect
メソッドは、図形の座標・サイズの値を直接パラメータとして渡し、四角形の線と塗りを描画するメソッドです。
一方で似た機能に、fillRectShape
メソッドとstrokeRectShape
メソッドがあります。
違いは、パラメータにRectangle
のインスタンスのみを渡すようになっています。
このクラスは、名前空間Phaser.Geom
配下にあるクラスで、単純に図形の情報だけを保持するクラスです。
Phaser API Documentation | Namespace: Geom
このように図形クラスが用意されている図形については、
fill***
、stroke***
メソッドとfill***Shape
、stroke***Shape
メソッドは対になって用意されています。
Graphics
クラスのメ各メソッドと図形クラスの対応関係を下の表にまとめています。
図形 | パラメータに 図形の属性を直接渡す Graphicsクラスのメソッド名 |
パラメータに 図形クラスのインスタンスを渡す Graphicsクラスのメソッド名 |
対応する 図形クラス名 |
---|---|---|---|
四角形 | fillRect / strokeRect | fillRectShape / strokeRectShape | Rectangle |
三角形 | fillTriangle / strokeTriangle | fillTriangleShape / strokeTriangleShape | Triangle |
正円 | fillCircle / strokeCircle | fillCircleShape / strokeCircleShape | Circle |
楕円 | fillEllipse / strokeEllipse | fillEllipseShape / strokeEllipseShape | Ellipse |
正方形(点) | fillPoint / - | fillPointShape / - | Point |
線 | - / lineBetween (※1) | - / strokeLineShape | Line |
多角形 | fillPoints / strokePoints | - / - | Polygon (※2) |
角の丸い四角形 | fillRoundedRect / strokeRoundedRect | - / - | - (※3) |
※1: 名前がstroke***
ではないですが、このメソッドが相当します。
※2: インスタンスをそのまま渡すのではなく、points
プロパティを渡します(この後の節で紹介)。
※3: 対応するクラスはありません。この後の節で自作例を紹介します。
Graphicsクラスの各描画メソッドのパラメータは以下のようになります。(オプション引数については一部省略。)
対応する図形クラスがある場合は、そのコンストラクタについても基本的には同様です。
図形 | パラメータ |
---|---|
四角形 | 左上のx,y座標、幅、高さ |
三角形 | 最初の点のx,y座標、2番目の点のx,y座標、3番目の点のx,y座標 |
正円 | 正円の中心のx,y座標、半径 |
楕円 | 楕円の中心のx,y座標、幅、高さ |
正方形(点) | 点のx,y座標 |
線 | 始点のx,y座標、終点のx,y座標 |
多角形 | 点の座標をx,y交互に連続して並べた配列。またはGeom.Pointクラスの配列。 |
角の丸い四角形 | 左上のx,y座標、幅、高さ、丸める角の半径 |
この記事ではすべてのメソッドのサンプルコードは掲載しませんが、上記の表のような関係性を把握しておくとコードを書くときに楽かと思います。
多角形
多角形の描画の方法について見ていきます。
ソースコードの例のように、Polygon
クラス生成する際、コンストラクタ引数に描画する図形の各頂点となる点(x,y)を列挙して配列で渡します。
その後、宣言した変数に対しpoints
メソッドを使用し、描画するメソッドに渡します。
(これ以外にも文字列で渡す方法などもありますが、詳細はAPIドキュメントを参照のこと)
ここではついでにgenerateTexture
メソッドについても紹介しておきます。
描画した図形をテクスチャとして保存し再利用できます。
const LINE_WIDTH = 5; // ラインの太さ
const grph = this.add.graphics({
lineStyle: { width: LINE_WIDTH, color: 0xE00000, alpha: 1 },
fillStyle: { color: 0xFFFFFF, alpha: 1 }
});
// 多角形を描画する
const poly = new Phaser.Geom.Polygon([100, 0, 200, 100, 150, 100, 150, 190, 50, 190, 50, 100, 0, 100, 100, 0]);
grph.fillPoints(poly.points).strokePoints(poly.points);
// 描画した図形からテクスチャを生成する
grph.generateTexture('txtr', 200, 200);
// テクスチャを描画する
const img = this.add.image(210, 100, 'txtr').setOrigin(0);
円弧
arc
はコンパスで作図したような円弧を描くメソッドです。
パスは自動で閉じないので、複数続けて描画する場合は必ずclosePath
メソッドを呼びます。
slice
はピザを切ったときのように必ず円の中心点を通ってパスを描きます。
こちらは自動でパスが閉じます。
const LINE_WIDTH = 5; // ラインの太さ
const RADIUS = 80; // 角の円の半径
let posX = 150;
let posY = 120;
const grph = this.add.graphics({
lineStyle: { width: LINE_WIDTH, color: 0xE00000, alpha: 1 },
fillStyle: { color: 0xFFFFFF, alpha: 1 }
});
// 切られた正円(フタ状)
grph.beginPath()
.arc(posX, posY, RADIUS, Math.PI / 4 * 5, Math.PI / 4 * 7)
.closePath().fill().stroke();
// 切られた正円(ポット状)
grph.beginPath()
.arc(posX, posY + 20, RADIUS, Math.PI / 4 * 5, Math.PI / 4 * 7, true)
.closePath().fill().stroke();
// 線と塗りを変更
grph.lineStyle(LINE_WIDTH + 5, 0x603030, 1).fillStyle(0xFFFF90, 1);
// 切り取られたピザ
posY += 200;
grph.slice(posX, posY, RADIUS, Math.PI / 6, Math.PI / 6 * 11)
.fill().stroke();
// ピザひとかけら
grph.slice(posX + 30, posY, RADIUS, Math.PI / 6, Math.PI / 6 * 11, true)
.fill().stroke();
パラメータについては、中心点のx,y座標・半径の順番で、続いて第4,5引数が「開始・終了角度 (ラジアン単位)」、第6引数は「回転方向を反転するか否か」になります。
キャンバス操作
以下のメソッドを使うと、描画する図形の表示位置、角度、スケールを変更できます。
各対象メソッドを呼び出した後に描画される図形が対象で、既存の図形には影響しません。
これにより、図形を描画するメソッドの引数を変えずに、図形の表示のみを変形できます。
楕円を書くfillEllipse
メソッドの引数がすべて同じなことに注目してください。
メソッド | 機能 |
---|---|
rotateCanvas | 以降に描画する図形を回転して表示する。 |
scaleCanvas | 以降に描画する図形のスケールを変更して表示する。 |
translateCanvas | 以降に描画する図形の位置を移動して表示する。 |
const WIDTH = 150;
const HEIGHT = 50;
const LINE_WIDTH = 5; // ラインの太さ
const grph = this.add.graphics({
lineStyle: { width: LINE_WIDTH, color: 0xE00000, alpha: 1 },
fillStyle: { color: 0xFFFFFF, alpha: 1 }
});
grph.setPosition(150, 150);
const drawFunc = () => {
grph.fillEllipse(0, 0, WIDTH, HEIGHT).strokeEllipse(0, 0, WIDTH, HEIGHT);
};
drawFunc();
grph.translateCanvas(200, 0); // 以降、右に250pxシフト
drawFunc();
grph.scaleCanvas(0.5, 0.5); // 以降、スケールを0.5倍に
drawFunc();
grph.rotateCanvas(Math.PI / 3); // 以降、時計回り60度
drawFunc();
グラデーション
lineGradientStyle
、fillGradientStyle
メソッドはそれぞれ線と塗りのスタイルにグラデーションを指定できる機能です。
パラメータの色指定の部分は、左上、右上、左下、右下の順番で指定します。
ただし、すべての図形に対しきれいにグラデーションが表示されるわけではなく、API ドキュメントによると線については「単一の線でのみ」、塗については「長方形と三角形でのみ」使用するのが最適とのことです。
const LINE_WIDTH = 20; // ラインの太さ
const grph = this.add.graphics();
grph.lineGradientStyle(LINE_WIDTH, 0xFFFFFF, 0xFFFFFF, 0x00FF00, 0x00FF00);
// 単一の線でのみ使用するのが最適です
grph.lineBetween(50, 40, 250, 40);
grph.fillGradientStyle(0xFF0000, 0x0000FF, 0xFFFF00, 0x00FFFF);
// 長方形と三角形でのみ使用するのが最適です
grph.fillTriangle(150, 100, 250, 200, 50, 200);
grph.fillRect(50, 250, 200, 150,);
「角の丸い四角形」のクラス・メソッドの自作
上で紹介した描画メソッドと図形クラスの対応表ですが、「角の丸い四角形」だけはクラスが用意されておらず、描画する際にはGraphicsクラスのfillRoundedRect
,strokeRoundedRect
メソッドに個別でパラメータを渡すしかありません。
しかし、他の図形と同じく専用の図形クラスと描画メソッドがあった方が都合がよさそうです。
ここではその図形クラスの自作例を紹介します。
また、そのクラスのインスタンスをパラメータとして渡て描画できるGraphics
クラスのメソッドについても作成してみます。
「角の丸い四角形」図形クラス
「角の丸い四角形」と四角形の違いは「丸の半径」の有無だけですので、四角形のPhaser.Geom.Rectangle
クラスから継承してRoundedRectangle
クラスを新たに定義します。
//Rectangleクラスから継承し、新たにRoundedRectangleクラスを定義
class RoundedRectangle extends Phaser.Geom.Rectangle {
radius: number;
constructor(x?: number, y?: number, width?: number, height?: number, radius?: number) {
super(x, y, width, height);
this.radius = radius;
return this;
};
}
Graphics
クラスの「角の丸い四角形」描画メソッド
Phaser.GameObjects.Graphics
クラスに対し拡張メソッドを宣言・定義します。
パラメータですが、上で作成したRoundedRectangle
クラスと合わせて、Rectangle
と「半径の値」を渡しても使用できるようにしてあります。
// 拡張メソッド 宣言
declare module "phaser" {
namespace GameObjects {
interface Graphics {
fillRoundedRectShape(rect: Phaser.Geom.Rectangle | RoundedRectangle, radius?: number): this;
strokeRoundedRectShape(rect: Phaser.Geom.Rectangle | RoundedRectangle, radius?: number): this;
}
}
}
// 拡張メソッド 定義
Object.defineProperty(Phaser.GameObjects.Graphics.prototype, 'fillRoundedRectShape', {
value: function (rect: Phaser.Geom.Rectangle | RoundedRectangle, radius?: number) {
this.fillRoundedRect(rect.x, rect.y, rect.width, rect.height,
rect instanceof RoundedRectangle ? rect.radius : radius);
return this;
}
});
Object.defineProperty(Phaser.GameObjects.Graphics.prototype, 'strokeRoundedRectShape', {
value: function (rect: Phaser.Geom.Rectangle | RoundedRectangle, radius?: number) {
this.strokeRoundedRect(rect.x, rect.y, rect.width, rect.height,
rect instanceof RoundedRectangle ? rect.radius : radius);
return this;
}
});
最後に、上で定義したクラスなどの使用例です。
const WIDTH = 200;
const HEIGHT = 100;
const LINE_WIDTH = 5; // ラインの太さ
const RADIUS = 40; // 角の円の半径
let posX = 50;
let posY = 50;
const grph = this.add.graphics({
lineStyle: { width: LINE_WIDTH, color: 0xE00000, alpha: 1 },
fillStyle: { color: 0xFFFFFF, alpha: 1 }
});
const rect = new Phaser.Geom.Rectangle(posX, posY, WIDTH, HEIGHT);
grph.fillRoundedRectShape(rect, RADIUS).strokeRoundedRectShape(rect, RADIUS);
posY += 150;
const rndRect = new RoundedRectangle(posX, posY, WIDTH, HEIGHT, RADIUS);
grph.fillRoundedRectShape(rndRect).strokeRoundedRectShape(rndRect);
Graphics
クラスのマウス操作
Graphics
クラスの描画範囲をマウスで操作できるようにする場合、
単にsetInteractive
メソッドを呼んだだけでは有効になりません。
以下の例のように引数に図形クラスを渡し、ポインタの検知範囲を明示します。
第二引数のContains
は境界内にポインタがあるかどうかをチェックするための静的メソッドです。
Rectangle
以外の場合も各図形クラスに同名のContains
メソッドがあるのでそれを指定します。
const X = 50;
const Y = 100;
const WIDTH = 100;
const HEIGHT = 50;
const grph = this.add.graphics({
fillStyle: { color: 0xFFFFFF, alpha: 1 }
});
const caption = this.add.text(X + WIDTH + 10, Y,
"左の長方形にマウスポインタをあててください",
{ padding: { x: 5, y: 10 } });
const rect = new Phaser.Geom.Rectangle(X, Y, WIDTH, HEIGHT);
grph.fillRectShape(rect);
grph.on(Phaser.Input.Events.POINTER_OVER, () => {
caption.setTint(0xFF6060);
}).on(Phaser.Input.Events.POINTER_OUT, () => {
caption.setTint(0xFFFFFF);
}
).on(Phaser.Input.Events.POINTER_DOWN, () => {
caption.setText('クリックされた!');
})
// 検知範囲を指定してマウス操作を有効にする
grph.setInteractive(rect, Phaser.Geom.Rectangle.Contains);
Discussion