Phaser3:物理ボディからエフェクトを作る②
前回に引き続きエフェクトを作ってみます。
ピクセル群の位置を調節する
前回の記事ではスプライト左上から中心(origin)までの距離分ピクセルがずれてしまっていたので、ずれを加味した座標を計算します。
let point = Phaser.Geom.Point.Clone(this.point) point.setTo(x + ((j + -source.width / 2) * Math.cos(rotate) - (i - source.height / 2) * Math.sin(rotate)), y + ((i - source.height / 2) * Math.cos(rotate) + (j - source.width / 2) * Math.sin(rotate)))
さらにピクセルにtweenを設定します。
this.tweens.add({ targets: point, x: Phaser.Math.Between(0,this.sys.canvas.width), y: Phaser.Math.Between(0, this.sys.canvas.height), ease: 'Power1', duration: 300, onComplete: () => { this.geomPoints.splice(this.geomPoints.indexOf(point), 1) }, });
実行結果がこちらです。
墨をばらまいたようなエフェクトができました。
GraphicsSubをclearしていないので軌跡が残ってしまっているようです。
これはこれで面白いのですが求めているものとは違う感じがするのでupdateでclearを行うようにし、飛散する範囲も狭めました。
さらにTweenも見直し、ポイントを縮小して飛沫の減衰を表現したいのでFillRectShapeを用いるようにします。
pointはvector3で定義しなおし、zの値をポイントのスケールとして扱うようにしました。
} create() { this.graphicsSub = this.add.graphics(); this.point = new Phaser.Math.Vector3(0, 0,0); this.matter.world.setBounds(); this.cameras.main.setBackgroundColor(0xffffff); this.graphics = this.add.graphics(); this.graphics.fillStyle(0x000000); let textures = ["bar", "block1", "block2", "block3", "rect"] let rand = ["bar", "block1", "block2", "block3", "rect"] this.obj = this.CreateVerts(Phaser.Math.Between(0, this.sys.canvas.width), Phaser.Math.Between(0, this.sys.canvas.height / 2), rand[Phaser.Math.FloorTo(Phaser.Math.Between(0, rand.length - 1))]) this.obj.setVelocity(6, 3); this.obj.setAngularVelocity(0.01); this.obj.setBounce(1); this.obj.setFriction(0, 0, 0); } update() { let rand = ["bar", "block1", "block2", "block3", "rect"] if (this.input.activePointer.isDown) { this.GetPixel(this.obj.x, this.obj.y, this.obj.rotation, this.obj.texture.key) this.obj.destroy(); this.obj = this.CreateVerts(Phaser.Math.Between(0, this.sys.canvas.width), Phaser.Math.Between(0, this.sys.canvas.height / 2), rand[Phaser.Math.FloorTo(Phaser.Math.Between(0, rand.length - 1))]) this.obj.setAngularVelocity(1); } this.graphicsSub.clear(); for (let p of this.geomPoints) { this.graphicsSub.fillRect(p.x, p.y , p.z,p.z) } } public GetPixel(x: number, y: number, rotate:number,key: string) { let source = this.textures.get(key).getSourceImage() this.graphicsSub.clear(); for (let i = 0; i < source.height; i++) { for (let j = 0; j < source.width; j++) { let pixel = this.textures.getPixel(j, i, key, 0) if (pixel.alpha > 0) { let color = new Phaser.Display.Color(); color.setTo(0, 0, 0) let point = this.point.clone(); point.set(x + ((j -source.width / 2) * Math.cos(rotate) - (i - source.height / 2) * Math.sin(rotate)), y + ((i - source.height / 2) * Math.cos(rotate) + (j - source.width / 2) * Math.sin(rotate)),1.5) let randompoint = this.point.clone() randompoint.x = Phaser.Math.Between(x - source.width / 2, x + source.width / 2) randompoint.y = Phaser.Math.Between(y - source.height / 2, y + source.height / 2) this.tweens.add({ targets: point, x: Phaser.Math.Between(0, this.sys.canvas.width), y: Phaser.Math.Between(0, this.sys.canvas.height), z:0, ease: 'Power1', duration: 150* Phaser.Math.Distance.Between(point.x,point.y,randompoint.x,randompoint.y), onComplete: () => { this.geomPoints.splice(this.geomPoints.indexOf(point), 1) }, }); this.geomPoints.push(point) } } } }
実行結果がこちらです。
粉砕エフェクトができていると思います。
今回のソースはこちらです。
/// <reference path="../app.ts" /> namespace MyGame { interface XY { x: number; y: number; } export class Vert { public point: Phaser.Math.Vector2; public next1: Phaser.Math.Vector2; public next2: Phaser.Math.Vector2; constructor(p: Phaser.Math.Vector2, n1: Phaser.Math.Vector2, n2: Phaser.Math.Vector2, ) { this.point = p; this.next1 = n1; this.next2 = n2; } } export class MyScene1 extends Phaser.Scene { //グラフィックオブジェクトを用意 public player: Phaser.GameObjects.Image; public beamGroup: Phaser.GameObjects.Group; public graphics: Phaser.GameObjects.Graphics; public graphicsSub: Phaser.GameObjects.Graphics; public points: Phaser.Math.Vector2[] = []; public closepath: Phaser.Math.Vector2[] = []; public pixels: number[][]; public imageData: ImageData; public pixelsCopy: number[][]; public border: Phaser.Math.Vector2[] = []; public verts: { [key: string]: Phaser.Math.Vector2[] } = {}; public canvas: Phaser.Textures.CanvasTexture; public obj: Phaser.Physics.Matter.Sprite; public point: Phaser.Math.Vector3; public geomPoints:Phaser.Math.Vector3[]=[] constructor() { super({ key: 'MyScene1', active: false }); } preload() { this.load.image("bar", "assets/images/bar.png"); this.load.image("block1", "assets/images/block1.png"); this.load.image("block2", "assets/images/block2.png"); this.load.image("block3", "assets/images/block3.png"); this.load.image("rect", "assets/images/rect32.png"); this.load.image("pixel", "assets/images/2x2.png"); } public CreateVerts(x: number, y: number, texture: string) { this.points = []; this.closepath = []; this.border = []; //phaserのテクスチャマネージャからsourceimageを取得する let source = this.textures.get(texture).getSourceImage();; if (this.verts[texture] === undefined) { //canvasを作成しテクスチャを描画 this.canvas = this.textures.createCanvas(texture + "_canvas", source.width, source.height); this.canvas.clear() this.canvas.draw(0, 0, source as HTMLImageElement); //imagedataを取得 this.imageData = this.canvas.imageData; this.pixels = new Array(); this.pixelsCopy = new Array(); //ピクセルを配列に入れる for (let i = 0; i < this.canvas.imageData.data.length; i += 4) { let p = [this.canvas.imageData.data[i], this.canvas.imageData.data[i + 1], this.canvas.imageData.data[i + 2], this.canvas.imageData.data[i + 3]] this.pixels.push(p); } //ピクセルを配列にいれなおす for (let i = 0; i < this.imageData.height; i++) { this.pixelsCopy[i] = new Array(); for (let j = 0; j < this.imageData.width; j++) { this.pixelsCopy[i].push(0) } } let xx = 0; let yy = 0; //アルファ値の取得 for (let i = 0; i < this.pixels.length; i++) { this.pixelsCopy[yy][xx] = this.pixels[i][3] xx++; if (xx === source.width) { xx = 0; yy++; } } let origin = 0; let tmp = new Phaser.Math.Vector2(0); while (this.pixels[origin][3] === 0) { origin++; } let mov = new Phaser.Math.Vector2(0, 0); tmp = this.GetBoarder(new Phaser.Math.Vector2(origin, 0), mov); this.border.push(new Phaser.Math.Vector2(origin, 0)) let prevmov = new Phaser.Math.Vector2(mov.x, mov.y); while (!(tmp.x === origin && tmp.y === 0)) { tmp = this.GetBoarder(tmp, mov); if (!(prevmov.x === mov.x && prevmov.y === mov.y)) { this.border.push(new Phaser.Math.Vector2(tmp.x, tmp.y)); } prevmov.set(mov.x, mov.y); } //取得した輪郭からverticesを作る let verts = "" for (let p of this.border) { verts += (p.x + x) + " " + (p.y + y) + " " } var poly = this.add.polygon(x, y, verts, 0x0000ff, 0.2); this.verts[texture] = this.border; return this.matter.add.sprite(x, y, texture, 0, { shape: { type: 'fromVerts', verts: verts, flagInternal: false }, label: texture }).setTint(0x05FBFF, 0x1E00FF0); } else { let verts = "" for (let p of this.verts[texture]) { verts += (p.x + x) + " " + (p.y + y) + " " } return this.matter.add.sprite(x, y, texture, 0, { shape: { type: 'fromVerts', verts: verts, flagInternal: false }, label: texture}).setTint(0x05FBFF, 0x1E00FF0); } } create() { this.graphicsSub = this.add.graphics(); this.point = new Phaser.Math.Vector3(0, 0,0); this.matter.world.setBounds(); this.cameras.main.setBackgroundColor(0xffffff); this.graphics = this.add.graphics(); this.graphics.fillStyle(0x000000); let textures = ["bar", "block1", "block2", "block3", "rect"] let rand = ["bar", "block1", "block2", "block3", "rect"] this.obj = this.CreateVerts(Phaser.Math.Between(0, this.sys.canvas.width), Phaser.Math.Between(0, this.sys.canvas.height / 2), rand[Phaser.Math.FloorTo(Phaser.Math.Between(0, rand.length - 1))]) this.obj.setVelocity(6, 3); this.obj.setAngularVelocity(0.01); this.obj.setBounce(1); this.obj.setFriction(0, 0, 0); } update() { let rand = ["bar", "block1", "block2", "block3", "rect"] if (this.input.activePointer.isDown) { this.GetPixel(this.obj.x, this.obj.y, this.obj.rotation, this.obj.texture.key) this.obj.destroy(); this.obj = this.CreateVerts(Phaser.Math.Between(0, this.sys.canvas.width), Phaser.Math.Between(0, this.sys.canvas.height / 2), rand[Phaser.Math.FloorTo(Phaser.Math.Between(0, rand.length - 1))]) this.obj.setAngularVelocity(1); } this.graphicsSub.clear(); for (let p of this.geomPoints) { this.graphicsSub.fillRect(p.x, p.y , p.z,p.z) } } public GetPixel(x: number, y: number, rotate:number,key: string) { let source = this.textures.get(key).getSourceImage() this.graphicsSub.clear(); for (let i = 0; i < source.height; i++) { for (let j = 0; j < source.width; j++) { let pixel = this.textures.getPixel(j, i, key, 0) if (pixel.alpha > 0) { let color = new Phaser.Display.Color(); color.setTo(0, 0, 0) let point = this.point.clone(); point.set(x + ((j -source.width / 2) * Math.cos(rotate) - (i - source.height / 2) * Math.sin(rotate)), y + ((i - source.height / 2) * Math.cos(rotate) + (j - source.width / 2) * Math.sin(rotate)),1.5) let randompoint = this.point.clone() randompoint.x = Phaser.Math.Between(x - source.width / 2, x + source.width / 2) randompoint.y = Phaser.Math.Between(y - source.height / 2, y + source.height / 2) this.tweens.add({ targets: point, x: Phaser.Math.Between(0, this.sys.canvas.width), y: Phaser.Math.Between(0, this.sys.canvas.height), z:0, ease: 'Power1', duration: 150* Phaser.Math.Distance.Between(point.x,point.y,randompoint.x,randompoint.y), onComplete: () => { this.geomPoints.splice(this.geomPoints.indexOf(point), 1) }, }); this.geomPoints.push(point) } } } } //衝突を処理するための関数 public GetCollide(body): Phaser.Physics.Arcade.Body { if (body.parent === body) { return body; } while (body.parent !== body) { body = body.parent; } return body; } public GetBoarder(v: Phaser.Math.Vector2, m: Phaser.Math.Vector2): Phaser.Math.Vector2 { let r: Phaser.Math.Vector2 = null; //左下のピクセルから調べていく let dir1 = () => { if (v.x > 0 && v.y < this.imageData.height - 1) { if (this.pixelsCopy[v.y + 1][v.x - 1] !== 0) { m.set(-1, 1); return new Phaser.Math.Vector2(v.x - 1, v.y + 1); } } return null; } //下 let dir2 = () => { if (v.y < this.imageData.height - 1 && !(m.x == 0 && m.y == -1)) { if (this.pixelsCopy[v.y + 1][v.x] !== 0) { m.set(0, 1); return new Phaser.Math.Vector2(v.x, v.y + 1); } } return null; } //右下 let dir3 = () => { if (v.x < this.imageData.width - 1 && v.y < this.imageData.height - 1) { if (this.pixelsCopy[v.y + 1][v.x + 1] !== 0) { m.set(1, 1); return new Phaser.Math.Vector2(v.x + 1, v.y + 1); } } return null; } //右 let dir4 = () => { if (v.x < this.imageData.width - 1) { if (this.pixelsCopy[v.y][v.x + 1] !== 0) { m.set(1, 0); return new Phaser.Math.Vector2(v.x + 1, v.y); } } return null; } //右上 let dir5 = () => { if (v.x < this.imageData.width - 1 && v.y > 0) { if (this.pixelsCopy[v.y - 1][v.x + 1] !== 0) { m.set(1, -1); return new Phaser.Math.Vector2(v.x + 1, v.y - 1); } } return null; } //上 let dir6 = () => { if (v.y > 0) { if (this.pixelsCopy[v.y - 1][v.x] !== 0) { m.set(0, -1); return new Phaser.Math.Vector2(v.x, v.y - 1); } } return null; } //左上 let dir7 = () => { if (v.x > 0 && v.y > 0) { if (this.pixelsCopy[v.y - 1][v.x - 1] !== 0) { m.set(-1, -1); return new Phaser.Math.Vector2(v.x - 1, v.y - 1); } } return null; } //左 let dir8 = () => { if (v.x > 0) { if (this.pixelsCopy[v.y][v.x - 1] !== 0) { m.set(-1, 0); return new Phaser.Math.Vector2(v.x - 1, v.y); } } return null; } if (m.x === -1 && m.y === 1) { r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } r = dir5(); if (r !== null) { return r; } r = dir6(); } if (m.x === 0 && m.y === 1) { r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } } if (m.x === 1 && m.y === 1) { r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } } if (m.x === 1 && m.y === 0) { r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } } if (m.x === 1 && m.y === -1) { r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } if (r !== null) { return r; } } if (m.x === 0 && m.y === -1) { r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } } if (m.x === -1 && m.y === -1) { r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } } if (m.x === -1 && m.y === 0) { r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } } if (m.x === 0 && m.y === 0) { r = dir1(); if (r !== null) { return r; } r = dir2(); if (r !== null) { return r; } r = dir3(); if (r !== null) { return r; } r = dir4(); if (r !== null) { return r; } r = dir5(); if (r !== null) { return r; } r = dir6(); if (r !== null) { return r; } r = dir7(); if (r !== null) { return r; } r = dir8(); if (r !== null) { return r; } } return null; } } }