いろいろガンガンいこうぜ。体も大事に

普段はJavaでAndroidアプリ開発しているプログラマーのブログです。

Photonを用いてオブジェクトの座標を同期してみる

Unity上でPhotonを使って,ネットゲームでのオブジェクトの座標の同期に挑戦してみました。



Photon Cloud公式のチュートリアルを見たところ(http://doc.exitgames.com/photon-cloud-jp/TutorialMarcoPolo/#cat-tutorialsなど)ネットゲームの大切な要素がたくさん詰まって(いそうだということが分かり)ました。(あまりしっかり理解できませんでした。)
大事な要素を少しずつに分けて確認したいと思います。



今回は必要最低限の構成で,プレイヤー間のオブジェクトの座標の同期を行うプロジェクトを作ってみました。



やれることとしては,

  • 名称固定な部屋を作れる(詳しくは,こちらを)
  • (存在している場合),名称固定な部屋に入れる
  • 部屋から抜ける
  • スペースキー(Androidの場合は画面タップで)で,Playerオブジェクト(Cupsule)が真上にジャンプ
  • 同じ部屋にいる他のユーザーのオブジェクトの状態が見える

です。



次に作成手順です。
Photon Cloud AppIdとプロジェクトの設定は
プロジェクトの設定はこちらのページがとても参考になりました。


作成手順


  1. プロジェクトを新規作成,AppIdを設定
  2. 空のsceneを作成
  3. PlaneとDirectinal Lightをおく
  4. 空のGameObjectを作成し,名前をGameManagerにする
  5. 下記のMainMenuとGameManagerをGameManagerに追加
  6. Capsuleをおき,名前をPlayerに
  7. PlayerにJumperスクリプトを追加
  8. PlayerにPhoton Unity NetworkingのPhotonViewスクリプトを追加
  9. PlayerのPhotoViewのObserverインスペクター中に,Player自身をドラッグアンドドロップ
  10. Assets以下にResourcesディレクトリを作成。その中にPlayerをプレハブ化
  11. シーン中のPlayerを削除

    こここまでである程度同期するけれど,カクカクしてしまいます。

  12. NetworkPlayer.csを作成し,Playerにコンポーネント追加

    OnPhotonSerializeViewが呼ばれていません。プルプルしてまいす。(オブジェクトが本来あるべき点と原点とを行ったり来たりしているように見える)

  13. PlayerのインスペクターのObserver変更
    Player中のObserにPlayer中のNetworkコンポーネントをドラッグアンドドラッグして,Player中のObserveを変更します。

PlayerやFloorにMaterialをつけた方が見やすいかと。


ソースコードを張っておきます。
おかしい点があったら,教えていただけると嬉しいです。


以下,ソースコード

GameManager.cs
using UnityEngine;
using System.Collections;

public class GameManager : Photon.MonoBehaviour
{
	public string playerPrefabName = "Player";

	void OnJoinedRoom ()
	{
		StartGame ();
	}
    
	IEnumerator OnLeftRoom ()
	{
		while (PhotonNetwork.room!=null || PhotonNetwork.connected==false) {
			yield return 0;
		}
		
		Application.LoadLevel (Application.loadedLevel);
	}
	
	void OnGUI ()
	{
		if (PhotonNetwork.room == null) {
			return;
		}

		if (GUILayout.Button ("Leave & QUIT")) {
			PhotonNetwork.LeaveRoom ();
		}
	}

	void StartGame ()
	{
		PhotonNetwork.Instantiate (playerPrefabName, getPositionRamdomly (), Quaternion.identity, 0);
	}
	
	Vector3 getPositionRamdomly ()
	{
		return new Vector3 (Random.Range (-3.0f, +3.0f), 1, Random.Range (-3.0f, +3.0f));
	}
}
Jumper.cs
using UnityEngine;
using System.Collections;

[RequireComponent(typeof(PhotonView))]
[RequireComponent(typeof(Rigidbody))]
public class Jumper : Photon.MonoBehaviour
{
	void Awake ()
	{
		if (!photonView.isMine) {
			rigidbody.isKinematic = true;
		}
	}
	
	void Update ()
	{		
		if (Input.GetKeyDown (KeyCode.Space) || IsTapScreen ()) {
			rigidbody.AddForce (transform.up * 10.0f, ForceMode.Impulse);
		}
	}
	
	bool IsTapScreen ()
	{
		if (Input.touchCount <= 0) {
			return false;	
		}
		
		foreach (Touch touch in Input.touches) { 
			if (touch.phase == TouchPhase.Began) {
				return true;
			}
		}
		return false;
	}
}
MainMenu.cs
using UnityEngine;
using System.Collections;

public class MainMenu : MonoBehaviour
{
	private const string ROOM_NAME = "JumpingSampleRoom";

	void Awake ()
	{
		if (!PhotonNetwork.connected) {
			PhotonNetwork.ConnectUsingSettings ("v1.0");
		}
		PhotonNetwork.playerName = PlayerPrefs.GetString ("playerName", "Guest" + Random.Range (1, 9999));
	}

	void OnGUI ()
	{
		if (!PhotonNetwork.connected) {
			return;
		}

		if (PhotonNetwork.room != null) {
			return;
		}

		GUILayout.BeginArea (new Rect ((Screen.width - 400) / 2, (Screen.height - 300) / 2, 400, 300));

		GUILayout.Label ("Main Menu");

		{
			GUILayout.BeginHorizontal ();
			GUILayout.Label ("Player name:", GUILayout.Width (150));
			PhotonNetwork.playerName = GUILayout.TextField (PhotonNetwork.playerName);
			if (GUI.changed) {
				PlayerPrefs.SetString ("playerName", PhotonNetwork.playerName);
			}
			GUILayout.EndHorizontal ();
		}	
		
		GUILayout.Space (15);

		{	
			GUILayout.BeginHorizontal ();
			GUILayout.Label ("JOIN ROOM:", GUILayout.Width (150));
			GUILayout.Label (ROOM_NAME, GUILayout.Width (150));
			if (GUILayout.Button ("GO")) {
				PhotonNetwork.JoinRoom (ROOM_NAME);
			}
			GUILayout.EndHorizontal ();
		}
		
		GUILayout.Space (15);

		{
			GUILayout.BeginHorizontal ();
			GUILayout.Label ("CREATE ROOM:", GUILayout.Width (150));
			GUILayout.Label (ROOM_NAME, GUILayout.Width (150));
			if (GUILayout.Button ("GO")) {
				PhotonNetwork.CreateRoom (ROOM_NAME, true, true, 10);
			}
			GUILayout.EndHorizontal ();
		}
        
		GUILayout.EndArea ();
	}
}
NetworkCharacter.cs
using UnityEngine;
using System.Collections;

[RequireComponent(typeof(PhotonView))]
public class NetworkCharacter : Photon.MonoBehaviour
{
	private Vector3 correctPlayerPosision = Vector3.zero;

	void Update ()
	{
		if (!photonView.isMine) {
			transform.position = Vector3.Lerp (transform.position, correctPlayerPosision, Time.deltaTime * 5);
		}
	}
	
	void OnPhotonSerializeView (PhotonStream stream, PhotonMessageInfo info)
	{
		if (stream.isWriting) {
			stream.SendNext (transform.position);
		} else {
			correctPlayerPosision = (Vector3)stream.ReceiveNext();
		}
	}
}


それでは,また会えることを祈りつつ。