ドティ・ラン

Doty Runは、ダイナミックなゲームプレイ、プロシージャル生成、そして革新的なメカニクスを特徴とするエンドレスランナーゲームです。 冒険心あふれるドティの導きに従い、プレイヤーはコイン、障害物、急な曲がり角が待ち受ける氷の道を進みながら、高得点を狙います。 Doty Runは、Niantic Studioが複雑でインタラクティブなウェブゲームを開発する能力を際立たせています。

サンプルプロジェクトで自分好みにカスタマイズしましょう。

player controller

プレーヤーコントローラー checkmark bullet

入力アクションやタッチジェスチャーに反応するプレイヤーコントローラの実装方法を学びます。

サンプルプロジェクトを見る
Gyroscope Controller

ジャイロスコープ・コントローラー checkmark bullet

ジャイロスコープを使ったティルトコントロールをマスターして、直感的で没入感のあるゲームプレイを。

サンプルプロジェクトを見る

Behind the Build: Doty Run

Written by Camilo Medina

December 4, 2024


Introduction

Doty Runは、プレイヤーが勇敢な冒険者Dotyを操作し、氷点下の道を駆け抜けながら貴重なDotyコインを集める、興奮のエンドレスランニングゲームです。 目標は? できるだけ長く生き残り、できるだけ遠くまで進み、できるだけ多くのコインを集めてハイスコアを更新しよう。 PCユーザーの場合、移動は矢印キーで操作します。 氷の道を navigation し、障害物を避け、道をたどって冒険を続けよう。 すべてのプレイで最高のスコアが保存されます—それぞれの冒険が、
でそのスコアを更新するチャンスです! どこまで行けるでしょうか?

Project Structure

