aha
This commit is contained in:
@ -0,0 +1,10 @@
|
||||
namespace Mirror.Examples.PickupsDropsChilds
|
||||
{
|
||||
public enum EquippedItem : byte
|
||||
{
|
||||
nothing,
|
||||
ball,
|
||||
bat,
|
||||
box
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bfef79b434bc424eacedbe92f3ca7e8
|
||||
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/PickupsDropsChilds/Scripts/Enumerations.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61617d75ae890064c89c20720fd50c0c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,67 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.PickupsDropsChilds
|
||||
{
|
||||
public class EquippedBall : MonoBehaviour, IEquipped
|
||||
{
|
||||
// Note: This example doesn't include animations or sounds for simplicity.
|
||||
// These are just here for illustration purposes...the implementation
|
||||
// methods could do something interesting like play a sound or animation.
|
||||
[Header("Components")]
|
||||
public Animator animator;
|
||||
public AudioSource audioSource;
|
||||
|
||||
[Header("Equipped Item")]
|
||||
[SerializeField]
|
||||
EquippedItemConfig _equippedItemConfig;
|
||||
|
||||
public EquippedItemConfig equippedItemConfig
|
||||
{
|
||||
get => _equippedItemConfig;
|
||||
set
|
||||
{
|
||||
Debug.Log($"{transform.root.name} EquippedItemConfig set from {_equippedItemConfig} to {value}", gameObject);
|
||||
_equippedItemConfig = value;
|
||||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
equippedItemConfig = new EquippedItemConfig { usages = 3, maxUsages = 3 };
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void Use()
|
||||
{
|
||||
// Effectively unlimited uses
|
||||
if (equippedItemConfig.maxUsages == 0)
|
||||
{
|
||||
Debug.Log("Ball used");
|
||||
return;
|
||||
}
|
||||
|
||||
if (equippedItemConfig.usages > 0)
|
||||
Debug.Log("Ball used");
|
||||
else
|
||||
Debug.Log("Ball is out of uses");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void AddUsages(byte usages)
|
||||
{
|
||||
Debug.Log($"Ball added {usages} usages");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void ResetUsages()
|
||||
{
|
||||
Debug.Log("Ball reset");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void ResetUsages(byte usages)
|
||||
{
|
||||
Debug.Log($"Ball reset usages to {usages}");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65881ca9607b5ea42b654a7ed2566027
|
||||
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/PickupsDropsChilds/Scripts/Interfaces/EquippedBall.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,67 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.PickupsDropsChilds
|
||||
{
|
||||
public class EquippedBat : MonoBehaviour, IEquipped
|
||||
{
|
||||
// Note: This example doesn't include animations or sounds for simplicity.
|
||||
// These are just here for illustration purposes...the implementation
|
||||
// methods could do something interesting like play a sound or animation.
|
||||
[Header("Components")]
|
||||
public Animator animator;
|
||||
public AudioSource audioSource;
|
||||
|
||||
[Header("Equipped Item")]
|
||||
[SerializeField]
|
||||
EquippedItemConfig _equippedItemConfig;
|
||||
|
||||
public EquippedItemConfig equippedItemConfig
|
||||
{
|
||||
get => _equippedItemConfig;
|
||||
set
|
||||
{
|
||||
Debug.Log($"{transform.root.name} EquippedItemConfig set from {_equippedItemConfig} to {value}", gameObject);
|
||||
_equippedItemConfig = value;
|
||||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
equippedItemConfig = new EquippedItemConfig { usages = 5, maxUsages = 5 };
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void Use()
|
||||
{
|
||||
// Effectively unlimited uses
|
||||
if (equippedItemConfig.maxUsages == 0)
|
||||
{
|
||||
Debug.Log("Bat used");
|
||||
return;
|
||||
}
|
||||
|
||||
if (equippedItemConfig.usages > 0)
|
||||
Debug.Log("Bat used");
|
||||
else
|
||||
Debug.Log("Bat is out of uses");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void AddUsages(byte usages)
|
||||
{
|
||||
Debug.Log($"Bat added {usages} usages");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void ResetUsages()
|
||||
{
|
||||
Debug.Log("Bat reset");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void ResetUsages(byte usages)
|
||||
{
|
||||
Debug.Log($"Bat reset usages to {usages}");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35ca3b53a1e608f46ae9195e9d8cae83
|
||||
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/PickupsDropsChilds/Scripts/Interfaces/EquippedBat.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,67 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.PickupsDropsChilds
|
||||
{
|
||||
public class EquippedBox : MonoBehaviour, IEquipped
|
||||
{
|
||||
// Note: This example doesn't include animations or sounds for simplicity.
|
||||
// These are just here for illustration purposes...the implementation
|
||||
// methods could do something interesting like play a sound or animation.
|
||||
[Header("Components")]
|
||||
public Animator animator;
|
||||
public AudioSource audioSource;
|
||||
|
||||
[Header("Equipped Item")]
|
||||
[SerializeField]
|
||||
EquippedItemConfig _equippedItemConfig;
|
||||
|
||||
public EquippedItemConfig equippedItemConfig
|
||||
{
|
||||
get => _equippedItemConfig;
|
||||
set
|
||||
{
|
||||
Debug.Log($"{transform.root.name} EquippedItemConfig set from {_equippedItemConfig} to {value}", gameObject);
|
||||
_equippedItemConfig = value;
|
||||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
equippedItemConfig = new EquippedItemConfig { usages = 0, maxUsages = 0 };
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void Use()
|
||||
{
|
||||
// Effectively unlimited uses
|
||||
if (equippedItemConfig.maxUsages == 0)
|
||||
{
|
||||
Debug.Log("Box used");
|
||||
return;
|
||||
}
|
||||
|
||||
if (equippedItemConfig.usages > 0)
|
||||
Debug.Log("Box used");
|
||||
else
|
||||
Debug.Log("Box is out of uses");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void AddUsages(byte usages)
|
||||
{
|
||||
Debug.Log($"Box added {usages} usages");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void ResetUsages()
|
||||
{
|
||||
Debug.Log("Box reset");
|
||||
}
|
||||
|
||||
// Play appropriate animation or sound
|
||||
public void ResetUsages(byte usages)
|
||||
{
|
||||
Debug.Log($"Box reset usages to {usages}");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5712a4d15653a184cb34b9c1e044563d
|
||||
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/PickupsDropsChilds/Scripts/Interfaces/EquippedBox.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,74 @@
|
||||
namespace Mirror.Examples.PickupsDropsChilds
|
||||
{
|
||||
interface IEquipped
|
||||
{
|
||||
EquippedItemConfig equippedItemConfig { get; set; }
|
||||
|
||||
void Use();
|
||||
void AddUsages(byte usages);
|
||||
void ResetUsages();
|
||||
void ResetUsages(byte usages);
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct EquippedItemConfig : System.IEquatable<EquippedItemConfig>
|
||||
{
|
||||
// Usages remaining...this could be ammo, potion doses, magic item charges, etc.
|
||||
public byte usages;
|
||||
|
||||
// Maximum usages...set to 0 for effectively unlimited uses
|
||||
public byte maxUsages;
|
||||
|
||||
public EquippedItemConfig(byte maxUsages)
|
||||
{
|
||||
usages = maxUsages;
|
||||
this.maxUsages = maxUsages;
|
||||
}
|
||||
|
||||
public EquippedItemConfig(byte usages, byte maxUsages)
|
||||
{
|
||||
this.usages = usages;
|
||||
this.maxUsages = maxUsages;
|
||||
}
|
||||
|
||||
public void Use()
|
||||
{
|
||||
// Reset usages to within allowed range in case higher than maxUsages
|
||||
ResetUsages(usages);
|
||||
|
||||
// if we have usages left, decrement
|
||||
if (usages > 0)
|
||||
usages--;
|
||||
}
|
||||
|
||||
// Add a specific number of usages
|
||||
public void AddUsages(byte usages)
|
||||
{
|
||||
// Limit usages to maxUsages
|
||||
this.usages = (byte)Mathd.Clamp(this.usages + usages, 0, maxUsages);
|
||||
}
|
||||
|
||||
// Fully reload to max usages
|
||||
public void ResetUsages()
|
||||
{
|
||||
this.usages = maxUsages;
|
||||
}
|
||||
|
||||
// Reload to a specific number of usages
|
||||
public void ResetUsages(byte usages)
|
||||
{
|
||||
// Limit usages to maxUsages
|
||||
this.usages = (byte)Mathd.Clamp(usages, 0, maxUsages);
|
||||
}
|
||||
|
||||
public bool Equals(EquippedItemConfig other)
|
||||
{
|
||||
return usages == other.usages && maxUsages == other.maxUsages;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"EquippedItemConfig[{usages}/{maxUsages}]";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b85cf93aed753aa448c8cf58ae3bb79d
|
||||
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/PickupsDropsChilds/Scripts/Interfaces/IEquipped.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,259 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.PickupsDropsChilds
|
||||
{
|
||||
public class PickupsDropsChilds : NetworkBehaviour
|
||||
{
|
||||
[Header("Player Components")]
|
||||
public GameObject rightHand;
|
||||
|
||||
[Header("Prefabs")]
|
||||
public GameObject ballPrefab;
|
||||
public GameObject batPrefab;
|
||||
public GameObject boxPrefab;
|
||||
public GameObject sceneObjectPrefab;
|
||||
|
||||
// IMPORTANT: Order of SyncVar declarations is intentional!
|
||||
// ChangeEquipment coroutine depends on equippedItemConfig being set first
|
||||
// but equippedItemConfig can also be changed independent of equippedItem
|
||||
// changing, e.g. reloading usages.
|
||||
[Header("SyncVars in Specific Order")]
|
||||
[SyncVar(hook = nameof(OnChangeEquippedItemConfig))]
|
||||
public EquippedItemConfig equippedItemConfig = default;
|
||||
[SyncVar(hook = nameof(OnChangeEquipment))]
|
||||
public EquippedItem equippedItem;
|
||||
|
||||
[Header("Diagnostics")]
|
||||
[ReadOnly] public GameObject equippedObject;
|
||||
|
||||
// Cached reference to IEquipped component on the child object
|
||||
IEquipped iEquipped;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!isLocalPlayer) return;
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha0) && equippedItem != EquippedItem.nothing)
|
||||
CmdChangeEquippedItem(EquippedItem.nothing);
|
||||
if (Input.GetKeyDown(KeyCode.Alpha1) && equippedItem != EquippedItem.ball)
|
||||
CmdChangeEquippedItem(EquippedItem.ball);
|
||||
if (Input.GetKeyDown(KeyCode.Alpha2) && equippedItem != EquippedItem.bat)
|
||||
CmdChangeEquippedItem(EquippedItem.bat);
|
||||
if (Input.GetKeyDown(KeyCode.Alpha3) && equippedItem != EquippedItem.box)
|
||||
CmdChangeEquippedItem(EquippedItem.box);
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.U) && iEquipped != null)
|
||||
CmdUseItem();
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.I) && iEquipped != null)
|
||||
CmdAddUsages(1);
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.O) && iEquipped != null)
|
||||
CmdResetUsages();
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.P) && iEquipped != null)
|
||||
CmdResetUsages(3);
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.X) && equippedItem != EquippedItem.nothing)
|
||||
CmdDropItem();
|
||||
}
|
||||
|
||||
void OnChangeEquippedItemConfig(EquippedItemConfig _, EquippedItemConfig newEquippedItemConfig)
|
||||
{
|
||||
// equippedItem may be EquippedItem.nothing so check for not null
|
||||
// before getting reference to the IEquipped interface component
|
||||
// and only set the equippedItemConfig if it's different.
|
||||
if (equippedObject != null && equippedObject.TryGetComponent(out iEquipped))
|
||||
if (!iEquipped.equippedItemConfig.Equals(equippedItemConfig))
|
||||
iEquipped.equippedItemConfig = equippedItemConfig;
|
||||
}
|
||||
|
||||
void OnChangeEquipment(EquippedItem _, EquippedItem newEquippedItem)
|
||||
{
|
||||
StartCoroutine(ChangeEquipment());
|
||||
}
|
||||
|
||||
// Since Destroy is delayed to the end of the current frame, we use a coroutine
|
||||
// to clear out any child objects before instantiating the new one
|
||||
IEnumerator ChangeEquipment()
|
||||
{
|
||||
while (rightHand.transform.childCount > 0)
|
||||
{
|
||||
Destroy(rightHand.transform.GetChild(0).gameObject);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
equippedObject = null;
|
||||
|
||||
switch (equippedItem)
|
||||
{
|
||||
case EquippedItem.ball:
|
||||
equippedObject = Instantiate(ballPrefab, rightHand.transform);
|
||||
break;
|
||||
case EquippedItem.bat:
|
||||
equippedObject = Instantiate(batPrefab, rightHand.transform);
|
||||
break;
|
||||
case EquippedItem.box:
|
||||
equippedObject = Instantiate(boxPrefab, rightHand.transform);
|
||||
break;
|
||||
}
|
||||
|
||||
// equippedItem may be EquippedItem.nothing so check for not null
|
||||
// before getting reference to the IEquipped interface component
|
||||
// and only set the equippedItemConfig if it's different.
|
||||
if (equippedObject != null && equippedObject.TryGetComponent(out iEquipped))
|
||||
if (!iEquipped.equippedItemConfig.Equals(equippedItemConfig))
|
||||
iEquipped.equippedItemConfig = equippedItemConfig;
|
||||
}
|
||||
|
||||
[Command]
|
||||
void CmdChangeEquippedItem(EquippedItem selectedItem)
|
||||
{
|
||||
switch (selectedItem)
|
||||
{
|
||||
case EquippedItem.ball:
|
||||
if (ballPrefab.TryGetComponent(out IEquipped ball))
|
||||
equippedItemConfig = ball.equippedItemConfig;
|
||||
break;
|
||||
case EquippedItem.bat:
|
||||
if (batPrefab.TryGetComponent(out IEquipped bat))
|
||||
equippedItemConfig = bat.equippedItemConfig;
|
||||
break;
|
||||
case EquippedItem.box:
|
||||
if (boxPrefab.TryGetComponent(out IEquipped box))
|
||||
equippedItemConfig = box.equippedItemConfig;
|
||||
break;
|
||||
case EquippedItem.nothing:
|
||||
equippedItemConfig = default;
|
||||
break;
|
||||
}
|
||||
|
||||
equippedItem = selectedItem;
|
||||
}
|
||||
|
||||
[Command]
|
||||
public void CmdUseItem()
|
||||
{
|
||||
// equippedItemConfig is a struct SyncVar so this
|
||||
// is how to update it correctly on the server.
|
||||
EquippedItemConfig config = equippedItemConfig;
|
||||
config.Use();
|
||||
equippedItemConfig = config;
|
||||
|
||||
// tell clients to invoke the Use method on the IEquipped object
|
||||
RpcUseItem();
|
||||
}
|
||||
|
||||
[Command]
|
||||
public void CmdAddUsages(byte usages)
|
||||
{
|
||||
// equippedItemConfig is a struct SyncVar so this
|
||||
// is how to update it correctly on the server.
|
||||
EquippedItemConfig config = equippedItemConfig;
|
||||
config.AddUsages(usages);
|
||||
equippedItemConfig = config;
|
||||
// tell clients to invoke the AddUsages method on the IEquipped object
|
||||
RpcAddUsages(usages);
|
||||
}
|
||||
|
||||
[Command]
|
||||
public void CmdResetUsages()
|
||||
{
|
||||
// equippedItemConfig is a struct SyncVar so this
|
||||
// is how to update it correctly on the server.
|
||||
EquippedItemConfig config = equippedItemConfig;
|
||||
config.ResetUsages();
|
||||
equippedItemConfig = config;
|
||||
// tell clients to invoke the ResetUsages method on the IEquipped object
|
||||
RpcResetUsages();
|
||||
}
|
||||
|
||||
[Command]
|
||||
public void CmdResetUsages(byte usages)
|
||||
{
|
||||
// equippedItemConfig is a struct SyncVar so this
|
||||
// is how to update it correctly on the server.
|
||||
EquippedItemConfig config = equippedItemConfig;
|
||||
config.ResetUsages(usages);
|
||||
equippedItemConfig = config;
|
||||
// tell clients to invoke the ResetUsages method on the IEquipped object
|
||||
RpcResetUsages(usages);
|
||||
}
|
||||
|
||||
[Command]
|
||||
void CmdDropItem()
|
||||
{
|
||||
// Instantiate the scene object on the server
|
||||
Vector3 pos = rightHand.transform.position;
|
||||
Quaternion rot = rightHand.transform.rotation;
|
||||
equippedObject = Instantiate(sceneObjectPrefab, pos, rot);
|
||||
|
||||
// set the RigidBody as non-kinematic on the server only (isKinematic = true in prefab)
|
||||
equippedObject.GetComponent<Rigidbody>().isKinematic = false;
|
||||
|
||||
SceneObject sceneObject = equippedObject.GetComponent<SceneObject>();
|
||||
|
||||
// set the SyncVar on the scene object for clients to instantiate
|
||||
sceneObject.equippedItem = equippedItem;
|
||||
|
||||
// set the equippedItemConfig for the iEquipped interface
|
||||
sceneObject.equippedItemConfig = equippedItemConfig;
|
||||
|
||||
// set the direction to launch the scene object
|
||||
sceneObject.direction = rightHand.transform.forward;
|
||||
|
||||
// Spawn the scene object on the network for all to see
|
||||
NetworkServer.Spawn(equippedObject);
|
||||
|
||||
// set the player's SyncVar to nothing so clients will destroy the iEquipped child item
|
||||
equippedItem = EquippedItem.nothing;
|
||||
equippedItemConfig = default;
|
||||
}
|
||||
|
||||
// public because it's called from a script on the SceneObject
|
||||
[Command]
|
||||
public void CmdPickupItem(GameObject obj)
|
||||
{
|
||||
if (obj.TryGetComponent(out SceneObject sceneObject))
|
||||
{
|
||||
// set the player's SyncVar so clients can show the iEquipped item
|
||||
equippedItem = sceneObject.equippedItem;
|
||||
|
||||
// set the equippedItemConfig on the iEquipped object
|
||||
equippedItemConfig = sceneObject.equippedItemConfig;
|
||||
}
|
||||
|
||||
// Destroy the scene object
|
||||
NetworkServer.Destroy(obj);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void RpcUseItem()
|
||||
{
|
||||
// iEquipped could be null so use the null conditional operator
|
||||
iEquipped?.Use();
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void RpcAddUsages(byte usages)
|
||||
{
|
||||
// iEquipped could be null so use the null conditional operator
|
||||
iEquipped?.AddUsages(usages);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void RpcResetUsages()
|
||||
{
|
||||
// iEquipped could be null so use the null conditional operator
|
||||
iEquipped?.ResetUsages();
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void RpcResetUsages(byte usages)
|
||||
{
|
||||
// iEquipped could be null so use the null conditional operator
|
||||
iEquipped?.ResetUsages(usages);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb266ca5176fd4e438673d08fbb59491
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Examples/PickupsDropsChilds/Scripts/PickupsDropsChilds.cs
|
||||
uploadId: 736421
|
108
Assets/Mirror/Examples/PickupsDropsChilds/Scripts/SceneObject.cs
Normal file
108
Assets/Mirror/Examples/PickupsDropsChilds/Scripts/SceneObject.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.PickupsDropsChilds
|
||||
{
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class SceneObject : NetworkBehaviour
|
||||
{
|
||||
[Header("Prefabs")]
|
||||
public GameObject ballPrefab;
|
||||
public GameObject batPrefab;
|
||||
public GameObject boxPrefab;
|
||||
|
||||
[Header("Settings")]
|
||||
[Range(0, 5)] public float force = 1;
|
||||
|
||||
// IMPORTANT: Order of SyncVar declarations is intentional!
|
||||
// ChangeEquipment coroutine depends on equippedItemConfig being set first
|
||||
// but equippedItemConfig can also be changed independent of equippedItem
|
||||
// changing, e.g. reloading usages.
|
||||
[Header("SyncVars in Specific Order")]
|
||||
[SyncVar(hook = nameof(OnChangeEquippedItemConfig))]
|
||||
public EquippedItemConfig equippedItemConfig = default;
|
||||
[SyncVar(hook = nameof(OnChangeEquipment))]
|
||||
public EquippedItem equippedItem;
|
||||
|
||||
[Header("Diagnostics")]
|
||||
[ReadOnly] public GameObject equippedObject;
|
||||
[ReadOnly] public Vector3 direction;
|
||||
|
||||
// Cached reference to IEquipped component on the child object
|
||||
[ReadOnly, SerializeField] IEquipped iEquipped;
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
if (Application.isPlaying) return;
|
||||
|
||||
base.OnValidate();
|
||||
|
||||
if (TryGetComponent(out Rigidbody rb))
|
||||
rb.isKinematic = true;
|
||||
|
||||
if (TryGetComponent(out NetworkTransformBase nt))
|
||||
nt.syncDirection = SyncDirection.ServerToClient;
|
||||
}
|
||||
|
||||
public override void OnStartServer()
|
||||
{
|
||||
if (TryGetComponent(out Rigidbody rb))
|
||||
{
|
||||
rb.isKinematic = false;
|
||||
rb.AddForce(direction * force, ForceMode.Impulse);
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseDown()
|
||||
{
|
||||
NetworkClient.localPlayer.GetComponent<PickupsDropsChilds>().CmdPickupItem(gameObject);
|
||||
}
|
||||
|
||||
void OnChangeEquippedItemConfig(EquippedItemConfig _, EquippedItemConfig newEquippedItemConfig)
|
||||
{
|
||||
// equippedItem may be EquippedItem.nothing so check for not null
|
||||
// before getting reference to the IEquipped interface component
|
||||
if (equippedObject != null && equippedObject.TryGetComponent(out iEquipped))
|
||||
if (!iEquipped.equippedItemConfig.Equals(equippedItemConfig))
|
||||
iEquipped.equippedItemConfig = equippedItemConfig;
|
||||
}
|
||||
|
||||
void OnChangeEquipment(EquippedItem _, EquippedItem newEquippedItem)
|
||||
{
|
||||
StartCoroutine(ChangeEquipment());
|
||||
}
|
||||
|
||||
// Since Destroy is delayed to the end of the current frame, we use a coroutine
|
||||
// to clear out any child objects before instantiating the new one
|
||||
IEnumerator ChangeEquipment()
|
||||
{
|
||||
while (transform.childCount > 0)
|
||||
{
|
||||
Destroy(transform.GetChild(0).gameObject);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
equippedObject = null;
|
||||
|
||||
switch (equippedItem)
|
||||
{
|
||||
case EquippedItem.ball:
|
||||
equippedObject = Instantiate(ballPrefab, transform);
|
||||
break;
|
||||
case EquippedItem.bat:
|
||||
equippedObject = Instantiate(batPrefab, transform);
|
||||
break;
|
||||
case EquippedItem.box:
|
||||
equippedObject = Instantiate(boxPrefab, transform);
|
||||
break;
|
||||
}
|
||||
|
||||
// equippedItem may be EquippedItem.nothing so check for not null
|
||||
// before getting reference to the IEquipped interface component
|
||||
// and only set the equippedItemConfig if it's different.
|
||||
if (equippedObject != null && equippedObject.TryGetComponent(out iEquipped))
|
||||
if (!iEquipped.equippedItemConfig.Equals(equippedItemConfig))
|
||||
iEquipped.equippedItemConfig = equippedItemConfig;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ce6433915d7fc64cb9ffa753e73bf14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Examples/PickupsDropsChilds/Scripts/SceneObject.cs
|
||||
uploadId: 736421
|
Reference in New Issue
Block a user