2022-06-29 14:45:17 +03:00

361 lines
14 KiB
C#

// ----------------------------------------------------------------------------
// <copyright file="CharacterInstantiation.cs" company="Exit Games GmbH">
// Photon Voice Demo for PUN- Copyright (C) 2016 Exit Games GmbH
// </copyright>
// <summary>
// Class that handles character instantiation when the actor is joined.
// </summary>
// <author>developer@photonengine.com</author>
// ----------------------------------------------------------------------------
namespace ExitGames.Demos.DemoPunVoice
{
using System.Collections.Generic;
using ExitGames.Client.Photon;
using Photon.Realtime;
using UnityEngine;
using Photon.Pun;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class CharacterInstantiation : MonoBehaviourPunCallbacks, IOnEventCallback
{
public enum SpawnSequence { Connection, Random, RoundRobin }
public Transform SpawnPosition;
public float PositionOffset = 2.0f;
public GameObject[] PrefabsToInstantiate;
public List<Transform> SpawnPoints;
public bool AutoSpawn = true;
public bool UseRandomOffset = true;
public SpawnSequence Sequence = SpawnSequence.Connection;
public delegate void OnCharacterInstantiated(GameObject character);
public static event OnCharacterInstantiated CharacterInstantiated;
[SerializeField]
private byte manualInstantiationEventCode = 1;
protected int lastUsedSpawnPointIndex = -1;
#pragma warning disable 649
[SerializeField]
private bool manualInstantiation;
[SerializeField]
private bool differentPrefabs;
[SerializeField] private string localPrefabSuffix;
[SerializeField] private string remotePrefabSuffix;
#pragma warning restore 649
public override void OnJoinedRoom()
{
if (!this.AutoSpawn)
{
return;
}
if (this.PrefabsToInstantiate != null)
{
int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
if (actorNumber < 1)
{
actorNumber = 1;
}
int index = (actorNumber - 1) % this.PrefabsToInstantiate.Length;
Vector3 spawnPos;
Quaternion spawnRotation;
this.GetSpawnPoint(out spawnPos, out spawnRotation);
Camera.main.transform.position += spawnPos;
if (this.manualInstantiation)
{
this.ManualInstantiation(index, spawnPos, spawnRotation);
}
else
{
GameObject o = this.PrefabsToInstantiate[index];
o = PhotonNetwork.Instantiate(o.name, spawnPos, spawnRotation);
if (CharacterInstantiated != null)
{
CharacterInstantiated(o);
}
}
}
}
private void ManualInstantiation(int index, Vector3 position, Quaternion rotation)
{
GameObject prefab = this.PrefabsToInstantiate[index];
GameObject player;
if (this.differentPrefabs)
{
player = Instantiate(Resources.Load(string.Format("{0}{1}", prefab.name, this.localPrefabSuffix)) as GameObject, position, rotation);
}
else
{
player = Instantiate(prefab, position, rotation);
}
PhotonView photonView = player.GetComponent<PhotonView>();
if (PhotonNetwork.AllocateViewID(photonView))
{
object[] data =
{
index, player.transform.position, player.transform.rotation, photonView.ViewID
};
RaiseEventOptions raiseEventOptions = new RaiseEventOptions
{
Receivers = ReceiverGroup.Others,
CachingOption = EventCaching.AddToRoomCache
};
PhotonNetwork.RaiseEvent(this.manualInstantiationEventCode, data, raiseEventOptions, SendOptions.SendReliable);
if (CharacterInstantiated != null)
{
CharacterInstantiated(player);
}
}
else
{
Debug.LogError("Failed to allocate a ViewId.");
Destroy(player);
}
}
public void OnEvent(EventData photonEvent)
{
if (photonEvent.Code == this.manualInstantiationEventCode)
{
object[] data = photonEvent.CustomData as object[];
int prefabIndex = (int) data[0];
GameObject prefab = this.PrefabsToInstantiate[prefabIndex];
Vector3 position = (Vector3)data[1];
Quaternion rotation = (Quaternion)data[2];
GameObject player;
if (this.differentPrefabs)
{
player = Instantiate(Resources.Load(string.Format("{0}{1}", prefab.name, this.remotePrefabSuffix)) as GameObject, position, rotation);
}
else
{
player = Instantiate(prefab, position, Quaternion.identity);
}
PhotonView photonView = player.GetComponent<PhotonView>();
photonView.ViewID = (int) data[3];
}
}
#if UNITY_EDITOR
protected void OnValidate()
{
// Move any values from old SpawnPosition field to new SpawnPositions
if (this.SpawnPosition)
{
if (this.SpawnPoints == null)
this.SpawnPoints = new List<Transform>();
this.SpawnPoints.Add(this.SpawnPosition);
this.SpawnPosition = null;
}
}
#endif
/// <summary>
/// Override this method with any custom code for coming up with a spawn location.
/// </summary>
protected virtual void GetSpawnPoint(out Vector3 spawnPos, out Quaternion spawnRot)
{
// Fetch a point using the Sequence method indicated
Transform point = this.GetSpawnPoint();
if (point != null)
{
spawnPos = point.position;
spawnRot = point.rotation;
}
else
{
spawnPos = new Vector3(0, 0, 0);
spawnRot = new Quaternion(0, 0, 0, 1);
}
if (this.UseRandomOffset)
{
Random.InitState((int)(Time.time * 10000));
Vector3 random = Random.insideUnitSphere;
random.y = 0;
random = random.normalized;
spawnPos += this.PositionOffset * random;
}
}
protected virtual Transform GetSpawnPoint()
{
// Fetch a point using the Sequence method indicated
if (this.SpawnPoints == null || this.SpawnPoints.Count == 0)
{
return null;
}
switch (this.Sequence)
{
case SpawnSequence.Connection:
{
int id = PhotonNetwork.LocalPlayer.ActorNumber;
return this.SpawnPoints[(id == -1) ? 0 : id % this.SpawnPoints.Count];
}
case SpawnSequence.RoundRobin:
{
this.lastUsedSpawnPointIndex++;
if (this.lastUsedSpawnPointIndex >= this.SpawnPoints.Count)
{
this.lastUsedSpawnPointIndex = 0;
}
// Use Vector.Zero and Quaternion.Identity if we are dealing with no or a null spawnpoint.
return this.SpawnPoints[this.lastUsedSpawnPointIndex];
}
case SpawnSequence.Random:
{
return this.SpawnPoints[Random.Range(0, this.SpawnPoints.Count)];
}
default:
return null;
}
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(CharacterInstantiation))]
public class CharacterInstantiationEditor : Editor
{
private SerializedProperty spawnPoints, prefabsToInstantiate, useRandomOffset, positionOffset, autoSpawn, manualInstantiation, differentPrefabs, localPrefabSuffix, remotePrefabSuffix, sequence, manualInstantiationEventCode;
private GUIStyle fieldBox;
private const int PAD = 6;
private void OnEnable()
{
this.spawnPoints = this.serializedObject.FindProperty("SpawnPoints");
this.prefabsToInstantiate = this.serializedObject.FindProperty("PrefabsToInstantiate");
this.useRandomOffset = this.serializedObject.FindProperty("UseRandomOffset");
this.positionOffset = this.serializedObject.FindProperty("PositionOffset");
this.autoSpawn = this.serializedObject.FindProperty("AutoSpawn");
this.manualInstantiation = this.serializedObject.FindProperty("manualInstantiation");
this.differentPrefabs = this.serializedObject.FindProperty("differentPrefabs");
this.localPrefabSuffix = this.serializedObject.FindProperty("localPrefabSuffix");
this.remotePrefabSuffix = this.serializedObject.FindProperty("remotePrefabSuffix");
this.manualInstantiationEventCode = this.serializedObject.FindProperty("manualInstantiationEventCode");
this.sequence = this.serializedObject.FindProperty("Sequence");
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
this.EditableReferenceList(this.prefabsToInstantiate, new GUIContent(this.prefabsToInstantiate.displayName, this.prefabsToInstantiate.tooltip), this.fieldBox);
this.EditableReferenceList(this.spawnPoints, new GUIContent(this.spawnPoints.displayName, this.spawnPoints.tooltip), this.fieldBox);
if (this.fieldBox == null)
{
this.fieldBox = new GUIStyle("HelpBox") { padding = new RectOffset(PAD, PAD, PAD, PAD) };
}
// Spawn Pattern
EditorGUILayout.LabelField("Spawn Pattern Setup");
EditorGUILayout.BeginVertical(this.fieldBox);
EditorGUILayout.PropertyField(this.sequence);
EditorGUILayout.PropertyField(this.useRandomOffset);
if (this.useRandomOffset.boolValue)
{
EditorGUILayout.PropertyField(this.positionOffset);
}
EditorGUILayout.EndVertical();
// Auto/Manual Spawn
EditorGUILayout.LabelField("Network Instantiation Setup");
EditorGUILayout.BeginVertical(this.fieldBox);
EditorGUILayout.PropertyField(this.autoSpawn);
EditorGUILayout.PropertyField(this.manualInstantiation);
if (this.manualInstantiation.boolValue)
{
EditorGUILayout.PropertyField(this.manualInstantiationEventCode);
EditorGUILayout.PropertyField(this.differentPrefabs);
if (this.differentPrefabs.boolValue)
{
EditorGUILayout.PropertyField(this.localPrefabSuffix);
EditorGUILayout.PropertyField(this.remotePrefabSuffix);
}
}
EditorGUILayout.EndVertical();
if (EditorGUI.EndChangeCheck())
{
this.serializedObject.ApplyModifiedProperties();
}
}
/// <summary>
/// Create a basic rendered list of objects from a SerializedProperty list or array, with Add/Destroy buttons.
/// </summary>
public void EditableReferenceList(SerializedProperty list, GUIContent gc, GUIStyle style = null)
{
EditorGUILayout.LabelField(gc);
if (style == null)
style = new GUIStyle("HelpBox") { padding = new RectOffset(6, 6, 6, 6) };
EditorGUILayout.BeginVertical(style);
int count = list.arraySize;
if (count == 0)
{
if (GUI.Button(EditorGUILayout.GetControlRect(GUILayout.MaxWidth(20)), "+", (GUIStyle)"minibutton"))
{
list.InsertArrayElementAtIndex(0);
list.GetArrayElementAtIndex(0).objectReferenceValue = null;
}
}
else
{
// List Elements and Delete buttons
for (int i = 0; i < list.arraySize; ++i)
{
EditorGUILayout.BeginHorizontal();
bool add = (GUI.Button(EditorGUILayout.GetControlRect(GUILayout.MaxWidth(20)), "+", (GUIStyle)"minibutton"));
EditorGUILayout.PropertyField(list.GetArrayElementAtIndex(i), GUIContent.none);
bool remove = (GUI.Button(EditorGUILayout.GetControlRect(GUILayout.MaxWidth(20)), "x", (GUIStyle)"minibutton"));
EditorGUILayout.EndHorizontal();
if (add)
{
list.InsertArrayElementAtIndex(i);
list.GetArrayElementAtIndex(i).objectReferenceValue = null;
EditorGUILayout.EndHorizontal();
break;
}
if (remove)
{
list.DeleteArrayElementAtIndex(i);
EditorGUILayout.EndHorizontal();
break;
}
}
}
EditorGUILayout.EndVertical();
}
}
#endif
}