Unityカーレース

4人まで参加してマルチプレイ出来ます。一人でもタイムアタックで遊べます。AI車は8台で3台が早いです。コースから落下したら戻ります。ほかの人がいる時抜けてもゲームは維持されます。
Photon Unity Network(PUN2)にアップデートしました。今回photon transform viewとphoton transform view classicをオブジェクトで使い分けました。free素材を使いました。
ゲーム説明
PC用:キーボードの カーソルキー「→」、「←」で左右にハンドルを切ります。 カーソルキー「↑」、「z」でアクセル「↓」、「x」でブレーキです。

スマホ用:タッチするとバーチャルパッドが表示されます。離すと消えて真っすぐになります。バーチャルパッドで左右にハンドルを切り、アクセルとブレーキはボタンで操作します。

①Asset StoreからPhoton Unity Networking Freeをimportします。
②unity standard assetsをimportします。standard assetsにあるUtilityを使います。
③Photon接続Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class NetworkManager : MonoBehaviour {

	private float timeUP;
	public GUIStyle style;

	void Awake () {
		// Server接続
		PhotonNetwork.ConnectUsingSettings("v0.1");
	}
	// Lobby参加OK時	
	void OnJoinedLobby() {
		// ランダムにRoom参加
		PhotonNetwork.JoinRandomRoom();
	}
	// Room参加NG時
	void OnPhotonRandomJoinFailed() {
		// 名前なしRoom作成
		PhotonNetwork.CreateRoom(null);
	}
	// Room参加OK時
	void OnJoinedRoom() {
		if (PhotonNetwork.countOfPlayers < 2) {
			//プレイヤーをインスタンス化
			Vector3 spawnPosition = new Vector3 (-53f, 0.5f, -11f); //生成位置
			PhotonNetwork.Instantiate ("Player", spawnPosition, Quaternion.identity, 0);
			Vector3 enemyPosition = new Vector3 (-53f, 0.5f, -5f); //生成位置
			PhotonNetwork.InstantiateSceneObject ("Enemy", enemyPosition, Quaternion.identity, 0, null);
			Vector3 enemy3Position = new Vector3 (-55.5f, 0.5f, -5f); //生成位置
			PhotonNetwork.InstantiateSceneObject ("Enemy2", enemy3Position, Quaternion.identity, 0, null);
			Vector3 enemy4Position = new Vector3 (-53f, 0.5f, -7f); //生成位置
			PhotonNetwork.InstantiateSceneObject ("Enemy3", enemy4Position, Quaternion.identity, 0, null);
			Vector3 enemy2Position = new Vector3 (-55.5f, 0.5f, -7f); //生成位置
			PhotonNetwork.InstantiateSceneObject ("Enemy4", enemy2Position, Quaternion.identity, 0, null);
			Vector3 enemy5Position = new Vector3 (-53f, 0.5f, -9f); //生成位置
			PhotonNetwork.InstantiateSceneObject ("Enemy5", enemy5Position, Quaternion.identity, 0, null);
			Vector3 enemy6Position = new Vector3 (-55.5f, 0.5f, -9f); //生成位置
			PhotonNetwork.InstantiateSceneObject ("Enemy6", enemy6Position, Quaternion.identity, 0, null);
		}
		if (PhotonNetwork.countOfPlayers == 2) {
			Vector3 spawnPosition = new Vector3 (-55.5f, 0.5f, -11f); //生成位置
			PhotonNetwork.Instantiate ("Player", spawnPosition, Quaternion.identity, 0);
		}
		if (PhotonNetwork.countOfPlayers == 3) {
			Vector3 spawnPosition = new Vector3 (-53f, 0.5f, -13f); //生成位置
			PhotonNetwork.Instantiate ("Player", spawnPosition, Quaternion.identity, 0);
		}
		if (PhotonNetwork.countOfPlayers == 4) {
			Vector3 spawnPosition = new Vector3 (-55.5f, 0.5f, -13f); //生成位置
			PhotonNetwork.Instantiate ("Player", spawnPosition, Quaternion.identity, 0);
		}
		if (PhotonNetwork.countOfPlayers > 4) {
			PhotonNetwork.Disconnect ();
		}
	}
	// GUI表示
	void OnGUI() {
		// Photon接続状態
		GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());
		timeUP += Time.deltaTime;
		if (timeUP < 10f) {
			GUILayout.Label ("count of players " + PhotonNetwork.countOfPlayers.ToString (),style);
		}
	}
}

