チュートリアル: 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 が作成され、ネットワークオブジェクトへのリファレンスを用いて初期化が行われます。 MessagingManagerMessagingManagerによるメッセージの送受信 に記載されている『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);
}

前のページ: はじめに

続き: ゲームロジックとARイベント