Three.jsのプラットフォーム

先日、オーク氏はThree.jsの例を使用して最初のプルリクエストを受け入れ、喜んでそれについてhabropostを書くことにしました。 Three.jsで突然3次元のプラットフォーマーを作成したいが、その方法が実際にはわからない場合は、 次の例を使用してください。











サンプルコード全体の長さは300行未満で、ハイフンで十分に希釈されているため、簡単に把握できます。 ただし、あなたの運命をさらに容易にするために、キーポイントについていくつかの言葉を以下に書きます。



背景



2日間シューティングゲームを書くことができる人について聞いたことはありますが、私たち自身は伝説に匹敵することができますか? 自分の強さをテストするために、Google にThree.jsのレッスンを重ね、2日間の傑作を作り始めました。 しかし、1、2時間後に疲れた後、私はそこに何があるかについてコメントし、インターネット読むために新鮮な空気を吸いに行きました 。 これは私がこのベンチャーに戻るたびに繰り返されました。 数日が経ち、数週間。 しかし、ドロップは石を鋭くし続けました、そして、1ヵ月のどこかで私はあなたがショットガンからモンスターのキャラバンを攻撃して撃つことができる私自身のシューティングゲームをしました



すべてをドロップして、モンスターをいくつか撃ってください






今こそ、道を振り返り、私がかなり上手く行ったことと、間違った方向に進んだ場所を考える時でした。 実際、この記事で説明するプラットフォーマーの例は、私にとっては最初のカテゴリーに分類されるものの1つです。



シューティングゲームですか、それともプラットフォーマーですか?



おそらく、シューターの本質的に単純化されたバージョンをプラットフォーマーと呼んでいるのはなぜでしょうか。 オーク氏は尋ねただけでなく、プルリクエストを受け入れる前に、例の名前をシューターに戻しました。 それでも、私はこの例を射手とは考えていません。 少なくとも誰にも撃つことができないからです。 ただし、3次元プラットフォームで実行してジャンプすることはできます。 プレイヤーのモデルを追加し、カメラの代わりに操作することで、サードパーソンゲームのサンプルコードを簡単に作り直すことができますが、これは私には関係ないようです。



たとえば、マリオがまだプラットフォーマーであると主張する人はいないでしょうか?




要するに、Sklifosovsky!



はい、私はトピックから少し脱線します。 したがって、プラットフォーマーを作成するには、まず、ゲームの世界に少なくとも1つのプラットフォームを追加する必要があります。 これは簡単なことです。3Dモデルを取得し、お気に入りの形式(babylon、ctm、dae、obj、ply、stl、vtk、またはwrlから)にエクスポートし、 Three.jsエディターにアップロードし、再度エクスポートして、あなたの健康に読み込みます。 次の2つのオプションがあります。



  1. 最初にプラットフォームをロードし、次にシーンを作成してそこにプラットフォームを追加します
  2. シーンを作成してプラットフォームを追加し、バックグラウンドでロードします


もちろん、最初のオプションはイデオロギー的にはより正確ですが、Three.jsの例のほとんど(これを含む)は気にせず、2番目のシナリオに従って動作します。 1と2のコードに特に違いはないことに注意してください-最初のケースでは、呼び出しを転送してシーンを初期化してロードハンドラー渡し、2つ目のケースでは、 松葉杖を追加してメインループでプラットフォームの状態確認し、遠くに飛ばないようにする必要があります起動するまで停止します。 多くのリソースをプリロードする場合の最初のオプションを正しく実装するには、さらに多くのコードやサードパーティライブラリが必要になるため、私はまさにこの方法を採用しました。



プラットフォームの読み込みコードを表示しますか?
function makePlatform( jsonUrl, textureUrl, textureQuality ) { var placeholder = new THREE.Object3D(); var texture = THREE.ImageUtils.loadTexture( textureUrl ); texture.anisotropy = textureQuality; var loader = new THREE.JSONLoader(); loader.load( jsonUrl, function( geometry ) { geometry.computeFaceNormals(); var platform = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ map : texture }) ); platform.name = "platform"; placeholder.add( platform ); }); return placeholder; };
      
      







ダウンロードを高速化するために、jsonファイルから法線を削除しました。ここでcomputeFaceNormalsの呼び出しが表示されます。また、上記のプラットフォーム可用性チェック用にplatform.nameが設定されています。 これがなければ、コードは次のようになります。



 loader.load( jsonUrl, function( geometry ) { placeholder.add( new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ map : texture }) ) ); });
      
      







さて、 自分でシーンを作成し、カメラとプラットフォームを追加したとしましょう。 次に、ゲームキャラクターを飛行させたり落下させたりせずに、何らかの方法でゲームキャラクターに沿って移動させる必要があります。 Raycasterクラスはこれに役立ちます。 名前が示すように、指定されたビームと選択したジオメトリの交差を計算しますこの場合、単純にビームを下に向け、プラットフォームとの最も近い交差を見つけます。






