babylon.js ステップ14 パーティクル表現
babylon.js を覚えようシリーズ ステップ14はパーティクル表現です。
Three.jsでは自作で頑張るわけですが、babylon.jsではパーティクルシステムが備わっていますので、簡単にパーティクル表現が可能です
いきましょう!
パーティクルの作り方
パーティクルシステム
パーティクルシステムを用いたパーティクル表現の例はこちらです。
コード中に説明を追加していますので順番にみていきましょう
コード例は以下です。
var createScene = function () { var scene = new BABYLON.Scene(engine); // ライトやカメラなど環境セッティング var light0 = new BABYLON.PointLight("Omni", new BABYLON.Vector3(-10, 7, -10), scene); var camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 1, 0.8, 20, new BABYLON.Vector3(0, 0, 0), scene); camera.attachControl(canvas, true); // パーティクルの吹き出し口のオブジェクト作成 var fountain = BABYLON.Mesh.CreateBox("foutain", 1.0, scene); // 地面作成 var ground = BABYLON.Mesh.CreatePlane("ground", 50.0, scene); ground.position = new BABYLON.Vector3(0, -15, 0); ground.rotation = new BABYLON.Vector3(Math.PI / 2, 0, 0); ground.material = new BABYLON.StandardMaterial("groundMat", scene); ground.material.backFaceCulling = false; ground.material.diffuseColor = new BABYLON.Color3(0.3, 0.3, 1); // パーティクルシステムの作成 var particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene); //パーティクルに貼るテクスチャ定義 particleSystem.particleTexture = new BABYLON.Texture("textures/flare.png", scene); // パーティクルの出る(emit:放出)ポイントをVector3で定義 mn~maxで定義可能 particleSystem.emitter = fountain; // the starting object, the emitter particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0); // Starting all from particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0); // To... // パーティクルの色 2種類と消えゆくときの色 particleSystem.color1 = new BABYLON.Color4(0.7, 0.8, 1.0, 1.0); particleSystem.color2 = new BABYLON.Color4(0.2, 0.5, 1.0, 1.0); particleSystem.colorDead = new BABYLON.Color4(0, 0, 0.2, 0.0); // パーティクル ランダムで min〜maxに particleSystem.minSize = 0.1; particleSystem.maxSize = 0.5; // パーティクルのライフタイム設定 ランダムでmin~max particleSystem.minLifeTime = 0.3; particleSystem.maxLifeTime = 1.5; // 放出(emit)率 particleSystem.emitRate = 1500; // ブレンドモード : BLENDMODE_ONEONE, or BLENDMODE_STANDARD particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE; // 重力設定 particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0); // エミット(放出)後のパーティクルの方向 particleSystem.direction1 = new BABYLON.Vector3(-7, 8, 3); particleSystem.direction2 = new BABYLON.Vector3(7, 8, -3); // 角速度(ラジアン) particleSystem.minAngularSpeed = 0; particleSystem.maxAngularSpeed = Math.PI; // スピード particleSystem.minEmitPower = 1; particleSystem.maxEmitPower = 3; particleSystem.updateSpeed = 0.005; // Particle Systemをスタート particleSystem.start(); // 吹き出すアニメーション var keys = []; var animation = new BABYLON.Animation("animation", "rotation.x", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE); // アニメーション設定 フレーム0, valueは拡大率 "1" keys.push({ frame: 0, value: 0 }); // フレーム50, 拡大率 "0.2" keys.push({ frame: 50, value: Math.PI }); // フレーム 100, 拡大率 "1" keys.push({ frame: 100, value: 0 }); // アニメーションスタート animation.setKeys(keys); fountain.animations.push(animation); scene.beginAnimation(fountain, 0, 100, true); return scene; }
動いている例はこちら
www.babylonjs-playground.com
解説
この部分で パーティクルシステム(Particle System)を作成しています
// パーティクルシステムの作成 var particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene); //パーティクルに貼るテクスチャ定義 particleSystem.particleTexture = new BABYLON.Texture("textures/flare.png", scene); // パーティクルの出るポイントをVector3で定義 mn~maxで定義可能 particleSystem.emitter = fountain; // the starting object, the emitter particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0); // Starting all from particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0); // To...
2000というのは、表示可能な最大数の設定値です。
さらに、パーティクルシステムには、パーティクルが見えるようにするためのテクスチャと、開始点からのパーティクルの位置と広がりを設定する「エミッタ(emitter)」も必要です。
パーティクルのスタート
particleSystem.start();
パーティクルの停止
particleSystem.stop();
リセット方法
particleSystem.reset()
もし遅延起動させたい場合には
particleSystem.startDelay = 3000;
なんてこともできます。
タイマーを設定して、自動的に勝手に止めることもできます
particleSystem.targetStopDuration = 5; //自動停止 particleSystem.disposeOnStop = true; //パーティクルシステムを破棄し、メモリを開放
パーティクルのテクスチャマスク
particleSystem.textureMask = new BABYLON.Color4(0.1, 0.8, 0.8, 1.0);
テクスチャにマスクを適用して、一部の色をフィルタリングしたり、アルファチャネルの一部をフィルタリングしたりすることもできます。
パーティクルエミッタ
パーティクルエミッタはメッシュだけでなく、任意のVector3でもOKです。
particleSystem.emitter = new BABYLON.Vector3(-1, 2, 3);
これで任意の点から、パーティクルを放出できます。
ローカル空間
エミッタがメッシュのとき、isLocal設定で、パーティクルをメッシュの座標系に依存させることができます。
particleSystem.isLocal = true
場所と広がり
minEmit maxEmitを設定することで、放出場所を設定します。
particleSystem.minEmitBox = new BABYLON.Vector3(-2, -3, 4); particleSystem.maxEmitBox = new BABYLON.Vector3(4, 2, 3);
シンプルな例がこちら
ただテクスチャを貼ったパーティクルを数個だし、漂流して消えます。
var createScene = function () { var scene = new BABYLON.Scene(engine); // Setup environment var light0 = new BABYLON.PointLight("Omni", new BABYLON.Vector3(0, 2, 8), scene); var camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 1, 0.8, 20, new BABYLON.Vector3(0, 0, 0), scene); camera.attachControl(canvas, true); // Create a particle system var particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene); //Texture of each particle particleSystem.particleTexture = new BABYLON.Texture("textures/flare.png", scene); // Where the particles come from particleSystem.emitter = new BABYLON.Vector3(0, 0, 0); // the starting object, the emitter particleSystem.minEmitBox = new BABYLON.Vector3(-0.5, -0.5, -0.5); // Starting all from particleSystem.maxEmitBox = new BABYLON.Vector3(0.5, 0.5, 0.5); // To... // Start the particle system particleSystem.start(); return scene; }
ライフタイム
パーティクルの生存期間を設定できます
// Life time of each particle (random between...)
particleSystem.minLifeTime = 0.3;
particleSystem.maxLifeTime = 1.5;
また、高度な設定方法もあります。
system.targetStopDuration = 0.5など停止が決まっているとき
particleSystem.addLifeTimeGradient(0, 0.5); //初期のパーティクルはLife time 0.5 particleSystem.addLifeTimeGradient(1, 0); // 最後のパーティクルはLife time 0
第一引数の0は、最初。1は終わりの時を意味します。
乱数をとりたい場合
particleSystem.addLifeTimeGradient(0, 0.5, 0.8); particleSystem.addLifeTimeGradient(1.0, 0, 0.1);
このように,第二引数と第三引数の間で乱数をとります
サイズ
// Size of each particle (random between...) particleSystem.minSize = 0.1; particleSystem.maxSize = 0.5;
このような書き方もできる
// Scale of each particle (random between...) particleSystem.minScaleX = 0.1; particleSystem.maxScaleX = 0.5; particleSystem.minScaleY = 0.2; particleSystem.maxScaleY = 0.4;
時間経過を反映した場合(要:targetStopDuration)
particleSystem.addStartSizeGradient(0, 10); particleSystem.addStartSizeGradient(1.0, 500);
色
particleSystem.color1 = new BABYLON.Color4(0.7, 0.8, 1.0, 1.0); particleSystem.color2 = new BABYLON.Color4(0.2, 0.5, 1.0, 1.0); particleSystem.colorDead = new BABYLON.Color4(0, 0, 0.2, 0.0);
グラデーションもできます
particleSystem.addColorGradient(0, new BABYLON.Color4(1, 1, 1, 0)); ||> 開始時、終了時設定あり >|| particleSystem.addColorGradient(0, new BABYLON.Color4(1, 1, 1, 0)); particleSystem.addColorGradient(1.0, new BABYLON.Color4(1, 1, 1, 1));
ブレンドモード
BLENDMODE_ONEONEがデフォルトであり、blendMode指定されていない場合に使用されます。
- BLENDMODE_ONEONE -色は結果に影響を与えるアルファなしで追加されます
- BLENDMODE_STANDARD- カラーは、パーティクルのアルファを使用して追加されます(つまり、カラー(1-アルファ)+パーティクルカラーアルファ)
- BLENDMODE_ADD -色が追加されますが、粒子の色のみが粒子のアルファを使用します(つまり、色+ particleColor *アルファ)
例はこちら
https://www.babylonjs-playground.com/#MX2Z99#8
- BLENDMODE_MULTIPLY-色が乗算され、(1-アルファ)に追加されます(つまり、色*パーティクルカラー+ 1-アルファ)
例はこちら
https://playground.babylonjs.com/#KUDH9F#1
- BLENDMODE_MULTIPLYADD- BLENDMODE_MULTIPLY後に BLENDMODE_ADD
例はこちら(スペースキーを押すと爆発表現のパーティクルが放出されます)
https://www.babylonjs-playground.com/#VS5XS7#0
rate
rateは1秒あたりのパーティクルの放出数を設定します
後ほど、設定値を手動設定もできます
particleSystem.manualEmitCount = 300;
時間経過を考慮したGradient設定もできます
particleSystem.addEmitRateGradient(0, 10); particleSystem.addEmitRateGradient(1.0, 500);
放出方向
2つの方向を指定できます。
一方向のみを指定した場合、パーティクルは指定された一般的な方向にランダムに放出されます。
2つ指定されている場合、パーティクルは2つの方向の間を移動します。
particleSystem.direction1 = new BABYLON.Vector3(-7, 8, 3); particleSystem.direction2 = new BABYLON.Vector3(7, 8, -3);
重力
//Set the gravity of all particles (not necessarily down) particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);
回転
パーティクルのZ軸を中心として、角速度の範囲をラジアン/秒で定義できます。
particleSystem.minAngularSpeed = 0; particleSystem.maxAngularSpeed = Math.PI;
次のように初期回転角度も定義できます
particleSystem.minInitialRotation = 0; particleSystem.maxInitialRotation = Math.PI;
速度
放出するパーティクルのパワーと全体的なモーション速度の範囲を定義できます
0.01がデフォルト。(更新が速い=アニメーションが速い)
particleSystem.minEmitPower = 1; particleSystem.maxEmitPower = 3; particleSystem.updateSpeed = 0.005;
Gradient設定で徐々に速度を変えることもできます
速度リミット
時間の経過に伴う速度の制限を定義できます。
制限に達した場合、速度に係数が適用されます。
particleSystem.addLimitVelocityGradient(0, 0.5); particleSystem.addLimitVelocityGradient(1.0, 3);
例はこちら
抗力係数
空気摩擦をシミュレートするために使用されます。
たとえば、ドラッグ係数が0.8に設定されている場合、粒子の方向の20%だけが粒子の位置に追加されます。
particleSystem.addDragGradient(0, 0.5); particleSystem.addDragGradient(1.0, 3);
例はこちら
整列
例はこちら
https://www.babylonjs-playground.com/#EV0SEQ
このようんy軸を揃えて整列させると
system.billboardMode = BABYLON.ParticleSystem.BILLBOARDMODE_Y;
こんな感じで湯気を表現できる
https://www.babylonjs-playground.com/#B9HKG0#0
まとめ
パーティクルシステムを用いて、様々なパーティクル表現を学びました。
パーティクルのエミッタ(放出元)の設定や、パーティクルの放出方向、色、ブレンドモード、速度、数、重力設定などなど、事細かに設定できることがわかりました。
ここでは紹介しませんでしたがパフォーマンスに問題がある際には、WebGL2を用いたパーティクル作成機能などもあるので、ドキュメントを読んでみてください。
次回は、環境設定に入ります
babylon.js ステップ13 スプライト(Sprites)
babylon.js ステップ13はスプライトアニメーションです。
2Dではおなじみのこの技術。
もちろんbabylon.jsでも使えます
いきましょう!
- 2つのスプライト管理方法
- スプライトマネージャー
- Picking(ピッキング)
- スプライトパックマネージャー(バージョン4.1から利用可能)
- スプライトアニメーション方法
- スプライトマップ
- タイル表現に便利なスプライトマップタイル
- まとめ
2つのスプライト管理方法
スプライトは大量の2D画像を利用するため、パフォーマンス管理が重要です。
babylon.jsでは2つの管理方法があります
- A uniform spritesheet (均一化されたスプライト)・・・SpriteManagerを利用
- A packed spritesheet(パッケージされたスプライト)・・・SpritePackedManagerを利用
これらは、1枚のスプライトであってもManagerを使う必要があります。
スプライトマネージャー
// 木の画像用のsprite managerの作成 var spriteManagerTrees = new BABYLON.SpriteManager("treesManager", "Assets/Palm-arecaceae.png", 2000, 800, scene);
treesManagerの部分は名前なのでユニークな名前を設定します。
「Assets/Palm-arecaceae.png」の木の画像を最大2000個作ることができる容量を確保します。
800の部分は画像のサイズに対応するセルサイズです。
最後に追加するsceneを設定します。
// プレイヤー用のsprite managerの作成 var spriteManagerPlayer = new BABYLON.SpriteManager("playerManager","Assets/Player.png", 2, {width: 64, height: 64}, scene);
上記では、セルサイズを[64x64]で設定しています。
Picking(ピッキング)
次はピッキング(選択)を行います
完成図
https://www.babylonjs-playground.com/#9RI8CG#0
やり方
- 必要なスプライトの選択をオンにします。 sprite.isPickable = true;
- SpriteManagerがピッキングをサポートできるようにします。 spriteManager.isPickable = true;
// 選択されたスプライトを傾ける var pickResult = scene.pickSprite(this.pointerX, this.pointerY); if (pickResult.hit) { pickResult.pickedSprite.angle += 0.5; }
sceneのpickSprite関数を使って、マウスポインタの選択を取得します。
そして、それがhit(衝突)している場合(ほぼ間違いなくhitしていますが)、スプライト画像を傾けます
multiPickSpriteを使用して、貫通系の複数選択も可能です
var pickResult = scene.multiPickSprite(this.pointerX, this.pointerY); for (var i = 0; i < pickResult.length; i++) { pickResult[i].pickedSprite.angle += Math.PI / 4; }
スプライトパックマネージャー(バージョン4.1から利用可能)
パックされたスプライトシートを用いる場合は、SpritePackedManagerを利用します
これは、JSONファイル(位置を含む)と画像ファイルが必要です。
// SpritePackedManager(名前,画像,インスタンス最大数,シーン) var spm = new BABYLON.SpritePackedManager("spm", "pack1.png", 4, scene);
画像の例は次のような感じです。1枚の画像データに複数の画像を詰め込みます
jsonファイルの中身はこんな感じで、その画像の始まりの座標と、横幅、縦幅でできています。
{ "frames": { "eye.png": { "frame": {"x":0,"y":148,"w":400,"h":400} }, "redman.png": { "frame": {"x":0,"y":0,"w":55,"h":97} }, "spot.png": { "frame": {"x":199,"y":0,"w":148,"h":148} }, "triangle.png": { "frame": {"x":55,"y":0,"w":144,"h":72} } } }
1枚の画像なのに、擬似的に複数の画像として扱うことができます。
var sprite = new BABYLON.Sprite("sprite", spm); sprite.cellRef = "spot.png";
そして、このように該当画像を選んで使用できます
スプライトの設定
sprite.size = 0.3; //大きさ sprite.angle = Math.PI/4; //向き sprite.invertU = -1; // 反射 sprite.width = 0.3; //幅 sprite.height = 0.4; //高さ sprite.position.y = -0.3; //ポジション
スプライトアニメーション方法
スプライトの利点のアニメーションが簡単な事です。
均一なスプライトシートと(SpriteManager)を使用することです。
すべてのアニメーション画像を含む1つの大きな画像ファイルを次々にロードします。
マネージャーで指定したピクセルサイズは均一であることに注意してください。
たとえば、次のプレーヤーのスプライトシートでは、40以上のアクションを定義しています
使い方:
// フレーム0からフレーム43までアニメーション化 // trueはループするかどうか // 100はフレーム間の遅延時間 player.playAnimation(0, 43, true, 100);
スプライトマップ
数千以上のアニメーションが必要な場合にはスプライトマップの利用を検討してください
spriteMapを利用すると情報をメモリに保存して高速な演算を可能にするようです。
var spriteMap = new BABYLON.SpriteMap(name, atlasJSON, spriteTexture, options, scene);
タイル表現に便利なスプライトマップタイル
マインクラフトの如く、同じサイズのタイルを使いたい場合にはスプライトマップタイルが便利です。
spriteMap.changeTiles(layerID, tileID, frameID)
- layerID:システムのターゲットレイヤーの整数。
- tileID:Vector2 | レイヤーのターゲットタイルのVector2 []。
- frameID:変更するスプライトの整数フレームID。
タイル情報の保存やロード、アニメーション機能などもあります
spriteMap.saveTileMaps() // セーブ .tilemaps形式 spriteMap.saveTileMaps(url) //ロード // アニメーション spriteMap.addAnimationToTile(frameID, animationFrame, nextFrameID, animationFrameDisplayTiming, globalSpeed)
まとめ
スプライト表現について学びました。
babylon.jsでスプライト表現を行うためにはManagerを使う必要があり、全て同じピクセルの画像ならば、SpriteManagerで、異なるサイズの画像を扱うなら SpritePackedManagerを使います。
マネージャを定義したなら、Spriteインスタンスをnew BABYLON.Spriteで呼び出し
メッシュのように自由にサイズや位置などを設定できます。
また、アニメーションを使う場合にはplayAnimation関数でできました。
タイル表現には、SpriteMapが便利であることを学びました。
以上になります。
次回はパーティクル表現をやります!
babylon.js ステップ12 レイキャスト(Raycasts)
babylon.jsのチュートリアルシリーズ 12回目
今回はレイキャストをやっていきます
いきましょう!
レイキャストとは光線による衝突判定
前回の11回ではマウスの位置と、壁の衝突判定を行いましたが。
レイキャストは、光線(レーザー・)による衝突または、交差チェックを行う機能です。
FPSゲームの弾丸の軌道なんかはレイキャストで行います。
マウスクリックで、選択されたメッシュ情報を取得する方法(scene.pick)を使用しました。
一方レイキャストでは似たようなメソッド、 scene.pickWithRay() を使っていきます
光線と最初に接したメッシュの取得
光線と最初に接したメッシュの取得をやっていきます。
真ん中に、光線を出力する赤い箱があります(箱はマウスで動かせる)
そして、その光線と接する可能性のある、青い箱が2つと緑の箱があります。
なお、奥の青い箱は、光線が届きません。
光線が当てられた箱は上に伸びていきます。
デモはこちら
https://www.babylonjs-playground.com/#KNE0O#84
まず、最初に光線の出力もとである赤い箱には、衝突検出をoffにする設定を入れます。赤い箱自体が衝突判定されてしまっては困りますので。
box.isPickable = false;
次に、箱から出る光線のベクトルを定義します。その際,箱の位置()を差し引いて箱の原点origin(光線が始まる場所)からの方向を取得します。
// 赤い箱との方向ベクトル var forward = new BABYLON.Vector3(0,0,1); forward = vecToLocal(forward, box); // 箱の方向 var direction = forward.subtract(origin); direction = BABYLON.Vector3.Normalize(direction);
vecToLocalは箱からの方向を取得する関数です。Normalizeは正規化(長さを1に)する作業です。
そして、取得した単位ベクトルから任意の長さの光線を作成します。
var ray = new BABYLON.Ray(origin, direction, length);
var hit = scene.pickWithRay(ray);
あとは、衝突したメッシュのyを増やしてあげれば良いです
if (hit.pickedMesh) { hit.pickedMesh.scaling.y += 0.01; }
Predicate関数を使用したレイキャスト
予め、衝突判定するメッシュを定義するための関数を作ることができます。
// boxとbox2は衝突判定をしない,それ以外はする function predicate(mesh){ if (mesh == box2 || mesh == box){ return false; } return true; }
そして、pickWithRayの時に、Predicateを渡してあげます。こうすることで、個別にxxx.isPickable = false; のおような設定を省略できます。
scene.pickWithRay(ray, predicate);
例はこちら。手前の青い箱のPickableをfalseにしているので、後ろの箱に衝突判定があります
https://www.babylonjs-playground.com/#KNE0O#18
マルチピック
scene.multiPickWithRay光線を最初の障害物で停止させたくない場合は、次のように使用できます。
https://www.babylonjs-playground.com/#KNE0O#19
var hits = scene.multiPickWithRay(ray);
hit算出をmultiPickWithRayに変えることで、貫通する衝突判定が可能です。
multiPickWithRayの戻り値は配列になっていることに注意してください.
そのほかRayクラスを直接利用することで、ここのメッシュの交差判定をしたり
Ray.intersectsMesh(mesh, fastCheck) → PickingInfo
ローカルスペースへ変更したりできます
Ray.Transform(ray, matrix) → Ray
Picking Ray
カメラから無限遠に向かって、衝突判定(照準)することができます。
そのためにはcreatePickingRay関数を使います。
前の11ステップでは壁との衝突判定を行いましたが、任意のメッシュの場合にはこちらの方が便利そうです。
例はこちら
デバッグ用のRayHelper
デバッグ用に光線に色をつけることができます。
BABYLON.RayHelper.CreateAndShow(ray, scene, new BABYLON.Color3(1, 1, 0.1));
例はこちら
まとめ
pickWithRay() を使って光線的な衝突を検出し、そこから衝突したメッシュを取得することができました。
また、multiPickWithRay()を使えば貫通する光線を用いて一度に複数の衝突も取得できました。
また、カメラからの照準(選択)する方法としてcreatePickingRay()を用いる方法を学びました。
以上です!
長かったですが、これで衝突判定系は終わりです。
次回は2D表現のスプライトいっみます
babylon.js ステップ11 画面クリックとの衝突判定 (Picking Collisions)
babylon.js チュートリアル ステップ11 は画面クリックとのコリジョンです。
コリジョンシリーズの3回目です。
いきましょう!
3Dオブジェクトのクリック(hit)を検出
壁を表す平面と、銃創の絵が描かれた平面の2つを作成して、壁への衝突を検出し、衝突している場合は、そこに銃槍を刻むシーンをやってみます。
まず、クリックイベントで「pick」関数を使用して座標を取得します
//クリックイベント検出 window.addEventListener("click", function () { // pickでポインタ情報を取得する var pickResult = scene.pick(scene.pointerX, scene.pointerY); });
pickResultオブジェクトは、主に4つの情報で構成されています。
- hit (ブール値):クリックがシーン内のオブジェクトにヒットした場合はtrue。
- distance (float):アクティブなカメラとヒットの間の距離(メッシュにヒットしなかった場合は無限)
- pickedMesh (BABYLON.Mesh):オブジェクトにヒットしたメッシュ。hitしてない場合はnull
- pickedPoint (BABYLON.Vector3):クリックしたVector3型で返す。hitしてない場合はnull
ヒットした情報を用いて銃槍の位置をずらす
pickResultを用いて
// もしクリックが壁にhitした場合、ぶつかった画像の位置を更新する if (pickResult.hit) { impact.position.x = pickResult.pickedPoint.x; impact.position.y = pickResult.pickedPoint.y; }
babylon.js ステップ10 メッシュの交差衝突判定 (Intersect Collisions - mesh)
メッシュ同士が、それぞれ衝突を検出する方法をやります
いきましょう!!
2つのメッシュが接触しているときに衝突イベントを発生させる方法
intersectsMesh()を使うことで、対象との交差(衝突)判定を行うことが出来ます
コードは次の通り
// 平面(plan1)とballoon1との交差判定 if (balloon1.intersectsMesh(plan1, false)) { balloon1.material.emissiveColor = new BABYLON.Color4(1, 0, 0, 1); } else { balloon1.material.emissiveColor = new BABYLON.Color4(1, 1, 1, 1); }
バビロンエンジンでは、交差判定を効率よく行うために
オブジェクトの周囲に下図のように境界ボックス(バウンティングボックス)を作成し、
このボックスと衝突するメッシュの間の交差をテストします。
また、より詳細に接触を検出したい場合には第二引数をtrueにすることで、
バウンティングボックスをメッシュにより近くし、
交差判定を正確にすることが出来ます。
ただ、この操作は計算負荷が高くなるようです。
左が、falseの例、右がtrueの例です。
このタイプのバウンディングボックスは、メッシュをある角度に回転させる場合に特に役立ちます。
メッシュと1点間の接触を検出する方法
特定の1点との接触判定をする方法が、intersectsPoint()です。
使用方法は次の通り
var pointToIntersect = new BABYLON.Vector3(10, -5, 0); if (balloon3.intersectsPoint(pointToIntersect)){ balloon3.material.emissiveColor = new BABYLON.Color4(1, 0, 0, 1); }
デモはこちらです
ボールと接触すると赤色になります
まとめ
babylon.jsでは衝突判定ようの便利な関数があり
オブジェクト間ではintersectsMesh()を
オブジェクトと点ではintersectsPoint()を使うことで衝突判定を行う方法を紹介しました。
次回はマウスとの衝突判定です!
babylon.js ステップ9 カメラとメッシュの衝突判定&重力 (Cameras, Mesh Collisions and Gravity)
babylon.js を覚えようステップ9は衝突判定と重力制御です!
いやー、重力とか楽しそうですね。
メッシュだけでなく、カメラにも設定できるようです。
いきましょう!!
説明
FPS(First Person Shooter)ゲームをプレイしたことがありますか?
この回では、そのFPS的なカメラの動きをシミュレートします。
カメラは床にあり、地面と衝突しており、シーン内のオブジェクトと衝突している可能性がありますね
では、このFPS的な設定をしていきましょう!
1.重力の設定
シーンにグラビティ(重力)ベクトルをVector3型で設定します。
scene.gravity = new BABYLON.Vector3(0, -9.81, 0);
古典的には、yに重力加速度9.8を下向き(負)に設定すると良いです。
カメラへの適用は、applyGravityプロパティをtrue設定します
camera.applyGravity = true;
2.楕円体を定義する
次の重要なステップは、カメラの周りに楕円体を定義することらしいです。
この楕円体はプレーヤーの幅(プレイヤーサイズ)を表します。
外部のメッシュがこの楕円体に接触すると衝突イベントが発生し、カメラがこのメッシュに近づきすぎないようにすることで、カメラがメッシュにめり込まないようにします。
babylon.jsカメラの楕円体プロパティのデフォルトはサイズ(0.5、1、0.5)ですが、
値を変更すると、調整された軸に応じて、背が高く、大きく、小さく、薄くなります。
次の例では、カメラの楕円体をデフォルトの楕円体よりも少し大きくします。
//カメラの周りに楕円を定義 (e.g. your player's size) camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
カメラの楕円体はオフセット(Offset)され、常に視点が楕円体の上にあります。
camera.ellipsoidOffsetプロパティを更新することにより、この動作を制御できます。
計算は次のようになります。
finalPosition = position - vec3(0, ellipsoid.y, 0) + ellipsoidOffset
衝突を適用する(Apply collision)
最後のステップとして、シーン内の衝突の検知をすることを宣言します。
// Enable Collisions scene.collisionsEnabled = true; camera.checkCollisions = true;
そして、どのメッシュがカメラと衝突するか設定出来ます。
これはとても便利ですね!Three.jsではなかった設定です。
ground.checkCollisions = true; box.checkCollisions = true;
以上です!!
demoはこちら
https://www.babylonjs-playground.com/#4HUQQ
カメラは地面と衝突するまで落ちます。
また、ボックスに近づくとカメラがボックスに衝突します。
メッシュオブジェクトとメッシュオブジェクトの衝突
mesh.ellipsoidプロパティ
と
mesh.moveWithCollisions(velocity)関数
を使用して、メッシュでも同じことを行うこともできる。
この関数は、指定された速度でメッシュを移動させ、現在のメッシュとcheckCollisionsがアクティブになっているすべてのメッシュとの間に衝突がないかどうかをチェックします。
mesh.ellipsoidOffsetを使用して、メッシュ上で楕円体の中心を移動することもできます(デフォルトでは、楕円体はメッシュの中心にあります)。
var speedCharacter = 8; var gravity = 0.15; var character = ここにメッシュを設定; character.ellipsoid = new BABYLON.Vector3(0.5, 1.0, 0.5); //楕円を定義 character.ellipsoidOffset = new BABYLON.Vector3(0, 1.0, 0); //楕円オフセットを設定(上にずらす) var forwards = new BABYLON.Vector3(parseFloat(Math.sin(character.rotation.y)) / speedCharacter, gravity, parseFloat(Math.cos(character.rotation.y)) / speedCharacter); forwards.negate(); character.moveWithCollisions(forwards); //衝突判定ありの前進 // または var backwards = new BABYLON.Vector3(parseFloat(Math.sin(character.rotation.y)) / speedCharacter, -gravity, parseFloat(Math.cos(character.rotation.y)) / speedCharacter); character.moveWithCollisions(backwards); // 衝突判定ありの後進
ArcRotateCameraでの衝突
ArcRotateCameraでも衝突をチェックすることができますが、、、
障害物に沿ってスライドする代わりに、衝突が追加されてもこのカメラは移動しません。
衝突を有効にするには、camera.checkCollisions = trueを呼び出します。
次のコードで衝突半径を定義できます。
camera.collisionRadius = new BABYLON.Vector3(0.5, 0.5, 0.5)
まとめ
重力を設定する方法は、シーンに設定して、カメラのapplyGravityをtrueにするだけという。
ものすごく簡単に重力が設定できることを紹介しました。
Three.jsではもう少し複雑ですよね。
また、衝突判定には楕円を対象物の周りに作ってmoveWithCollisionsで移動させることで衝突判定が出来ます。
お次はメッシュの衝突判定をより詳しくいきます!
babylon.js ステップ8 アニメーション(Animations)
babylon.js でのステップも8個目!!
今回はアニメーションです!
アニメーションは、メッシュに動きを与えて見栄えがするシーンを作成出来ます。
いきましょう!
- アニメーションの2つの方法
- キーフレームアニメーション
- 終了を待って同期処理をする
- CreateAndStartAnimation関数
- アニメーションのブレンド
- アニメーションの重みを設定してブレンド
- イージング機能
- 複雑なアニメーション
- まとめ
アニメーションの2つの方法
アニメーション方法には2つあります。
- 予めアニメーションの動きのキーフレームを定義する方法
- 実行時にアニメーションを適用されるようにプロパティを随時変更する方法
キーフレームアニメーション
まず環境を作ります
シーン、ライト、カメラを設定してから、箱を一個置いてみます。
function createScene() { //Here... your basic scene as before: [scene, light, camera] //Create a box var box1 = BABYLON.Mesh.CreateBox("Box1", 10.0, scene); box1.position.x = -20;
アニメーションオブジェクトを定義します。
(GSAPみたいなトゥイーン的な方法ですね)
// new BABYLON.Animation(名前,変更対象(例:xの大きさ),値の型,動作タイプ); var animationBox = new BABYLON.Animation("myAnimation", "scaling.x", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
値の型は次の設定値があります
- BABYLON.Animation.ANIMATIONTYPE_FLOAT
- BABYLON.Animation.ANIMATIONTYPE_VECTOR2
- BABYLON.Animation.ANIMATIONTYPE_VECTOR3
- BABYLON.Animation.ANIMATIONTYPE_QUATERNION
- BABYLON.Animation.ANIMATIONTYPE_MATRIX
- BABYLON.Animation.ANIMATIONTYPE_COLOR3
動作タイプには次の設定があります
- BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE・・・以前の値を使いインクリメントしてループ
- BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE・・・初期値にリセットしてループ
- BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT・・・最後の値をキープしてループ
動作モードについてもう少し捕捉すると
次のように アニメーションキーを設定するとして
// 配列にキーフレームを設定(フレームは昇順でpushしないとならない!) var keys = []; //キーフレーム 0で, スケールを1 keys.push({ frame: 0, value: 1 }); //キーフレーム20で, スケールを0.2 keys.push({ frame: 20, value: 0.2 }); //キーフレーム 100で, スケールを1 keys.push({ frame: 100, value: 1 });
Vector2、Vector3、Quaternionの場合は、キーにinTangent値とoutTangent値を設定します
var keys = []; keys.push({ frame: 0, value: BABYLON.Vector3.Zero(), outTangent: new BABYLON.Vector3(1, 0, 0) }); keys.push({ frame: 20, inTangent: new BABYLON.Vector3(1, 0, 0), value: new BABYLON.Vector3(1, 1, 1), outTangent: new BABYLON.Vector3(-1, 0, 0) }); keys.push({ frame: 100, inTangent: new BABYLON.Vector3(-1, 0, 0), value: BABYLON.Vector3.Zero() });
そして、定義したアニメーション配列をアニメオブジェクトに設定します
animationBox.setKeys(keys);
そして、このアニメーションを箱に紐付けます
box1.animations = []; box1.animations.push(animationBox);
そして、実行コマンド(beginAnimation)でアニメーションが起動します
// 箱をキーフレーム0から100まで実行! scene.beginAnimation(box1, 0, 100, true);
キーフレームを入れ替えて逆に実行も可能!
scene.beginAnimation(box1, 100, 0, true);
beginAnimationには、そのほかスピード設定などなど、いろいろ設定できるのドキュメントを確認してみてくださいな
アニメーションを止める方法
実行した後に
var newAnimation = scene.beginAnimation(box1, 0, 100, true);
pauseで止められます
newAnimation.pause();
そのほか、restart(),stop(),reset()もあります
終了を待って同期処理をする
promises を使って同期実行が出来ます
var anim = scene.beginAnimation(box1, 0, 100, false); console.log("before"); await anim.waitAsync(); console.log("after");
CreateAndStartAnimation関数
AbstractMeshオブジェクトの場合には、
StartとEndを設定することでアニメーションを定義出来ます。
BABYLON.Animation.CreateAndStartAnimation('boxscale', box1, 'scaling.x', 30, 120, 1.0, 1.5);
アニメーションのブレンド
歩いている状態から走る状態へのなめらかな遷移など、アニメーションのブレンドが出来ます!
enableBlending = true を設定
例はこちら
https://www.babylonjs-playground.com/#2BLI9T#174
アニメーションの重みを設定してブレンド
Babylon.js 3.2以降では、ブレンドを特定の重みでアニメーションを開始できます。
重さを用いたアニメーションはscene.beginWeightedAnimationAPIを使用します。
// 停止 重み 1.0 // scene.beginWeightedAnimation(対象,開始フレーム,終了フレーム,重み,ループ(true/false),(スピード),(終了後の処理),(アニメート設定)) var idleAnim = scene.beginWeightedAnimation(skeleton, 0, 89, 1.0, true); // 歩き 重み 0 var walkAnim = scene.beginWeightedAnimation(skeleton, 90, 124, 0, true); // 走り 重み 0 var runAnim = scene.beginWeightedAnimation(skeleton, 125, 146, 0, true);
重みは0から1.0の間をとります。-1を設定するとモードがオフになります。
イージング機能
アニメーションにイージングを追加出来ます
イージングチートシート
Easing Functions Cheat Sheet
適用できる関数はこちら
- BABYLON.CircleEase()
- BABYLON.BackEase(amplitude)
- BABYLON.BounceEase(bounces, bounciness)
- BABYLON.CubicEase()
- BABYLON.ElasticEase(oscillations, springiness)
- BABYLON.ExponentialEase(exponent)
- BABYLON.PowerEase(power)
- BABYLON.QuadraticEase()
- BABYLON.QuarticEase()
- BABYLON.QuinticEase()
- BABYLON.SineEase()
- BABYLON.BezierCurveEase()
EasingModeプロパティを使用して、イージング関数の動作の変更もできます。
つまり、アニメーションの補間方法を変更できます。EasingModeに指定できる値は3つあります。
- BABYLON.EasingFunction.EASINGMODE_EASEIN :補間は、イージング関数に関連付けられた数式に従います。
- BABYLON.EasingFunction.EASINGMODE_EASEOUT :補間は、100%補間からイージング関数に関連付けられた数式の出力を引いたものに従います。
- BABYLON.EasingFunction.EASINGMODE_EASEINOUT :補間では、アニメーションの前半にEaseInを使用し、後半にEaseOutを使用します。
補完ベジェの自作も出来ます
// 例 var bezierEase = new BABYLON.BezierCurveEase(0.32, -0.73, 0.69, 1.59);
複雑なアニメーション
複雑なアニメーションでは、アニメーションの各フレーム(ティック)ごとにすべてを選択できます。
ゲームのような複雑なアニメーションを設定する場合は、こちらを使います。
requestAnimationFrameを使うわけですがbabylon.jsでは便利なregisterBeforeRenderが用意されています
scene.registerBeforeRender(function () { //Your code here });
まとめ
アニメーションには、予め動きを定義できる
「キーフレームアニメーション」
と
「複雑なアニメーション」
の2つがありました。
「キーフレームアニメーション」には、かなり細かい設定が可能なプロパティが多く用意されており、予定調和可能な映画やCMのような表現で活躍すると思います。
また、ゲームのような動きが予測出来ないような場合には「複雑なアニメーション」をregisterBeforeRender関数を利用して実行します。
お次はコリジョン(衝突)判定
いきましょう