チュートリアル: Pong: ARDKとゲームロジックの使用
ARDKの機能を使用して作った、ARマルチプレイヤー版『Pong』のUnityプロジェクト例です。このチュートリアルでは、プロジェクトを正しく機能させるためのUnity上での各ステップやC#スクリプトの使用法をご確認いただけます。この使用例では低レベルメッセージを使ってプレイヤー間のデータ送信を行っています。このプロジェクトの別バージョンでは、プレイヤー同期用のメッセージ送受信プロセスを能率化する高レベルAPIオブジェクト(HLAPI)のセットアップおよび使用例が示されています (こちら) 。
ARDKとゲームロジックの使用
GameController
ARNetworkingと ARSession オブジェクトを作成してARNetworkingManagerによる接続が完了したら、ゲームのセットアップおよびロジックの大部分を処理する GameController
(GameManagers GameObject
内)へ移動します。
イベントリスナーの設定
Start
には2つのコールバックが設定されています。 PreloadProgressUpdated
コールバックは、プリローダーによるAR共有データのキャッシュが終了したら「Join」ボタンを有効化します。 OnAnyARNetworkingSessionInitialized
コールバックは ARNetworking
の初期化が終了したら追加のセットアップを処理します:
private void Start() { startGameButton.SetActive(false); ARNetworkingFactory.ARNetworkingInitialized += OnAnyARNetworkingSessionInitialized; preloadManager.ProgressUpdated += PreloadProgressUpdated; } private void PreloadProgressUpdated(FeaturePreloadManager.PreloadProgressUpdatedArgs args) { if (args.PreloadAttemptFinished) { if (args.FailedPreloads.Count > 0) { Debug.LogError("Failed to download resources needed to run AR Multiplayer"); return; } joinButton.interactable = true; preloadManager.ProgressUpdated -= PreloadProgressUpdated; } } private void OnAnyARNetworkingSessionInitialized(AnyARNetworkingInitializedArgs args) { _arNetworking = args.ARNetworking; _arNetworking.PeerPoseReceived += OnPeerPoseReceived; _arNetworking.PeerStateReceived += OnPeerStateReceived; _arNetworking.ARSession.FrameUpdated += OnFrameUpdated; _arNetworking.Networking.Connected += OnDidConnect; _messagingManager = new MessagingManager(); _messagingManager.InitializeMessagingManager(args.ARNetworking.Networking, this); }
OnAnyARNetworkingSessionInitialized
はネットワークオブジェクトへのリファレンスをキャッシュし、ネットワークとARイベントの処理にこれを使用します。その他のイベントもサブスクライブされており、これは一部のゲームアクションが実行されるイベント、またはゲームステートが変化するイベントに対応しています。最後に MessagingManager
が作成され、ネットワークオブジェクトへのリファレンスを用いて初期化が行われます。 MessagingManager
は MessagingManagerによるメッセージの送受信 に記載されている『Pong』用に作成されたマネージャーです。
ゲームの開始
上記のとおり、StartGame ボタンはピアとホストの同期が完了するまで無効化されています。同期が完了するとホストの**StartGame** ボタンが有効化され、ゲームオブジェクトを生成するためのヒットテストが行えるようになります。画面の任意の場所(StartGame ボタンを除く)をタップすると、 ARSession
が検知したプレーンに対してヒットテストが実行されます。プレーンがヒットした場合(床、机など)、ヒット地点を中心にしてゲームオブジェクトが生成されます。ホストはその後、非ホストにメッセージを送って同じ場所にオブジェクトを生成します。
// When all players are ready, create the game. Only the host will have the option to call this public void StartGame() { if (!_objectsSpawned) InstantiateObjects(_location); startGameButton.SetActive(false); _isGameStarted = true; _ballBehaviour.GameStart(_isHost, _messagingManager); } // Instantiate game objects internal void InstantiateObjects(Vector3 position) { if (_playingField != null) { Debug.Log("Relocating the playing field!"); _playingField.transform.position = position; var offset = _isHost ? new Vector3(0, 0, -2) : new Vector3(0, 0, 2); // Instantiate the player and opponent avatars at opposite sides of the field _player.transform.position = position + offset; offset.z *= -1; _opponent.transform.position = position + offset; _ball.transform.position = position; if (_isHost) _messagingManager.SpawnGameObjects(position); return; } score.text = "Score: 0 - 0"; // Instantiate the playing field at floor level Debug.Log("Instantiating the playing field!"); _playingField = Instantiate(playingFieldPrefab, position, Quaternion.identity); // Determine the starting location for the local player based on whether or not it is host var startingOffset = _isHost ? new Vector3(0, 0, -2) : new Vector3(0, 0, 2); // Instantiate the player and opponent avatars at opposite sides of the field _player = Instantiate(playerPrefab, position + startingOffset, Quaternion.identity); startingOffset.z *= -1; _opponent = Instantiate(playerPrefab, position + startingOffset, Quaternion.identity); // Instantiate the ball at floor level, and hook up all references correctly _ball = Instantiate(ballPrefab, position, Quaternion.identity); _ballBehaviour = _ball.GetComponent<BallBehaviour>(); _messagingManager.SetBallReference(_ballBehaviour); _ballBehaviour.Controller = this; _objectsSpawned = true; if (!_isHost) return; _messagingManager.SpawnGameObjects(position); } private void FindFieldLocation(Touch touch) { var currentFrame = _arNetworking.ARSession.CurrentFrame; if (currentFrame == null) return; var results = currentFrame.HitTest ( _camera.pixelWidth, _camera.pixelHeight, touch.position, ARHitTestResultType.ExistingPlaneUsingExtent ); if (results.Count <= 0) { Debug.Log("Unable to place the field at the chosen location. Can't find a valid surface"); return; } // Get the closest result var result = results[0]; var hitPosition = result.WorldTransform.ToPosition(); InstantiateObjects(hitPosition); }