babylon.js ステップ15 環境設定
ステップ15は環境設定です。
今までは無機質な虚無空間でしたが、ここで遂に空が登場します。
そうする事で、見栄えのする3D空間が完成します
いきましょう!!
背景色の変更
シーンの背景色を変更するプロパティを使う事で簡単に、背景色を設定できます
- scene.clearColor - 背景を変更する関数
scene.clearColor = new BABYLON.Color3(0.5, 0.8, 0.5);
Color3型をnewして色を設定できます
アンビエントカラーの変更
次のプロパティを使うとアンビエントカラーを設定できます
scene.ambientColor = new BABYLON.Color3(0.3, 0.3, 0.3);
clearColorの設定方法と同じように見えますが、アンビエントカラーについては、ambientColorシーンアイテムの最終的な色を決定するためにかなりの数の計算で使用されています。
主に、メッシュとの組み合わせて使用され、メッシュマテリアルのStandardMaterial.ambientColor によって最終的なアンビエントカラーが決まるようです。
スカイボックス
美しい完璧な空を作りたいと思いませんか?
それを簡単に実現したのがスカイボックスです。
このボックスの中にカメラを設置すれば、あたかも広大な空の中にいるような演出ができます。
作り方は次の通り
まずは箱を作ります。
var skybox = BABYLON.Mesh.CreateBox("skyBox", 100.0, scene); var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene); skyboxMaterial.backFaceCulling = false; skyboxMaterial.disableLighting = true; skybox.material = skyboxMaterial;
さらにinfiniteDistanceプロパティを設定します。
skybox.infiniteDistance = true;
これによって、スカイボックスがカメラの位置に追随するため、カメラがスカイボックスから出てしまうことはなくなります。
skyboxMaterial.disableLighting = true;
ボックスに対するすべての光の反射を削除します
次に、空のテクスチャを内側に貼り付けます。
skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("textures/skybox", scene); skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
なお、設定している/skyboxディレクトリには6つの面に対するテクスチャが入っていて、適切な名前設定の必要があります
- 「skybox_nx.jpg」(左)
- 「skybox_ny.jpg」(下)
- 「skybox_nz.jpg」(背面)
- 「skybox_px.jpg」(右)
- 「skybox_py.jpg 」(上)
- 「skybox_pz.jpg」(前)
そして、これらのテクスチャは合わせると継ぎ目なくつながっている必要があります。
あとどうやらjpgだけ対応していますpngを設定したらエラーになりました
スカイボックステクスチャは、空だけのテクスチャである必要は なく。
建物、丘、山、木、湖、惑星、星などざまざまです。
スカイボックスとGoogleで検索すると様々なテクスチャがあるかと思います。
そのほか、ddsファイルを利用する方法もあります
skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("/assets/textures/SpecularHDR.dds", scene)
最後に、レンダリング順序を設定します
skybox.renderingGroupId = 0; // Some other mesh myMesh.renderingGroupId = 1;
スカイボックスの設定renderingGroupIdを0にして、他のすべてのレンダリング可能なオブジェクトを0より大きくします。
スカイボックスの自動生成方法
var envTexture = new BABYLON.CubeTexture("/assets/textures/SpecularHDR.dds", scene); scene.createDefaultSkybox(envTexture, true, 1000);
スカイボックス用の画像を自分で生成する方法
1枚のパノラマ画像からスカイボックス用の6面の画像にする事をキューブマップ(cubemap)と呼びます。
そんなキューブマップをやってくれる便利なツールがありました
こちらのパノラマ画像のキューブマップ変換機が便利そうです
christinayan01.jp
名前の命名規則は、それぞれ次のように変更します
l ->「nx」(左)
d ->「ny」(下)
b ->「nz」(背面)
r ->「px」(右)
u ->「py 」(上)
f ->「pz」(前)
Fog霧
フォグはかなり高度な効果なのですが、Babylon.jsのフォグは最大限に簡素化されています。
まず、次のようにフォグモードを定義します。
scene.fogMode = BABYLON.Scene.FOGMODE_EXP;
モードは次の4種類
- BABYLON.Scene.FOGMODE_NONE -デフォルト無効
- BABYLON.Scene.FOGMODE_EXP -フォグ密度は指数関数に従
- BABYLON.Scene.FOGMODE_EXP2 -上記と同じですが高速
- BABYLON.Scene.FOGMODE_LINEAR -フォグ密度は線形関数に従う
EXPまたはEXP2の場合には密度を設定できます
scene.fogDensity = 0.01;
それ以外の場合には、位置を設定できます
scene.fogStart = 20.0; scene.fogEnd = 60.0;
すべてのモードで色を設定できます
scene.fogColor = new BABYLON.Color3(0.9, 0.9, 0.85);
まとめ
環境設定として背景色やスカイボックス、霧の使い方を学びました。
これで解像度の高い空ができるかと思います
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で移動させることで衝突判定が出来ます。
お次はメッシュの衝突判定をより詳しくいきます!