AI車はPhotonNetwork.InstantiateSceneObjectでシーンにインスタンス化するので、接続しているプレーヤーがいる限り存在します。PhotonNetwork.countOfPlayersでプレーヤーの数によって座標を変更しているのは、同じ場所にインスタンス化するのを防止するためです。
④自車
Tagの名前をPlayerにします。Box Collider、Rigidbody、Audio Source、Photon View(Script)、Photon Transform View(Script)、そして下記のPlayermove(Script)をアタッチします。自車はProjectのResourcesにドラッグ&ドロップしてプレハブ化してHierarchyから削除します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Playermove : Photon.MonoBehaviour {

	private float baseDistance = 3f;
	private float baseCameraHeight = 2f;
	private float chaseDamper = 3f;        // カメラの追跡スピード
	Transform cam;
	private float accel = 0;
	AudioSource sound1;
	AudioSource sound2;
	AudioSource sound3;
	// Use this for initialization
	void Start () {
		if (photonView.isMine) {
			cam = GameObject.FindGameObjectWithTag ("MainCamera").transform;
			//AudioSourceコンポーネントを取得し、変数に格納
			AudioSource[] audioSources = GetComponents<AudioSource> ();
			sound1 = audioSources [0];
			sound2 = audioSources [1];
			sound3 = audioSources [2];
		}
	}
	// Update is called once per frame
	void Update () {
		if (photonView.isMine) {
			if (Input.GetKey (KeyCode.DownArrow) || Input.GetKey (KeyCode.X)) {
				if (accel > -0.7f) {
					accel = accel - 0.03f;
				}
			} 
			if (Input.GetKey (KeyCode.UpArrow) || Input.GetKey (KeyCode.Z)) {
				if (accel < 12.8f) {
					accel = accel + 0.3f;
				}
			}
			if (Input.GetKey (KeyCode.RightArrow)) {
				transform.Rotate (0, 80 * Time.deltaTime, 0);
			}
			if (Input.GetKey (KeyCode.LeftArrow)) {
				transform.Rotate (0, -80 * Time.deltaTime, 0);
			}
			accel = accel * 0.98f;
			transform.position += transform.forward * Time.deltaTime * accel;
			// カメラの位置を設定
			var desiredPos = transform.position - transform.forward * baseDistance + Vector3.up * baseCameraHeight;
			cam.position = Vector3.Lerp (cam.position, desiredPos, Time.deltaTime * chaseDamper);
			// カメラの向きを設定
			cam.LookAt (transform);
			if (accel > 12.5f && sound3.isPlaying == false) {
				sound3.Play ();
				sound2.Stop ();
				sound1.Stop ();
			} else if (accel > 9.5f && accel <= 12.5f && sound2.isPlaying == false) {
				sound2.Play ();
				sound3.Stop ();
				sound1.Stop ();
			} else if (accel > 0.5f && accel <= 9.5f && sound1.isPlaying == false) {
				sound1.Play ();
				sound3.Stop ();
				sound2.Stop ();
			} else if (accel < 0.5f) {
				sound1.Stop ();
				sound3.Stop ();
				sound2.Stop ();
			}
		}
	}
}

⑤AI車のコースをつくる。
HierarchyでCreate Emptyをつくる。名前はWaypointsとします。そこにstandard assetsのUtilityにあるWaypointCircuitというスクリプトを持ってきます。WaypointsのInspectorを見て、WaypointCircuitコンポーネントのAssign using all child objectsをクリックし、子オブジェクトを元にルートを作成します。
⑥AI車
Box Collider、Rigidbody、Standard AssetsにあるWaypoint Progress Tracker(Script)、Photon View(Script)、Photon Transform View(Script)、そして下記のWaypoint1(Script)をアタッチします。InspectorでWaypoint Progress Tracker(Script)のTargetにHierarchyにあるオブジェクトWaypointsをドラッグ&ドロップします。AI車は動きを調整して出来上がってからプレハブ化します。動作確認したらProjectのResourcesにドラッグ&ドロップしてプレハブ化してHierarchyから削除します。プレハブ化すると、Waypoint Progress Tracker(Script)のTargetが空になるので、続けてWaypoint Progress Tracker(Script)そのものを修正します。