シンプルですが、ニュアンスがあります。 たとえば、キャラクターの位置をビームの開始点として使用することはできません。この場合、何らかの理由でキャラクターが少なくとも1ミリ失敗した場合、プラットフォームとの交点を見つけられず、プラットフォームに押し戻すのではなく自由落下させます。 したがって、ビームの始まりは、「鳥の飛行」の高さで、上部にある必要があります。



この場所でより詳細に、してください...
 var raycaster = new THREE.Raycaster(); raycaster.ray.direction.set( 0, -1, 0 ); var birdsEye = 100; ... // ,   raycaster.ray.origin.copy( playerPosition ); raycaster.ray.origin.y += birdsEye; var hits = raycaster.intersectObject( platform );
      
      







多階レベルのアーキテクチャの場合、この高さは明らかにプラットフォーム間の最小垂直距離によって制限されます。 次に、失敗したキャラクターをプッシュする決定をいつ行うかを慎重に検討する必要があります。 「失敗」の最大許容深度を制限しない場合、キャラクターはプラットフォームの下に移動(または飛行)するだけで、即座にプラットフォームにテレポートします。 制限しすぎると、キャラクターはジャンプ後に着地するときにプラットフォームを簡単に通過できます。



コードではどのように見えますか?
 var kneeDeep = 0.4; ... // ,   // ,   ,           if( ( hits.length > 0 ) && ( hits[0].face.normal.y > 0 ) ) { var actualHeight = hits[0].distance - birdsEye; //    ,    if( ( playerVelocity.y <= 0 ) && ( Math.abs( actualHeight ) < kneeDeep ) ) { playerPosition.y -= actualHeight; playerVelocity.y = 0; } }
      
      





熱心な読者は、playerVelocity.y <= 0のチェックがある理由を尋ねます。 回答:ジャンプする際にプラットフォームからの分離に問題が生じないようにします。



実際には、学校の物理学コースの基本的な法則に従って、キャラクターを空間内で移動させる必要があります。 キャラクターはいつでもplayerVelocity



速度とplayerPosition



スペースでの位置を知っていると仮定します。 キャラクターの動きの計算は一見すると次のようになります(擬似コード)。



 if(   ) playerVelocity.y -= gravity * time; playerPosition += playerVelocity * time; if(   ) playerVelocity *= damping ^ time;
      
      





悲しいかな、ここではすべてがそれほど単純ではありません。 学校教育を受けていない、またはゲームを構築するベテランの読者にとって、この擬似コードは「オイラー法」と呼ばれ、この方法は単にひどいことでも知られています。 そして、ここに理由があります(写真はWikipediaから盗まれます):







ご覧のように、計算された軌道はますます発散し、時間の経過とともに予想される結果になります。 それ自体では、この状況はそれほど怖くない-それは1つのささやかな可変time



によって怖がらせる。 time



を10%短縮すると、この画像がどのように変化するか想像してみてください(たとえば、より高速なブラウザーへの転送)。







ご覧のとおり、Firefoxでゲームを起動すると、1つのダイナミクスが得られます。また、クロムで起動すると、まったく異なるものになります。 バックグラウンドタスクの強度と星の位置に応じて、キャラクターの行動は「浮き上がり」ます。 どうする?



抜け道があり、非常に簡単です。 計算を長い可変ステップtime



で、短い固定ステップで複数の計算に置き換える必要があります。 たとえば、レンダリング間の2つの連続した間隔が19ミリ秒と21ミリ秒の場合、最初のレンダリングで5ミリ秒の3ステップを計算し、残りの4ミリ秒を21に加算して、2番目の5ミリ秒の5ステップを計算する必要があります。



あ、何?
このようなもの:

 var timeStep = 5; var timeLeft = timeStep + 1; ... function( dt ) { //    ;) var platform = scene.getObjectByName( "platform", true ); if( platform ) { timeLeft += dt; //     dt = 5; while( timeLeft >= dt ) { //   ... timeLeft -= dt; } } }
      
      







実際にはこれですべてですplayerVelocity



などに応じて、キャラクターの動きのパラメーター(たとえばplayerVelocity



)を設定するだけです。



そうそう、私は完全に忘れていました。 この例のストライプの出っ張りは、プラットフォーム全体にジャンプしてキャラクターを送信します。 どうやって? それは非常に簡単です-キャラクターがplayerVelocity



の出っ張りにplayerVelocity



と、事前に選択された垂直に傾斜したコンポーネントが追加されます。 特別なトリックは必要ありません-すべてがすでに機能しています。



確かに。 読んで、自分で書いて、批判を歓迎します。 接続へ!



更新 :怠laな労働者の希望に応じて、最後にもう一度リンクを追加します。




All Articles