この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Unityで作成したマルチプレイヤーゲームをGameLiftで動かしてみました!! このシリーズでは、GameLiftを利用するために必要なアプリケーション側での振る舞いをサンプルコードを交えながら紹介していきます。 ということで前回に続く、第3回の記事となります。
範囲
*の箇所が第3回の範囲です。
- 全体構成の説明
- クライアントサイドアプリ実装のポイント
- *サーバーサイドアプリ実装のポイント
- API Gateway + Lambda実装のポイント
- GameLiftへのデプロイ
- ゲームプレイ
サーバーサイドアプリ実装のポイント
Unity公式チュートリアルのマルチプレーヤゲームをGameLiftに適応させるには、サーバーサイドアプリとしていくつかの通信/処理を実装する必要があります。
Launch server processにおいて必要な通信/処理
- 1.GameLiftServiceから起動されServerSDKの初期化処理を実行する
- 2.GameLiftServiceにProcessReadyであることを通知する
- 3.GameLiftServiceに定期的にプロセスの状態を通知する
Start gameにおいて必要な通信/処理
- 4.Gameのサーバーサイドアプリケーションを起動する
Add playerにおいて必要な通信/処理
- 5.GameLiftServiceにPlayerが追加されたことを通知する
Drop playerにおいて必要な通信/処理
- 6.GameLiftServiceにPlayerが削除されたことを通知する
Stop gameにおいて必要な通信/処理
- 7.GameLiftServiceにGameが終了したことを通知する
Shut down server processにおいて必要な通信/処理
- 8.GameLiftServiceにGameLiftのprocessが終了したことを通知する
1〜4までの処理はサーバー起動時に実行する、または、サーバー起動時のコールバック関数として実装する箇所。5〜8まではアプリケーション内のイベントに応じて実行する箇所となります。 実現方法は色々ありますが、この通信/処理を満たすよう設計/実装することが、ゲーム(サーバーサイドアプリ)をGameLiftに適用させるためのポイントになります。 なお、この通信/処理を実現するためにはServerSDKをUnityプロジェクトに統合する必要があります。まだ実施されいない方は下記ブログを参考に実施してください。
サーバーサイドアプリ修正
ということで、実際にチュートリアルのマルチプレーヤゲームをGameLiftに適用させてみました。 実施した処理は、大きく分けて以下の2つです。
- C#スクリプトの追加/修正
- GameObjectの追加
C#スクリプトの追加/修正
3つのC#スクリプトを追加/修正します。
- GameLift.cs
- MyNetworkManager.cs
- PlayerController.cs
GameLift.cs
サーバーサイドアプリ起動時に実行される処理です。「1.GameLiftServiceから起動されServerSDKの初期化処理を実行する」の処理は26行目、「2.GameLiftServiceにProcessReadyであることを通知する」の処理は49行目で実施しています。コールバック関数は29行目ProcessParameters
にて実装しており、第1引数がOnStartGameSession(「4.Gameのサーバーサイドアプリケーションを起動する」)
、第2引数がOnProcessTerminate
、第3引数がOnHealthCheck(「3.GameLiftServiceに定期的にプロセスの状態を通知する」)
、第4引数がport
、第5引数が logParameters
となります。
また、このサーバーサイドアプリでは起動時にはリスニングポートを指定できるようにしています。このように実装している理由は第5回のブログで紹介します。
GameLift.cs
using UnityEngine;
using UnityEngine.Networking;
using System.Collections.Generic;
using Aws.GameLift.Server;
using Aws.GameLift.Server.Model;
public class GameLift : MonoBehaviour
{
public static int listeningPort = 7777;
void Awake()
{
}
public void Start()
{
string[] args = System.Environment.GetCommandLineArgs();
int len = args.Length;
for (int i = 0; i < args.Length; i++) {
Debug.Log(args[i]);
if (args [i] == "-listeningPort") {
listeningPort = int.Parse(args[i+1]);
}
}
var initSDKOutcome = GameLiftServerAPI.InitSDK();
if (initSDKOutcome.Success)
{
ProcessParameters processParameters = new ProcessParameters(
(gameSession) => {
NetworkManager.singleton.networkAddress = "localhost";
NetworkManager.singleton.networkPort = listeningPort;
NetworkManager.singleton.StartServer ();
GameLiftServerAPI.ActivateGameSession();
},
() => {
GameLiftServerAPI.ProcessEnding();
},
() => {
return true;
},
listeningPort,
new LogParameters(new List<string>()
{
"/local/game/logs"
})
);
var processReadyOutcome = GameLiftServerAPI.ProcessReady(processParameters);
if (processReadyOutcome.Success)
{
print("ProcessReady success.");
}
else
{
print("ProcessReady failure : " + processReadyOutcome.Error.ToString());
}
}
else
{
print("InitSDK failure : " + initSDKOutcome.Error.ToString());
}
}
void OnApplicationQuit()
{
GameLiftServerAPI.Destroy();
}
}
MyNetworkManager.cs
NetworkManagerを継承したクラスです。NetworkManagerにはoerride
可能なメソッドがいくつか用意されており、OnServerAddPlayer
、OnServerDisconnect
もその一つです。5.GameLiftServiceにPlayerが追加されたことを通知するの処理はOnServerAddPlayer
メソッド(15行目)にて実装しています。この関数は、クライアントがサーバーに接続した際にサーバーサイドで呼び出されます。関数内ではクライアントサイドが送信したplayerSessionId
を取得後、AcceptPlayerSession
を実行し、GameLiftServiceにプレイヤーが追加されたことを通知します。
また、プレイヤー追加時には、connectionId
をキーにplayerSessionId
をplayerSessionData
に登録し現在このゲームセッションに接続中のプレイヤーを管理します。
6.GameLiftServiceにPlayerが削除されたことを通知するの処理はOnServerDisconnect
メソッド(24行目)にて実装しています。この関数は、クライアントが切断された際にサーバーサイドで呼び出されます。関数内ではconnectionId
をキーにplayerSessionId
を取得後、RemovePlayerSession
を実行し、GameLiftServiceにプレイヤーが削除されたことを通知します。
MyNetworkManager.cs
using UnityEngine;
using UnityEngine.Networking;
using System.Collections.Generic;
using UnityEngine.Networking.NetworkSystem;
using Aws.GameLift.Server;
public class MyNetworkManager : NetworkManager {
public static Dictionary<int, string> playerSessionData = new Dictionary<int, string>();
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId, NetworkReader extraMessageReader)
{
Debug.Log("OnServerAddPlayer");
var playerSessionId = extraMessageReader.ReadMessage<StringMessage>().value;
GameLiftServerAPI.AcceptPlayerSession(playerSessionId);
base.OnServerAddPlayer(conn,playerControllerId);
playerSessionData.Add(conn.connectionId,playerSessionId);
}
public override void OnServerDisconnect(NetworkConnection conn)
{
Debug.Log("OnServerDisconnect");
string playerSessionId = playerSessionData[conn.connectionId];
GameLiftServerAPI.RemovePlayerSession(playerSessionId);
playerSessionData.Remove(conn.connectionId);
base.OnServerDisconnect(conn);
}
public override void OnClientConnect(NetworkConnection conn)
{
#クライアントサイドで実行される処理
}
}
PlayerController.cs
プレイヤーの操作を司るクラスです。今回はこのクラスに「プレイヤーがQ
を押した時にゲームを終了する」処理を追加します。7.GameLiftServiceにGameが終了したことを通知するの処理を61行目、8.GameLiftServiceにGameLiftのprocessが終了したことを通知するの処理を62行目でそれぞれ実装しています。
PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using Aws.GameLift.Server;
using Aws.GameLift.Server.Model;
public class PlayerController : NetworkBehaviour
{
public GameObject bulletPrefab;
public Transform bulletSpawn;
void Update()
{
if (!isLocalPlayer)
{
return;
}
var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
transform.Rotate(0, x, 0);
transform.Translate(0, 0, z);
if (Input.GetKeyDown(KeyCode.Space))
{
CmdFire();
}
if (Input.GetKeyDown(KeyCode.Q))
{
CmdStopGame();
}
}
[Command]
void CmdFire()
{
var bullet = (GameObject)Instantiate(
bulletPrefab,
bulletSpawn.position,
bulletSpawn.rotation);
bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 6;
NetworkServer.Spawn(bullet);
Destroy(bullet, 2.0f);
}
public override void OnStartLocalPlayer()
{
GetComponent<Renderer>().material.color = Color.blue;
}
[Command]
private void CmdStopGame()
{
NetworkManager.singleton.StopServer ();
GameLiftServerAPI.TerminateGameSession();
GameLiftServerAPI.ProcessEnding();
}
}
GameObjectの追加
空のゲームオブジェクトを追加し、先ほど作成したGameLift.cs
をアタッチします。
まとめ
GameLiftを利用するためのサーバーサイドアプリの実装のポイントを確認しました。次回はAPI Gateway + Lambda実装のポイントを紹介しようと思います。