using System.Collections;
using UnityStandardAssets.Utility;
using UnityEngine;

[RequireComponent(typeof(WaypointProgressTracker))]
public class Waypoint1 : MonoBehaviour {

	private WaypointProgressTracker tracker = null;

	float speed = 12.8f;

	void Start()
	{
		tracker = GetComponent<WaypointProgressTracker>();
	}

	void Update()
	{
		Vector3 targetPosition = tracker.progressPoint.position + tracker.progressPoint.direction;
		transform.position = Vector3.MoveTowards( transform.position, targetPosition, speed * Time.deltaTime);
		transform.LookAt (targetPosition);
		Vector3 fwd = transform.TransformDirection(Vector3.forward);
		if (Physics.Raycast (transform.position, fwd, 5)) {
			speed = 8f;
		} else {
			speed = 12.8f;
		}
	}
}

⑦Waypoint Progress Tracker(Script)の修正。
プレハブ化すると、Waypoint Progress Tracker(Script)のTargetは空になります。インスタンス化していないためだと思われます。インスタンス化してから、HierarchyにあるオブジェクトWaypointsを読み込むよう修正します。Waypointsのタグをwaypointにします。

using System;
using UnityEngine;

namespace UnityStandardAssets.Utility
{
    public class WaypointProgressTracker : MonoBehaviour
    {
        // This script can be used with any object that is supposed to follow a
        // route marked out by waypoints.

        // This script manages the amount to look ahead along the route,
        // and keeps track of progress and laps.

        private WaypointCircuit circuit;//<--- [SerializeField]の代わりに変数の宣言をしてvoid Start()で代入。

        [SerializeField] private float lookAheadForTargetOffset = 5;
        // The offset ahead along the route that the we will aim for

        [SerializeField] private float lookAheadForTargetFactor = .1f;
        // A multiplier adding distance ahead along the route to aim for, based on current speed

        [SerializeField] private float lookAheadForSpeedOffset = 10;
        // The offset ahead only the route for speed adjustments (applied as the rotation of the waypoint target transform)

        [SerializeField] private float lookAheadForSpeedFactor = .2f;
        // A multiplier adding distance ahead along the route for speed adjustments

        [SerializeField] private ProgressStyle progressStyle = ProgressStyle.SmoothAlongRoute;
        // whether to update the position smoothly along the route (good for curved paths) or just when we reach each waypoint.

        [SerializeField] private float pointToPointThreshold = 4;
        // proximity to waypoint which must be reached to switch target to next waypoint : only used in PointToPoint mode.

        public enum ProgressStyle
        {
            SmoothAlongRoute,
            PointToPoint,
        }

        // these are public, readable by other objects - i.e. for an AI to know where to head!
        public WaypointCircuit.RoutePoint targetPoint { get; private set; }
        public WaypointCircuit.RoutePoint speedPoint { get; private set; }
        public WaypointCircuit.RoutePoint progressPoint { get; private set; }

        public Transform target;

        private float progressDistance; // The progress round the route, used in smooth mode.
        private int progressNum; // the current waypoint number, used in point-to-point mode.
        private Vector3 lastPosition; // Used to calculate current speed (since we may not have a rigidbody component)
        private float speed; // current speed of this object (calculated from delta since last frame)

        // setup script properties
        private void Start()
        {
            circuit = GameObject.FindWithTag ("waypoint").GetComponent<WaypointCircuit> ();//<---waypointを代入。
            // You can manually create a transform and assign it to this component *and* the AI,
            // then this component will update it, and the AI can read it.
            if (target == null)
            {
                target = new GameObject(name + " Waypoint Target").transform;
            }

            Reset();
        }
~~~~~~~~~~~~~

コメント