3D シーン

  • GameManager: ゲームの状態とスコアを管理するゲームマネージャースクリプトを含みます。
    • Startui-startスクリプトを含みます。 メッシュと物理コライダーを含みます。
    • コーナー: パスの方向が変わるコーナータイルの3Dモデル。
  • Environment: ゲームの環境用3Dモデルを含みます
  • Fog: シーン内の霧効果を管理するfog-controllerスクリプトを含みます
  • Sfx:背景音楽用のオーディオコンポーネントと、サウンド効果を管理するsfx-managerスクリプトを含みます
  • PathRoot: 実行時に生成されるパス要素のルート親です。 メッシュと物理コライダーを含みます。
  • コイン: コインの3Dモデル。 メッシュと物理コライダーを含む、イベント専用に設定されたコライダーセット
  • Player: 物理コライダーとすべてのプレイヤー関連スクリプトを含む:player-controllerplayer-collisioninput-managergyro-controllerplayer-animator、およびtouch-input-controller
  • Doty: Dotyの3Dモデルで、プレイヤーのすべての動作用のメッシュとアニメーションを含む。
  • カメラ: ゲームカメラを含みます
  • アンビエントライト: ゲームのアムビエントライトを含みます
  • ディレクショナルライト: ゲームの方向性ライトを含みます
  • 資産

    3Dモデル

    • 環境
      • env-1.glb: メイン環境の3Dモデル
    • オブジェクト
      • coin_1.glb: 収集アイテムとして使用されるコインの3Dモデル
      • doty_coin_gold_v2.glb: コインモデルの代替バージョン。

    スクリプト

    これは、このプロジェクト内のスクリプトの簡単な概要です。 ここでは、グローバルなCSSスタイルとモジュール化されたCSSスタイル(utilities.cssstart.cssgameplay.css、およびgame-over.css)をインポートし、ゲームUIのビジュアルデザインとレイアウトを定義しています。 さらに、UIで一貫したスタイリングを実現するために使用するフォントを注入します。

    game-manager.ts: ゲームのコア状態(開始ゲームプレイゲームオーバー)を管理し、プレイヤーのスコア計算を処理し、
    のハイスコアの保存と取得を管理します。

    utilities.jsTHREEライブラリへのアクセスが必要なコアメカニクスを実装します。例えば、ジャイロスコープへのアクセスを要求する関数などです。

    game-manager.ts: ゲームのコア状態(開始ゲームプレイゲームオーバー)を管理し、プレイヤーのスコア計算を処理し、
    のハイスコアの保存と取得を管理します。 例えば、ジャイロスコープへのアクセスを要求する関数などです。 これらのユーティリティは、Studio スクリプトから分離して管理されており、ECS ライブラリとの衝突を回避するためです。 カメラは現在の位置に配置され、プレイヤーに対してオフセットが生じます。 このアプローチでは、プレイヤーの位置に対してカメラの位置を動的に調整するために変換行列を計算する必要がなくなります。

    このスクリプトは、プレイヤーをX軸に沿って追跡するように拡張することも可能です。

    このメカニズムは、プレイヤーを追従する空のエンティティを作成し、そのエンティティの子としてカメラを設定することで実現されます。 カメラは現在の位置に配置され、プレイヤーに対してオフセットが生じます。 このアプローチでは、プレイヤーの位置に対してカメラの位置を動的に調整するために変換行列を計算する必要がなくなります。 スタート画面の背景とスタートボタンの外観を定義します。

    utilities.css: UI要素用のユーティリティクラスを提供します。例えば、要素を非表示にするクラスやアニメーションを追加するクラスなど。

    入力

    gyro-controller.ts: このコンポーネントは、電話のジャイロスコープを使用してY軸(左右の傾き)の回転を検出する入力メカニズムを実装します。これは、設定可能なスキーマプロパティangleLimitに基づいて動作します。

    CSS

    game-over.css: ゲームオーバー画面のUIスタイルを処理します。 これには、ゲームオーバー画面の背景、スコア表、およびリスタートボタンが含まれます。 現在、Studioでは、入力がアクティブかどうかを確認する機能(input.getAction())のみを提供しており、入力がトリガーされたタイミングや終了したタイミングに関する情報は提供されていません。

    start.css: スタート画面のUIスタイルを管理します。 スタート画面の背景とスタートボタンの外観を定義します。 例えば、要素を非表示にするクラスやアニメーションを追加するクラスなど。 これは、設定可能なスキーマプロパティangleLimitに基づいて動作します。 一部のデバイスでは、ジャイロスコープにアクセスするには明示的なユーザー許可が必要であり、この許可はボタンを押すなどの直接的なユーザー操作を通じてリクエストする必要があります。 このコンポーネントは、Z軸(コンパス方向)やX軸(前後傾き)周りの回転など、他の種類の回転を検出するように簡単に変更可能です。 また、弧状の障害物との衝突を、シンプルで直感的な方法でチェックします:現在のアニメーションが「Roll」であるかどうかを確認するだけです。 このアプローチにより、プレイヤーが下ボタンを適切なタイミングで押したかどうかを確認しつつ、コードを過剰に複雑化することなく実現できます。

    player-collision.ts: プレイヤーのすべての衝突を処理します。

    touch-input-controller.ts: このコンポーネントは画面上のタッチ操作を管理し、指が画面上で上、下、左、または右にスワイプされた際にon-input-action-triggeredイベントをトリガーします。 各衝突に対応するイベントを送信し、他のスクリプトでアクションをトリガーします。

    object-spawner.ts: このコンポーネントは、プロシージャルに生成された設定に基づいて、パスの新しいセクションのタイル上にオブジェクトを配置します。 設定により、各タイルに配置されるオブジェクト(コイン、障害物、パワーアップなど)が決定されます。 プレイヤー・アニメーター(player-animator.ts)経由で対応するアニメーションをトリガーし、 関数でゲームオーバー条件(落下や障害物との衝突など)をチェックし、必要に応じてon-player-lost-game イベントを ディスパッチします。

    path-movement.ts: このコンポーネントは、パスセクション内の現在の
    タイルを前方方向に移動させ、
    ゲームシーン内でプレイヤーの移動の錯覚を生み出します。 また、リスタートボタンが押された際に発生するイベントを管理します。

    ui-gameplay.ts: ゲームプレイUI画面の各要素を格納する個別のdiv要素を作成し、3つのゲームステータス(コイン、距離、ポイント)を表します。   このコンポーネントは、特定のアニメーションのタイミング処理も担当しています。 また、スタートボタンが押された際に発生するイベントを管理します。

    Implementation

    エンドレスランニングゲームを開発する際、特に注意すべき点があります:プレイヤーキャラクターは動きません。

    image1-3

    なぜ?

    ゲームはプレイヤーがどこまで移動するかを予測できないため、グローバル原点から離れすぎると、浮動小数点数の精度制限により数学的な計算エラーが発生する可能性があります。 さらに、プレイヤーを位置 (0,0,0)に固定することで、衝突検出やカメラ移動などの特定のゲームプレイメカニクスの開発が簡素化されます。


    では、それぞれ何をするのでしょうか?

    UI

    すべてのゲームにはUIが必要であり、Studioには組み込みのUIシステムが提供されています。 ただし、このゲームはウェブブラウザで実行されるように設計されているため、StudioのUIはCSSを使用して強化でき、よりスタイリッシュなデザインや高度なカスタマイズが可能です。

  • UI要素をインスタンス化新しいdiv要素を作成し、一意のIDを割り当てます。 例:const myUiElement = document.createElement('div') myUiElement.id = 'my-ui-element'
  • UI要素をドキュメントに追加する: 要素をドキュメントの本体に追加します。 例:document.body.append(myUiElement)
  • CSS ファイルの作成: 例:my-css-style.
  • css
  • 新しい UI 要素のスタイルを定義: CSS ファイル内で要素を指定するために一意の ID を使用します。 例:#my-ui-element {/_ ここにスタイルを追加 _/}
  • CSS ファイルをアプリケーションにインポート: 作成したCSS ファイルを app.js にインポートします。 例:import './my-css-style.css'
  • ヒント:app.jsファイルは手動で作成する必要があります。

    image2-2

     

    このゲームでは、プレイヤーの操作にジャイロスコープへのアクセスが必要のため、スタートボタンを押すと、utilities.jsスクリプトからrequestGyroscopePermission関数が呼び出されます。 これにより、ゲームプレイが開始される前に、ブラウザはユーザーから必要な権限を要求します。

    プレイヤーコントローラー

    pl

    ayer-controller.tsスクリプトは、gyro-controller.tsinput-manager.ts、およびtouch-input-controller.ts の3つの別々のスクリプトからの入力を処理するように設計されています。 各スクリプトは、プレイヤーコントローラーがプレイヤーキャラクターを移動またはアニメーションさせるために使用する特定の入力データを提供します。
    image5

    gyro-controller.tsスクリプトは、デバイスの方向が変更された際に on-device-orientation-changedイベントを送信し、そのデータとしてdeviceOrientationStateを渡します。 この状態は、デバイスの向き(左、中央、または右)を表します。 player-controller.tsはこのイベントを監視し、movePlayerSideways(side)関数を呼び出し、プレイヤーの位置を適切に調整します。 これはトリガーされたアクション(上、左、右、または下)を表します。 player-controller.tsはこのイベントを監視し、指定されたアクションに基づいて回転と移動を処理するrotateAndMovePlayer(actionName) を呼び出します。

    プレイヤーが実行するすべてのアクションに対して、player-controller.tsスクリプトは対応するアニメーションイベントをディスパッチし、適切なアニメーションがトリガーされるようにします。

    パスビルダー

    先ほど述べたように、プレイヤーキャラクターは移動しません。 これを実現するため、ゲームではセクションから構成されるパスを実装し、各セクションはタイルで構成されています。 タイルにはさまざまな種類があります。

    image6
    1. ベースタイル:標準の完全なタイル。 ベースタイル上では、プレイヤーは左右に3つの位置(中央)の間を移動できます。 ここでは、プレイヤーは横方向のみに移動できます。
    2. コーナータイル: 経路の方向変更を示し、新しいセクションの開始点をマークします。
    3. コーナータイル: 経路の方向変更を示し、新しいセクションの開始点をマークします。 各コーナーのタイルは、新しいパスセクションへと移行します。
    image9
             
    // TODO: convert image to code
    
          

    この関数は、セクション内のタイルのシーケンスを表す文字列の配列を生成します。

    各文字列は、セクション内に生成するタイルの種類を指定します。 includeCornerまたはincludeVariantsパラメーターにfalseを指定することで、ゲームは必要に応じてコーナータイルや部分タイルを除外できます。 このイベントは、プレイヤーが新しいタイルと衝突するたびに発生します。 コールバック関数handleTileChange(event)は、このイベントを処理し、以前のタイルを削除して新しいタイルを追加することで、パスを継続的に維持します。

    セクションに新しいタイルが追加されると、そのタイルはPathRootエンティティの子として追加されます。

    セクションに新しいタイルが追加されると、そのタイルはPathRootエンティティの子として追加されます。

    image4-3
             
    // TODO: convert image to code
    
          

    オブジェクトの配置(コイン、障害物、パワーアップなど)は、タイルの配置と類似したプロセスに従います。 object-spawner.tsスクリプトは、computeObjectConfigInSectionTiles(addCoins, addObstacles, addPowerUps)関数を使用して、新しいセクション内のオブジェクトの構成を生成します。

    例: { 0: [], 1: [], 2: ['obstacle'], 3: ['coin', 'obstacle'], 4: ['powerUp'], 5: [], 6: ['corner'] }

    各キーはタイルインデックスに対応し、値は該当するタイルに配置するオブジェクトの配列です。 このシステムは柔軟性と拡張性を重視して設計されており、新しいオブジェクトタイプの追加が可能です。

    ゲームプレイ

    ゲームプレイ

    中、画面の左側に現在収集したコインの数が表示され、右側には現在のポイントが表示されます。 プレイヤーが250メートルごとに到達すると、現在の距離が表示されます。
    image3-1

    プレイヤーが落下したり障害物に衝突したりしてゲームオーバーになった場合、game-manager.jsスクリプトが新しいスコアを計算します。 新しいスコアが以前のスコアよりも高い場合、新しいハイスコアを保存し、独自のメッセージと共に表示します。 データの保存と取得は、これらの2つの関数を使用して行われます。

    image7
             
    // TODO: convert image to code