Multiplayer Updated

This commit is contained in:
kingjuulian06
2023-12-22 14:41:12 +01:00
parent 2b2abe5eaa
commit f042410dab
1778 changed files with 165193 additions and 0 deletions
@@ -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;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: faef105eb77a94bbaacfe57f48968e19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5fc49087bdc848b2aefe5c91858c7b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
//}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e04a8cb02afdc4e778925020d233d718
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,136 @@
using UnityEngine;
namespace Mirror.Examples.CharacterSelection
{
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 { get; private set; }
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();
singleton = this;
}
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
// Include true for keepAuthority paramater to prevent ownership change
NetworkServer.ReplacePlayerForConnection(conn, playerObject, true);
// 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);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98c582433a349434990d734c5586d722
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,213 @@
using UnityEngine;
using Mirror;
namespace Mirror.Examples.CharacterSelection
{
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(NetworkTransformUnreliable))]
[RequireComponent(typeof(Rigidbody))]
public class PlayerControllerScript : NetworkBehaviour
{
public enum GroundState : byte { Jumping, Falling, Grounded }
[Header("Avatar Components")]
public CharacterController characterController;
[Header("Movement")]
[Range(1, 20)]
public float moveSpeedMultiplier = 8f;
[Header("Turning")]
[Range(1f, 200f)]
public float maxTurnSpeed = 100f;
[Range(.5f, 5f)]
public float turnDelta = 3f;
[Header("Jumping")]
[Range(0.1f, 1f)]
public float initialJumpSpeed = 0.2f;
[Range(1f, 10f)]
public float maxJumpSpeed = 5f;
[Range(0.1f, 1f)]
public float jumpDelta = 0.2f;
[Header("Diagnostics - Do Not Modify")]
public GroundState groundState = GroundState.Grounded;
[Range(-1f, 1f)]
public float horizontal;
[Range(-1f, 1f)]
public float vertical;
[Range(-200f, 200f)]
public float turnSpeed;
[Range(-10f, 10f)]
public float jumpSpeed;
[Range(-1.5f, 1.5f)]
public float animVelocity;
[Range(-1.5f, 1.5f)]
public float animRotation;
public Vector3Int velocity;
public Vector3 direction;
private Transform cameraObj;
public Transform cameraTarget;
private SceneReferencer sceneReferencer;
private CharacterSelection characterSelection;
public void Awake()
{
if (characterController == null)
characterController = GetComponent<CharacterController>();
if (characterSelection == null)
characterSelection = GetComponent<CharacterSelection>();
// Override CharacterController default values
characterController.enabled = false;
characterController.skinWidth = 0.02f;
characterController.minMoveDistance = 0f;
GetComponent<Rigidbody>().isKinematic = true;
this.enabled = false;
}
public override void OnStartAuthority()
{
characterController.enabled = true;
this.enabled = true;
#if UNITY_2021_3_OR_NEWER
sceneReferencer = GameObject.FindAnyObjectByType<SceneReferencer>();
#else
// Deprecated in Unity 2023.1
sceneReferencer = GameObject.FindObjectOfType<SceneReferencer>();
#endif
cameraObj = sceneReferencer.cameraObject.transform;
}
public override void OnStopAuthority()
{
this.enabled = false;
characterController.enabled = false;
}
void Update()
{
if (cameraObj && characterSelection)
{
characterSelection.floatingInfo.forward = cameraObj.transform.forward;
}
if (!Application.isFocused) return;
if (!characterController.enabled)
return;
HandleTurning();
HandleJumping();
HandleMove();
// Reset ground state
if (characterController.isGrounded)
groundState = GroundState.Grounded;
else if (groundState != GroundState.Jumping)
groundState = GroundState.Falling;
// Diagnostic velocity...FloorToInt for display purposes
velocity = Vector3Int.FloorToInt(characterController.velocity);
if (cameraObj != null)
{
cameraObj.position = cameraTarget.position;
cameraObj.rotation = cameraTarget.rotation;
}
}
// TODO: Turning works while airborne...feature?
void HandleTurning()
{
// Q and E cancel each other out, reducing the turn to zero.
if (Input.GetKey(KeyCode.Q))
turnSpeed = Mathf.MoveTowards(turnSpeed, -maxTurnSpeed, turnDelta);
if (Input.GetKey(KeyCode.E))
turnSpeed = Mathf.MoveTowards(turnSpeed, maxTurnSpeed, turnDelta);
// If both pressed, reduce turning speed toward zero.
if (Input.GetKey(KeyCode.Q) && Input.GetKey(KeyCode.E))
turnSpeed = Mathf.MoveTowards(turnSpeed, 0, turnDelta);
// If neither pressed, reduce turning speed toward zero.
if (!Input.GetKey(KeyCode.Q) && !Input.GetKey(KeyCode.E))
turnSpeed = Mathf.MoveTowards(turnSpeed, 0, turnDelta);
transform.Rotate(0f, turnSpeed * Time.deltaTime, 0f);
}
void HandleJumping()
{
// Handle variable force jumping.
// Jump starts with initial power on takeoff, and jumps higher / longer
// as player holds spacebar. Jump power is increased by a diminishing amout
// every frame until it reaches maxJumpSpeed, or player releases the spacebar,
// and then changes to the falling state until it gets grounded.
if (groundState != GroundState.Falling && Input.GetKey(KeyCode.Space))
{
if (groundState != GroundState.Jumping)
{
// Start jump at initial power.
groundState = GroundState.Jumping;
jumpSpeed = initialJumpSpeed;
}
else
// Jumping has already started...increase power toward maxJumpSpeed over time.
jumpSpeed = Mathf.MoveTowards(jumpSpeed, maxJumpSpeed, jumpDelta);
// If power has reached maxJumpSpeed, change to falling until grounded.
// This prevents over-applying jump power while already in the air.
if (jumpSpeed == maxJumpSpeed)
groundState = GroundState.Falling;
}
else if (groundState != GroundState.Grounded)
{
// handles running off a cliff and/or player released Spacebar.
groundState = GroundState.Falling;
jumpSpeed = Mathf.Min(jumpSpeed, maxJumpSpeed);
jumpSpeed += Physics.gravity.y * Time.deltaTime;
}
else
jumpSpeed = Physics.gravity.y * Time.deltaTime;
}
// TODO: Directional input works while airborne...feature?
void HandleMove()
{
// Capture inputs
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");
// Create initial direction vector without jumpSpeed (y-axis).
direction = new Vector3(horizontal, 0f, vertical);
// Clamp so diagonal strafing isn't a speed advantage.
direction = Vector3.ClampMagnitude(direction, 1f);
// Transforms direction from local space to world space.
direction = transform.TransformDirection(direction);
// Multiply for desired ground speed.
direction *= moveSpeedMultiplier;
// Add jumpSpeed to direction as last step.
direction.y = jumpSpeed;
// Finally move the character.
characterController.Move(direction * Time.deltaTime);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a507efeb4218499aaa7cd74f326f5de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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_2021_3_OR_NEWER
sceneReferencer = GameObject.FindAnyObjectByType<SceneReferencer>();
#else
// Deprecated in Unity 2023.1
sceneReferencer = GameObject.FindObjectOfType<SceneReferencer>();
#endif
sceneReferencer.GetComponent<Canvas>().enabled = true;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef22691ec32ce4b5f8dd75e8330b07ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f45e025c29e20480cb3d9ab86918814a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f36e96df6ff2459992432717c975c84
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d335b50444484132bf9affd60327c5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: