This commit is contained in:
2025-06-16 15:14:23 +02:00
committed by devbeni
parent 60fe4620ff
commit 4ff561284f
3174 changed files with 428263 additions and 0 deletions

View File

@ -0,0 +1,194 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using static Mirror.Examples.CharacterSelection.NetworkManagerCharacterSelection;
namespace Mirror.Examples.CharacterSelection
{
public class CanvasReferencer : MonoBehaviour
{
// Make sure to attach these Buttons in the Inspector
public Button buttonExit, buttonNextCharacter, buttonGo, buttonColour, buttonColourReset;
public Text textTitle, textHealth, textSpeed, textAttack, textAbilities;
public InputField inputFieldPlayerName;
public Transform podiumPosition;
private int currentlySelectedCharacter = 1;
private CharacterData characterData;
private GameObject currentInstantiatedCharacter;
private CharacterSelection characterSelection;
public SceneReferencer sceneReferencer;
public Camera cameraObj;
private void Start()
{
characterData = CharacterData.characterDataSingleton;
if (characterData == null)
{
Debug.Log("Add CharacterData prefab singleton into the scene.");
return;
}
buttonExit.onClick.AddListener(ButtonExit);
buttonNextCharacter.onClick.AddListener(ButtonNextCharacter);
buttonGo.onClick.AddListener(ButtonGo);
buttonColour.onClick.AddListener(ButtonColour);
buttonColourReset.onClick.AddListener(ButtonColourReset);
//Adds a listener to the main input field and invokes a method when the value changes.
inputFieldPlayerName.onValueChanged.AddListener(delegate { InputFieldChangedPlayerName(); });
LoadData();
SetupCharacters();
}
public void ButtonExit()
{
//Debug.Log("ButtonExit");
if (sceneReferencer)
{
sceneReferencer.CloseCharacterSelection();
}
}
public void ButtonGo()
{
//Debug.Log("ButtonGo");
// presumes we're already in-game
if (sceneReferencer && NetworkClient.active)
{
// You could check if prefab (character number) has not changed, and if so just update the sync vars and hooks of current prefab, this would call a command from your player.
// this is not fully setup for this example, but provides a minor template to follow if needed
//NetworkClient.localPlayer.GetComponent<CharacterSelection>().CmdSetupCharacter(StaticVariables.playerName, StaticVariables.characterColour);
CreateCharacterMessage _characterMessage = new CreateCharacterMessage
{
playerName = StaticVariables.playerName,
characterNumber = StaticVariables.characterNumber,
characterColour = StaticVariables.characterColour
};
ReplaceCharacterMessage replaceCharacterMessage = new ReplaceCharacterMessage
{
createCharacterMessage = _characterMessage
};
NetworkManagerCharacterSelection.singleton.ReplaceCharacter(replaceCharacterMessage);
sceneReferencer.CloseCharacterSelection();
}
else
{
// not in-game
SceneManager.LoadScene("MirrorCharacterSelection");
}
}
public void ButtonNextCharacter()
{
//Debug.Log("ButtonNextCharacter");
currentlySelectedCharacter += 1;
if (currentlySelectedCharacter >= characterData.characterPrefabs.Length)
{
currentlySelectedCharacter = 1;
}
SetupCharacters();
StaticVariables.characterNumber = currentlySelectedCharacter;
}
public void ButtonColour()
{
//Debug.Log("ButtonColour");
StaticVariables.characterColour = Random.ColorHSV(0f, 1f, 1f, 1f, 0f, 1f);
SetupCharacterColours();
}
public void ButtonColourReset()
{
//Debug.Log("ButtonColourReset ");
StaticVariables.characterColour = new Color(0, 0, 0, 0);
SetupCharacters();
}
private void SetupCharacters()
{
textTitle.text = "" + characterData.characterTitles[currentlySelectedCharacter];
textHealth.text = "Health: " + characterData.characterHealths[currentlySelectedCharacter];
textSpeed.text = "Speed: " + characterData.characterSpeeds[currentlySelectedCharacter];
textAttack.text = "Attack: " + characterData.characterAttack[currentlySelectedCharacter];
textAbilities.text = "Abilities:\n" + characterData.characterAbilities[currentlySelectedCharacter];
if (currentInstantiatedCharacter)
{
Destroy(currentInstantiatedCharacter);
}
currentInstantiatedCharacter = Instantiate(characterData.characterPrefabs[currentlySelectedCharacter]);
currentInstantiatedCharacter.transform.position = podiumPosition.position;
currentInstantiatedCharacter.transform.rotation = podiumPosition.rotation;
characterSelection = currentInstantiatedCharacter.GetComponent<CharacterSelection>();
currentInstantiatedCharacter.transform.SetParent(this.transform.root);
SetupCharacterColours();
SetupPlayerName();
if (cameraObj)
{
characterSelection.floatingInfo.forward = cameraObj.transform.forward;
}
}
public void SetupCharacterColours()
{
// Debug.Log("SetupCharacterColours");
if (StaticVariables.characterColour != new Color(0, 0, 0, 0))
{
characterSelection.characterColour = StaticVariables.characterColour;
characterSelection.AssignColours();
}
}
public void InputFieldChangedPlayerName()
{
//Debug.Log("InputFieldChangedPlayerName");
StaticVariables.playerName = inputFieldPlayerName.text;
SetupPlayerName();
}
public void SetupPlayerName()
{
//Debug.Log("SetupPlayerName");
if (characterSelection)
{
characterSelection.playerName = StaticVariables.playerName;
characterSelection.AssignName();
}
}
public void LoadData()
{
// check if the static save data has been pre-set
if (StaticVariables.playerName != "")
{
if (inputFieldPlayerName)
{
inputFieldPlayerName.text = StaticVariables.playerName;
}
}
else
{
StaticVariables.playerName = "Player Name";
}
// check that prefab is set, or exists for saved character number data
if (StaticVariables.characterNumber > 0 && StaticVariables.characterNumber < characterData.characterPrefabs.Length)
{
currentlySelectedCharacter = StaticVariables.characterNumber;
}
else
{
StaticVariables.characterNumber = currentlySelectedCharacter;
}
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: faef105eb77a94bbaacfe57f48968e19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/CanvasReferencer.cs
uploadId: 736421

View File

@ -0,0 +1,25 @@
using UnityEngine;
namespace Mirror.Examples.CharacterSelection
{
public class CharacterData : MonoBehaviour
{
// A reference data script for most things character and customisation related.
public static CharacterData characterDataSingleton { get; private set; }
public GameObject[] characterPrefabs;
public string[] characterTitles;
public int[] characterHealths;
public float[] characterSpeeds;
public int[] characterAttack;
public string[] characterAbilities;
public void Awake()
{
characterDataSingleton = this;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f5fc49087bdc848b2aefe5c91858c7b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/CharacterData.cs
uploadId: 736421

View File

@ -0,0 +1,62 @@
using UnityEngine;
using Mirror;
namespace Mirror.Examples.CharacterSelection
{
public class CharacterSelection : NetworkBehaviour
{
public Transform floatingInfo;
[SyncVar]
public int characterNumber = 0;
public TextMesh textMeshName;
[SyncVar(hook = nameof(HookSetName))]
public string playerName = "";
void HookSetName(string _old, string _new)
{
//Debug.Log("HookSetName");
AssignName();
}
[SyncVar(hook = nameof(HookSetColor))]
public Color characterColour;
private Material cachedMaterial;
public MeshRenderer[] characterRenderers;
void HookSetColor(Color _old, Color _new)
{
//Debug.Log("HookSetColor");
AssignColours();
}
public void AssignColours()
{
foreach (MeshRenderer meshRenderer in characterRenderers)
{
cachedMaterial = meshRenderer.material;
cachedMaterial.color = characterColour;
}
}
void OnDestroy()
{
if (cachedMaterial) { Destroy(cachedMaterial); }
}
public void AssignName()
{
textMeshName.text = playerName;
}
// To change server controlled sync vars, clients end Commands, and the hooks will fire
// Although not used in this example, we could change some character aspects without replacing current prefab.
//[Command]
//public void CmdSetupCharacter(string _playerName, Color _characterColour)
//{
// playerName = _playerName;
// characterColour = _characterColour;
//}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e04a8cb02afdc4e778925020d233d718
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/CharacterSelection.cs
uploadId: 736421

View File

@ -0,0 +1,135 @@
using UnityEngine;
namespace Mirror.Examples.CharacterSelection
{
[AddComponentMenu("")]
public class NetworkManagerCharacterSelection : NetworkManager
{
// See the scene 'SceneMapSpawnWithNoCharacter', to spawn as empty player.
// 'SceneMap' will auto spawn as random player character.
// Compare Network Manager inspector setups to see the difference between the two.
// Either of these allow selecting character after spawning in too.
public bool SpawnAsCharacter = true;
public static new NetworkManagerCharacterSelection singleton => (NetworkManagerCharacterSelection)NetworkManager.singleton;
private CharacterData characterData;
public override void Awake()
{
characterData = CharacterData.characterDataSingleton;
if (characterData == null)
{
Debug.Log("Add CharacterData prefab singleton into the scene.");
return;
}
base.Awake();
}
public struct CreateCharacterMessage : NetworkMessage
{
public string playerName;
public int characterNumber;
public Color characterColour;
}
public struct ReplaceCharacterMessage : NetworkMessage
{
public CreateCharacterMessage createCharacterMessage;
}
public override void OnStartServer()
{
base.OnStartServer();
NetworkServer.RegisterHandler<CreateCharacterMessage>(OnCreateCharacter);
NetworkServer.RegisterHandler<ReplaceCharacterMessage>(OnReplaceCharacterMessage);
}
public override void OnClientConnect()
{
base.OnClientConnect();
if (SpawnAsCharacter)
{
// you can send the message here, or wherever else you want
CreateCharacterMessage characterMessage = new CreateCharacterMessage
{
playerName = StaticVariables.playerName,
characterNumber = StaticVariables.characterNumber,
characterColour = StaticVariables.characterColour
};
NetworkClient.Send(characterMessage);
}
}
void OnCreateCharacter(NetworkConnectionToClient conn, CreateCharacterMessage message)
{
Transform startPos = GetStartPosition();
// check if the save data has been pre-set
if (message.playerName == "")
{
Debug.Log("OnCreateCharacter name invalid or not set, use random.");
message.playerName = "Player: " + UnityEngine.Random.Range(100, 1000);
}
// check that prefab is set, or exists for saved character number data
// could be a cheater, or coding error, or different version conflict
if (message.characterNumber <= 0 || message.characterNumber >= characterData.characterPrefabs.Length)
{
Debug.Log("OnCreateCharacter prefab Invalid or not set, use random.");
message.characterNumber = UnityEngine.Random.Range(1, characterData.characterPrefabs.Length);
}
// check if the save data has been pre-set
if (message.characterColour == new Color(0, 0, 0, 0))
{
Debug.Log("OnCreateCharacter colour invalid or not set, use random.");
message.characterColour = Random.ColorHSV(0f, 1f, 1f, 1f, 0f, 1f);
}
GameObject playerObject = startPos != null
? Instantiate(characterData.characterPrefabs[message.characterNumber], startPos.position, startPos.rotation)
: Instantiate(characterData.characterPrefabs[message.characterNumber]);
// Apply data from the message however appropriate for your game
// Typically Player would be a component you write with syncvars or properties
CharacterSelection characterSelection = playerObject.GetComponent<CharacterSelection>();
characterSelection.playerName = message.playerName;
characterSelection.characterNumber = message.characterNumber;
characterSelection.characterColour = message.characterColour;
// call this to use this gameobject as the primary controller
NetworkServer.AddPlayerForConnection(conn, playerObject);
}
void OnReplaceCharacterMessage(NetworkConnectionToClient conn, ReplaceCharacterMessage message)
{
// Cache a reference to the current player object
GameObject oldPlayer = conn.identity.gameObject;
GameObject playerObject = Instantiate(characterData.characterPrefabs[message.createCharacterMessage.characterNumber], oldPlayer.transform.position, oldPlayer.transform.rotation);
// Instantiate the new player object and broadcast to clients
NetworkServer.ReplacePlayerForConnection(conn, playerObject, ReplacePlayerOptions.KeepActive);
// Apply data from the message however appropriate for your game
// Typically Player would be a component you write with syncvars or properties
CharacterSelection characterSelection = playerObject.GetComponent<CharacterSelection>();
characterSelection.playerName = message.createCharacterMessage.playerName;
characterSelection.characterNumber = message.createCharacterMessage.characterNumber;
characterSelection.characterColour = message.createCharacterMessage.characterColour;
// Remove the previous player object that's now been replaced
// Delay is required to allow replacement to complete.
Destroy(oldPlayer, 0.1f);
}
public void ReplaceCharacter(ReplaceCharacterMessage message)
{
NetworkClient.Send(message);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 98c582433a349434990d734c5586d722
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/NetworkManagerCharacterSelection.cs
uploadId: 736421

View File

@ -0,0 +1,22 @@
using UnityEngine;
using Mirror;
namespace Mirror.Examples.CharacterSelection
{
public class PlayerEmpty : NetworkBehaviour
{
private SceneReferencer sceneReferencer;
public override void OnStartAuthority()
{
// enable UI located in the scene, after empty player spawns in.
#if UNITY_2022_2_OR_NEWER
sceneReferencer = GameObject.FindAnyObjectByType<SceneReferencer>();
#else
// Deprecated in Unity 2023.1
sceneReferencer = GameObject.FindObjectOfType<SceneReferencer>();
#endif
sceneReferencer.GetComponent<Canvas>().enabled = true;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ef22691ec32ce4b5f8dd75e8330b07ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/PlayerEmpty.cs
uploadId: 736421

View File

@ -0,0 +1,59 @@
using UnityEngine;
namespace Mirror.Examples.CharacterSelection
{
public class SceneCamera : NetworkBehaviour
{
[Header("Components")]
[SerializeField] CharacterSelection characterSelection;
[SerializeField] Transform cameraTarget;
[Header("Diagnostics")]
[ReadOnly, SerializeField] SceneReferencer sceneReferencer;
[ReadOnly, SerializeField] Transform cameraObj;
protected override void OnValidate()
{
base.OnValidate();
Reset();
}
void Reset()
{
characterSelection = GetComponent<CharacterSelection>();
cameraTarget = transform.Find("CameraTarget");
this.enabled = false;
}
public override void OnStartAuthority()
{
#if UNITY_2022_2_OR_NEWER
sceneReferencer = GameObject.FindAnyObjectByType<SceneReferencer>();
#else
// Deprecated in Unity 2023.1
sceneReferencer = GameObject.FindObjectOfType<SceneReferencer>();
#endif
cameraObj = sceneReferencer.cameraObject.transform;
this.enabled = true;
}
public override void OnStopAuthority()
{
this.enabled = false;
}
void Update()
{
if (!Application.isFocused)
return;
if (cameraObj && characterSelection)
characterSelection.floatingInfo.forward = cameraObj.transform.forward;
if (cameraObj && cameraTarget)
cameraObj.SetPositionAndRotation(cameraTarget.position, cameraTarget.rotation);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: db9418a9e709a7844acac42de096cba6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/SceneCamera.cs
uploadId: 736421

View File

@ -0,0 +1,47 @@
using UnityEngine;
using UnityEngine.UI;
namespace Mirror.Examples.CharacterSelection
{
public class SceneReferencer : MonoBehaviour
{
// Make sure to attach these Buttons in the Inspector
public Button buttonCharacterSelection;
private CharacterData characterData;
public GameObject characterSelectionObject;
public GameObject sceneObjects;
public GameObject cameraObject;
private void Start()
{
characterData = CharacterData.characterDataSingleton;
if (characterData == null)
{
Debug.Log("Add CharacterData prefab singleton into the scene.");
return;
}
buttonCharacterSelection.onClick.AddListener(ButtonCharacterSelection);
}
public void ButtonCharacterSelection()
{
// server-only mode should not press this button
//Debug.Log("ButtonCharacterSelection");
cameraObject.SetActive(false);
sceneObjects.SetActive(false);
characterSelectionObject.SetActive(true);
this.GetComponent<Canvas>().enabled = false;
}
public void CloseCharacterSelection()
{
//Debug.Log("CloseCharacterSelection");
cameraObject.SetActive(true);
characterSelectionObject.SetActive(false);
sceneObjects.SetActive(true);
this.GetComponent<Canvas>().enabled = true;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f45e025c29e20480cb3d9ab86918814a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/SceneReferencer.cs
uploadId: 736421

View File

@ -0,0 +1,33 @@
using UnityEngine;
namespace Mirror.Examples.CharacterSelection
{
// A fun little bob script for characters.
// You could reference this and change values depending on characters state, idle, walk, run.
public class ScriptAnimations : MonoBehaviour
{
public float minimum = 0.1f;
public float maximum = 0.5f;
private float yPos;
public float bounceSpeed = 3;
private float yStartPosition;
private void Start()
{
yStartPosition = this.transform.localPosition.y;
}
void Update()
{
float sinValue = Mathf.Sin(Time.time * bounceSpeed);
yPos = Mathf.Lerp(maximum, minimum, Mathf.Abs((1.0f + sinValue) / 2.0f));
transform.localPosition = new Vector3(transform.localPosition.x, yStartPosition + yPos, transform.localPosition.z);
}
}
//credits https://stackoverflow.com/questions/67322860/how-do-i-make-a-simple-idle-bobbing-motion-animation
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0f36e96df6ff2459992432717c975c84
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/ScriptAnimations.cs
uploadId: 736421

View File

@ -0,0 +1,13 @@
using UnityEngine;
namespace Mirror.Examples.CharacterSelection
{
// we will use static variables to pass data between scenes
// this could also be done using other methods
public class StaticVariables : MonoBehaviour
{
public static string playerName = "";
public static int characterNumber = 0;
public static Color characterColour;
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1d335b50444484132bf9affd60327c5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/CharacterSelection/Scripts/StaticVariables.cs
uploadId: 736421