Initial commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.XR.Content.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Detects a collision with a tagged collider, replacing this object with a 'broken' version
|
||||
/// </summary>
|
||||
public class Breakable : MonoBehaviour
|
||||
{
|
||||
public UnityAction<Collider> onBreak;
|
||||
public int pointValue = 1;
|
||||
|
||||
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
|
||||
public Collider collider => m_Collider;
|
||||
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
|
||||
|
||||
[SerializeField]
|
||||
Collider m_Collider;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The 'broken' version of this object.")]
|
||||
GameObject m_BrokenVersion;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The tag a collider must have to cause this object to break.")]
|
||||
string m_ColliderTag = "Destroyer";
|
||||
|
||||
bool m_Destroyed = false;
|
||||
|
||||
void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
if (m_Destroyed)
|
||||
return;
|
||||
|
||||
if (collision.gameObject.CompareTag(m_ColliderTag))
|
||||
{
|
||||
Break(collision.collider);
|
||||
}
|
||||
}
|
||||
|
||||
public void Break(Collider collider)
|
||||
{
|
||||
if (m_Destroyed) return;
|
||||
m_Destroyed = true;
|
||||
Instantiate(m_BrokenVersion, transform.position, transform.rotation);
|
||||
onBreak?.Invoke(collider);
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecc3a8c5ad509204cb2ac78f2182017c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,139 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Interaction.Toolkit;
|
||||
using UnityEngine.XR.Interaction.Toolkit.Interactables;
|
||||
|
||||
namespace XRMultiplayer.MiniGames
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mini-game called "Whack-A-Pig" where the player needs to hit pigs with a hammer.
|
||||
/// </summary>
|
||||
public class MiniGame_Whack : MiniGameBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The time to wait before resetting the hammer's position.
|
||||
/// </summary>
|
||||
[SerializeField] float m_HammerResetTime = .25f;
|
||||
|
||||
/// <summary>
|
||||
/// The interactable objects to use for the mini-game.
|
||||
/// </summary>
|
||||
readonly Dictionary<XRBaseInteractable, Pose> m_InteractablePoses = new();
|
||||
|
||||
/// <summary>
|
||||
/// The networked gameplay to use for handling the networked gameplay logic.
|
||||
/// </summary>
|
||||
NetworkedWhackAPig m_NetworkedGameplay;
|
||||
|
||||
/// <summary>
|
||||
/// The current score of the mini-game.
|
||||
/// </summary>
|
||||
int m_CurrentScore = 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
TryGetComponent(out m_NetworkedGameplay);
|
||||
|
||||
foreach (var interactable in m_GameInteractables)
|
||||
{
|
||||
if (!m_InteractablePoses.ContainsKey(interactable))
|
||||
{
|
||||
m_InteractablePoses.Add(interactable, new Pose(interactable.transform.position, interactable.transform.rotation));
|
||||
interactable.selectExited.AddListener(HammerDropped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void OnDestroy()
|
||||
{
|
||||
foreach (var kvp in m_InteractablePoses)
|
||||
{
|
||||
kvp.Key.selectExited.RemoveListener(HammerDropped);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the game by resetting the current score.
|
||||
/// </summary>
|
||||
public override void SetupGame()
|
||||
{
|
||||
base.SetupGame();
|
||||
m_CurrentScore = 0;
|
||||
m_NetworkedGameplay.ResetGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the game by spawning pigs if the player is the server.
|
||||
/// </summary>
|
||||
public override void StartGame()
|
||||
{
|
||||
base.StartGame();
|
||||
if (m_NetworkedGameplay.IsServer)
|
||||
{
|
||||
m_NetworkedGameplay.SpawnProcessServer();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes the game and ends the networked gameplay.
|
||||
/// </summary>
|
||||
/// <param name="submitScore">Whether to submit the score or not.</param>
|
||||
public override void FinishGame(bool submitScore = true)
|
||||
{
|
||||
base.FinishGame(submitScore);
|
||||
m_NetworkedGameplay.EndGame();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when the hammer is dropped on an interactable object.
|
||||
/// </summary>
|
||||
/// <param name="args">The interaction event arguments.</param>
|
||||
void HammerDropped(BaseInteractionEventArgs args)
|
||||
{
|
||||
XRBaseInteractable interactable = (XRBaseInteractable)args.interactableObject;
|
||||
if (m_InteractablePoses.ContainsKey(interactable))
|
||||
{
|
||||
StartCoroutine(DropHammerAfterTimeRoutine(interactable));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that drops the hammer after a specified time and resets the interactable's position.
|
||||
/// </summary>
|
||||
/// <param name="interactable">The interactable object.</param>
|
||||
IEnumerator DropHammerAfterTimeRoutine(XRBaseInteractable interactable)
|
||||
{
|
||||
yield return new WaitForSeconds(m_HammerResetTime);
|
||||
if (!interactable.isSelected)
|
||||
{
|
||||
Rigidbody body = interactable.GetComponent<Rigidbody>();
|
||||
bool wasKinematic = body.isKinematic;
|
||||
body.isKinematic = true;
|
||||
interactable.transform.SetPositionAndRotation(m_InteractablePoses[interactable].position, m_InteractablePoses[interactable].rotation);
|
||||
yield return new WaitForFixedUpdate();
|
||||
body.isKinematic = wasKinematic;
|
||||
foreach (var collider in interactable.colliders)
|
||||
{
|
||||
collider.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the local player's score and submits it to the server.
|
||||
/// </summary>
|
||||
/// <param name="pointValue">The point value to add to the score.</param>
|
||||
public void LocalPlayerScored(int pointValue)
|
||||
{
|
||||
m_CurrentScore += pointValue;
|
||||
if (m_CurrentScore < 0) m_CurrentScore = 0;
|
||||
m_MiniGameManager.SubmitScoreServerRpc(m_CurrentScore, XRINetworkPlayer.LocalPlayer.OwnerClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0ba65058d372504d92b0a44fa579df1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,376 @@
|
||||
using System.Collections;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Content.Interaction;
|
||||
|
||||
namespace XRMultiplayer.MiniGames
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a networked version of the Whack-A-Pig mini game.
|
||||
/// </summary>
|
||||
public class NetworkedWhackAPig : NetworkBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The proxy pigs to use for showing the trick.
|
||||
/// </summary>
|
||||
[SerializeField] GameObject[] m_ProxyPigs;
|
||||
|
||||
/// <summary>
|
||||
/// The hammers to use for hitting the pigs.
|
||||
/// </summary>
|
||||
[SerializeField] NetworkPhysicsInteractable[] m_Hammers;
|
||||
|
||||
[SerializeField] Collider[] m_HammerIgnoreColliders;
|
||||
|
||||
/// <summary>
|
||||
/// The pig prefab to spawn.
|
||||
/// </summary>
|
||||
[SerializeField] GameObject m_PigPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// The bad pig prefab to spawn.
|
||||
/// </summary>
|
||||
[SerializeField] GameObject m_BadPigPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// The time to stay spawned before despawning.
|
||||
/// </summary>
|
||||
[SerializeField] Vector2 m_TimeToStaySpawnedMinMax = new Vector2(.5f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The time to wait before spawning a new pig.
|
||||
/// </summary>
|
||||
[SerializeField] float m_TimeToSpawn = .5f;
|
||||
|
||||
/// <summary>
|
||||
/// The time to show the proxy pig.
|
||||
/// </summary>
|
||||
[SerializeField] float m_ProxyShowTime = .35f;
|
||||
|
||||
/// <summary>
|
||||
/// The hidden height of the proxy pig.
|
||||
/// </summary>
|
||||
[SerializeField] float m_HiddenHeight = -0.25f;
|
||||
|
||||
[SerializeField] float m_SpawnStartHeight = 0.0f;
|
||||
|
||||
[SerializeField] float m_SpawnShowHeight = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// The trick height min and max for the proxy pig.
|
||||
/// </summary>
|
||||
[SerializeField] Vector2 m_TrickHeightMinMax = new Vector2(-0.25f, -.05f);
|
||||
|
||||
/// <summary>
|
||||
/// The trick time min and max for the proxy pig.
|
||||
/// </summary>
|
||||
[SerializeField] Vector2 m_TrickTimeMinMax = new Vector2(1.0f, 3.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The bad pig spawn chance.
|
||||
/// </summary>
|
||||
[SerializeField] float m_BadPigSpawn = .7f;
|
||||
|
||||
[SerializeField] Collider gameBarrierCollider;
|
||||
|
||||
/// <summary>
|
||||
/// The mini game to use for handling the mini game logic.
|
||||
/// </summary>
|
||||
MiniGame_Whack m_MiniGame;
|
||||
|
||||
/// <summary>
|
||||
/// The current breakable pig.
|
||||
/// </summary>
|
||||
Breakable m_CurrentBreakablePig;
|
||||
|
||||
/// <summary>
|
||||
/// The current routine being played.
|
||||
/// </summary>
|
||||
IEnumerator m_CurrentRoutine;
|
||||
|
||||
/// <summary>
|
||||
/// The current proxy ID.
|
||||
/// </summary>
|
||||
int m_CurrentProxyId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we are currently spawning a pig.
|
||||
/// </summary>
|
||||
bool m_Spawning = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
void Start()
|
||||
{
|
||||
TryGetComponent(out m_MiniGame);
|
||||
foreach (var pig in m_ProxyPigs)
|
||||
{
|
||||
pig.SetActive(false);
|
||||
}
|
||||
|
||||
foreach (var areaCollider in m_HammerIgnoreColliders)
|
||||
{
|
||||
foreach (var hammer in m_Hammers)
|
||||
{
|
||||
foreach (var hammerInteractableCollider in hammer.baseInteractable.colliders)
|
||||
{
|
||||
Physics.IgnoreCollision(hammerInteractableCollider, areaCollider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the trick routine.
|
||||
/// </summary>
|
||||
[ContextMenu("Test Trick Routine")]
|
||||
void ShowTrickRoutine()
|
||||
{
|
||||
if (m_CurrentRoutine != null) StopTrickRoutine();
|
||||
m_CurrentRoutine = TrickRoutine();
|
||||
StartCoroutine(m_CurrentRoutine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the trick routine.
|
||||
/// </summary>
|
||||
[ContextMenu("Stop Trick Routine")]
|
||||
void StopTrickRoutine()
|
||||
{
|
||||
if (m_CurrentRoutine != null) StopCoroutine(m_CurrentRoutine);
|
||||
m_ProxyPigs[m_CurrentProxyId].SetActive(false);
|
||||
m_ProxyPigs[m_CurrentProxyId].transform.localPosition = new Vector3(m_ProxyPigs[m_CurrentProxyId].transform.localPosition.x, m_HiddenHeight, m_ProxyPigs[m_CurrentProxyId].transform.localPosition.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the trick routine.
|
||||
/// </summary>
|
||||
/// <returns>An IEnumerator representing the trick routine.</returns>
|
||||
IEnumerator TrickRoutine()
|
||||
{
|
||||
while (true & !m_MiniGame.finished)
|
||||
{
|
||||
// Choose a random proxy pig to show
|
||||
m_CurrentProxyId = Random.Range(0, m_ProxyPigs.Length);
|
||||
float riseTime = m_ProxyShowTime / 2;
|
||||
Vector3 startPosition = new Vector3(m_ProxyPigs[m_CurrentProxyId].transform.localPosition.x, 0, m_ProxyPigs[m_CurrentProxyId].transform.localPosition.z);
|
||||
m_ProxyPigs[m_CurrentProxyId].transform.localPosition = startPosition + Vector3.up * m_HiddenHeight;
|
||||
m_ProxyPigs[m_CurrentProxyId].SetActive(true);
|
||||
|
||||
float trickHeight = Random.Range(m_TrickHeightMinMax.x, m_TrickHeightMinMax.y);
|
||||
|
||||
|
||||
// Move the pig up to the trick height
|
||||
for (float i = 0; i < riseTime; i += Time.deltaTime)
|
||||
{
|
||||
float perc = i / riseTime;
|
||||
|
||||
float lerpHeight = Mathf.Lerp(m_HiddenHeight, trickHeight, perc);
|
||||
m_ProxyPigs[m_CurrentProxyId].transform.localPosition = startPosition + Vector3.up * lerpHeight;
|
||||
yield return null;
|
||||
}
|
||||
m_ProxyPigs[m_CurrentProxyId].transform.localPosition = startPosition + Vector3.up * trickHeight;
|
||||
|
||||
// Move the pig back down to the hidden height
|
||||
for (float i = 0; i < riseTime; i += Time.deltaTime)
|
||||
{
|
||||
float perc = i / riseTime;
|
||||
|
||||
float lerpHeight = Mathf.Lerp(trickHeight, m_HiddenHeight, perc);
|
||||
m_ProxyPigs[m_CurrentProxyId].transform.localPosition = startPosition + Vector3.up * lerpHeight;
|
||||
yield return null;
|
||||
}
|
||||
m_ProxyPigs[m_CurrentProxyId].transform.localPosition = startPosition + Vector3.up * m_HiddenHeight;
|
||||
|
||||
// Hide the pig
|
||||
m_ProxyPigs[m_CurrentProxyId].SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a pig on the server.
|
||||
/// </summary>
|
||||
public void SpawnProcessServer()
|
||||
{
|
||||
if (IsServer)
|
||||
SpawnProcessClientRpc(Random.Range(m_TrickTimeMinMax.x, m_TrickTimeMinMax.y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a pig on the clients.
|
||||
/// </summary>
|
||||
/// <param name="waitTime">The time to wait before spawning the pig.</param>
|
||||
[ClientRpc]
|
||||
public void SpawnProcessClientRpc(float waitTime)
|
||||
{
|
||||
m_Spawning = true;
|
||||
StartCoroutine(SpawnAfterTime(waitTime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a pig after a certain amount of time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to wait before spawning the pig.</param>
|
||||
/// <returns>An IEnumerator representing the spawn after time routine.</returns>
|
||||
IEnumerator SpawnAfterTime(float time)
|
||||
{
|
||||
yield return new WaitForSeconds(.25f);
|
||||
ShowTrickRoutine();
|
||||
yield return new WaitForSeconds(time);
|
||||
SpawnNewPig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from mini game manager when entering pre game state.
|
||||
/// </summary>
|
||||
public void ResetGame()
|
||||
{
|
||||
StopTrickRoutine();
|
||||
StopAllCoroutines();
|
||||
if (m_CurrentBreakablePig != null)
|
||||
Destroy(m_CurrentBreakablePig.gameObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the game and cleans up.
|
||||
/// </summary>
|
||||
public void EndGame()
|
||||
{
|
||||
StopTrickRoutine();
|
||||
StopAllCoroutines();
|
||||
if (m_CurrentBreakablePig != null)
|
||||
Destroy(m_CurrentBreakablePig.gameObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a new pig on the server.
|
||||
/// </summary>
|
||||
public void SpawnNewPig()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
SpawnPigClientRpc(Random.Range(0, m_ProxyPigs.Length), Random.value, Random.Range(m_TimeToStaySpawnedMinMax.x, m_TimeToStaySpawnedMinMax.y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a pig on the clients.
|
||||
/// </summary>
|
||||
/// <param name="spawnIdx">The index of the spawn transform to use.</param>
|
||||
/// <param name="randomValue">A random value used to determine if a bad pig should be spawned.</param>
|
||||
[ClientRpc]
|
||||
void SpawnPigClientRpc(int spawnIdx, float randomValue, float timeToStaySpawned)
|
||||
{
|
||||
StopTrickRoutine();
|
||||
m_Spawning = false;
|
||||
m_CurrentRoutine = SpawnRoutine(spawnIdx, randomValue, timeToStaySpawned);
|
||||
StartCoroutine(m_CurrentRoutine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a pig on the server and sets up collision ignoring.
|
||||
/// </summary>
|
||||
/// <param name="spawnIdx">The index of the spawn transform to use.</param>
|
||||
/// <param name="randomValue">A random value used to determine if a bad pig should be spawned.</param>
|
||||
/// <returns>An IEnumerator representing the spawn routine.</returns>
|
||||
IEnumerator SpawnRoutine(int spawnIdx, float randomValue, float timeToStaySpawned)
|
||||
{
|
||||
Vector3 spawnPos = m_ProxyPigs[spawnIdx].transform.position + (Vector3.up * m_SpawnStartHeight);
|
||||
|
||||
// Determine if we are spawning a bad pig or a good pig
|
||||
m_CurrentBreakablePig = Instantiate(randomValue > m_BadPigSpawn ? m_BadPigPrefab : m_PigPrefab, spawnPos, m_ProxyPigs[spawnIdx].transform.rotation).GetComponent<Breakable>();
|
||||
|
||||
m_CurrentBreakablePig.collider.enabled = false;
|
||||
// Updates Physics Ignore Collision to non local hammers cannot interact with the pigs
|
||||
foreach (var hammer in m_Hammers)
|
||||
{
|
||||
if (!hammer.IsOwner || !hammer.isInteracting)
|
||||
{
|
||||
foreach (var collider in hammer.baseInteractable.colliders)
|
||||
{
|
||||
Physics.IgnoreCollision(collider, m_CurrentBreakablePig.collider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_CurrentBreakablePig.onBreak += LocalPigDestroyed;
|
||||
|
||||
Vector3 startPosition = m_CurrentBreakablePig.transform.position;
|
||||
// Move the pig up to the show height
|
||||
for (float i = 0; i < m_TimeToSpawn; i += Time.deltaTime)
|
||||
{
|
||||
float perc = i / m_TimeToSpawn;
|
||||
|
||||
float lerpHeight = Mathf.Lerp(m_SpawnStartHeight, m_SpawnShowHeight, perc);
|
||||
m_CurrentBreakablePig.transform.position = startPosition + Vector3.up * lerpHeight;
|
||||
yield return null;
|
||||
}
|
||||
m_CurrentBreakablePig.transform.position = startPosition + Vector3.up * m_SpawnShowHeight;
|
||||
|
||||
m_CurrentBreakablePig.collider.enabled = true;
|
||||
yield return new WaitForSeconds(timeToStaySpawned);
|
||||
m_CurrentBreakablePig.collider.enabled = false;
|
||||
// Move the pig back down to the hidden height
|
||||
for (float i = 0; i < m_TimeToSpawn; i += Time.deltaTime)
|
||||
{
|
||||
float perc = i / m_TimeToSpawn;
|
||||
|
||||
float lerpHeight = Mathf.Lerp(m_SpawnShowHeight, m_SpawnStartHeight, perc);
|
||||
m_CurrentBreakablePig.transform.position = startPosition + Vector3.up * lerpHeight;
|
||||
yield return null;
|
||||
}
|
||||
m_CurrentBreakablePig.transform.position = startPosition + Vector3.up * m_SpawnStartHeight;
|
||||
|
||||
// Destroy the pig and spawn a new one if we are the server
|
||||
Destroy(m_CurrentBreakablePig.gameObject);
|
||||
if (IsServer)
|
||||
SpawnProcessServer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the destruction of a pig on the local client.
|
||||
/// </summary>
|
||||
/// <param name="collider">The collider of the pig that was destroyed.</param>
|
||||
void LocalPigDestroyed(Collider collider)
|
||||
{
|
||||
NetworkPhysicsInteractable grabInteractable = collider.GetComponentInParent<NetworkPhysicsInteractable>();
|
||||
|
||||
if (grabInteractable == null) // Probably in editor testing
|
||||
{
|
||||
if (IsServer & !m_Spawning)
|
||||
SpawnProcessServer();
|
||||
}
|
||||
else if (grabInteractable.IsOwner)
|
||||
{
|
||||
m_MiniGame.LocalPlayerScored(m_CurrentBreakablePig.pointValue);
|
||||
DestroyPigServerRpc(grabInteractable.OwnerClientId);
|
||||
StopCoroutine(m_CurrentRoutine);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys a pig on the server and notifies the clients.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client ID of the owner of the pig.</param>
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
void DestroyPigServerRpc(ulong clientId)
|
||||
{
|
||||
DestroyPigClientRpc(clientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys a pig on the clients.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client ID of the owner of the pig.</param>
|
||||
[ClientRpc]
|
||||
void DestroyPigClientRpc(ulong clientId)
|
||||
{
|
||||
if (XRINetworkPlayer.LocalPlayer.OwnerClientId != clientId && m_CurrentBreakablePig != null)
|
||||
{
|
||||
StopCoroutine(m_CurrentRoutine);
|
||||
m_CurrentBreakablePig.onBreak -= LocalPigDestroyed;
|
||||
m_CurrentBreakablePig.Break(null);
|
||||
}
|
||||
|
||||
if (IsServer & !m_Spawning)
|
||||
SpawnProcessServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bba13bfc67c46d4186c65bf73e39e71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user