Initial commit
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace XRMultiplayer.MiniGames
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a slingshot mini-game.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(TargetManager))]
|
||||
public class MiniGame_Slingshot : MiniGameBase
|
||||
{
|
||||
[SerializeField] SlingshotVisualUpdater[] m_Slingshots;
|
||||
|
||||
[SerializeField] float m_PreGameHeight = 0.65f;
|
||||
[SerializeField] float m_StartGameHeight = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The target manager.
|
||||
/// </summary>
|
||||
TargetManager m_TargetManager;
|
||||
|
||||
/// <summary>
|
||||
/// The current player score.
|
||||
/// </summary>
|
||||
int m_CurrentPlayerScore = 0;
|
||||
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
TryGetComponent(out m_TargetManager);
|
||||
SetSlingshotHeights(m_PreGameHeight);
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void SetupGame()
|
||||
{
|
||||
base.SetupGame();
|
||||
SetSlingshotHeights(m_PreGameHeight);
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void StartGame()
|
||||
{
|
||||
base.StartGame();
|
||||
m_CurrentPlayerScore = 0;
|
||||
m_TargetManager.ActivateTargets();
|
||||
SetSlingshotHeights(m_StartGameHeight);
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void FinishGame(bool submitScore = true)
|
||||
{
|
||||
base.FinishGame(submitScore);
|
||||
m_TargetManager.DeactivateTargets();
|
||||
m_MiniGameManager.FinishGame();
|
||||
|
||||
SetSlingshotHeights(m_PreGameHeight);
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void UpdateGame(float deltaTime)
|
||||
{
|
||||
base.UpdateGame(deltaTime);
|
||||
}
|
||||
|
||||
void SetSlingshotHeights(float height)
|
||||
{
|
||||
foreach (var slingshot in m_Slingshots)
|
||||
{
|
||||
slingshot.ResetSlingshot(height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the local player hits a target.
|
||||
/// </summary>
|
||||
/// <param name="targetValue"></param>
|
||||
public void LocalPlayerHitTarget(int targetValue)
|
||||
{
|
||||
if (m_MiniGameManager.currentNetworkedGameState == MiniGameManager.GameState.InGame)
|
||||
{
|
||||
m_CurrentPlayerScore += targetValue;
|
||||
m_MiniGameManager.SubmitScoreServerRpc(m_CurrentPlayerScore, XRINetworkPlayer.LocalPlayer.OwnerClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51a3259d5ba52f449b702e0717f0c721
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.Collections;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XRMultiplayer.MiniGames
|
||||
{
|
||||
/// <summary>
|
||||
/// A networked slingshot that shoots projectiles.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(SlingshotVisualUpdater))]
|
||||
public class Slingshot : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The projectile prefab to shoot from the slingshot.
|
||||
/// </summary>
|
||||
[SerializeField] GameObject m_ProjectilePrefab;
|
||||
|
||||
/// <summary>
|
||||
/// The projectile proxy object to show when the bucket is held.
|
||||
/// </summary>
|
||||
[SerializeField] GameObject m_ProjectileProxyObject;
|
||||
|
||||
/// <summary>
|
||||
/// The bucket interactable to use for setting the power of the slingshot.
|
||||
/// </summary>
|
||||
[SerializeField] NetworkPhysicsInteractable m_BucketInteractable;
|
||||
|
||||
/// <summary>
|
||||
/// The time to wait before resetting the proxy object.
|
||||
/// </summary>
|
||||
[SerializeField] float m_ResetTime = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The colliders to ignore when shooting the projectile.
|
||||
/// </summary>
|
||||
[SerializeField] Collider[] m_CollidersToIgnore;
|
||||
|
||||
/// <summary>
|
||||
/// The slingshot visual updater to use for updating the slingshot visuals.
|
||||
/// </summary>
|
||||
SlingshotVisualUpdater m_SlingshotVisualUpdater;
|
||||
|
||||
/// <summary>
|
||||
/// The slingshot mini game to use for handling the mini game logic.
|
||||
/// </summary>
|
||||
MiniGame_Slingshot m_MiniGame;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the script instance is being loaded.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
TryGetComponent(out m_SlingshotVisualUpdater);
|
||||
m_MiniGame = GetComponentInParent<MiniGame_Slingshot>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the bucket's held state changes.
|
||||
/// </summary>
|
||||
/// <param name="old">The previous held state.</param>
|
||||
/// <param name="current">The current held state.</param>
|
||||
public void OnBucketHeldChanged(bool current)
|
||||
{
|
||||
if (!current)
|
||||
{
|
||||
ShootProjectile();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_BucketInteractable.IsOwner)
|
||||
{
|
||||
m_SlingshotVisualUpdater.trajectoryLineRenderer.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoots a projectile from the slingshot.
|
||||
/// </summary>
|
||||
private void ShootProjectile()
|
||||
{
|
||||
m_ProjectileProxyObject.SetActive(false);
|
||||
SlingshotProjectile projectile = Instantiate(m_ProjectilePrefab, m_BucketInteractable.transform.position, Quaternion.identity).GetComponent<SlingshotProjectile>();
|
||||
XRINetworkGameManager.Instance.GetPlayerByID(m_BucketInteractable.OwnerClientId, out XRINetworkPlayer player);
|
||||
projectile.LaunchProjectile(m_SlingshotVisualUpdater.GetShotForce(), player.IsLocalPlayer, player.playerColor);
|
||||
|
||||
foreach (var collider in m_CollidersToIgnore)
|
||||
{
|
||||
Physics.IgnoreCollision(projectile.GetComponent<Collider>(), collider);
|
||||
}
|
||||
|
||||
if (m_BucketInteractable.IsOwner)
|
||||
{
|
||||
projectile.localPlayerHitTarget += OnLocalPlayerHitTarget;
|
||||
m_SlingshotVisualUpdater.ResetBucketPosition();
|
||||
ResetProxyObjectServerRpc();
|
||||
m_SlingshotVisualUpdater.trajectoryLineRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the proxy object on the server.
|
||||
/// </summary>
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
private void ResetProxyObjectServerRpc()
|
||||
{
|
||||
ResetProxyObjectClientRpc();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the proxy object on the clients.
|
||||
/// </summary>
|
||||
[ClientRpc]
|
||||
private void ResetProxyObjectClientRpc()
|
||||
{
|
||||
StartCoroutine(ResetProxyAfterTime());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the proxy object after a specified time.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator to control the coroutine.</returns>
|
||||
private IEnumerator ResetProxyAfterTime()
|
||||
{
|
||||
yield return new WaitForSeconds(m_ResetTime);
|
||||
m_ProjectileProxyObject.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the local player hits a target with the projectile.
|
||||
/// </summary>
|
||||
/// <param name="projectile">The projectile that hit the target.</param>
|
||||
/// <param name="targetValue">The value of the target hit.</param>
|
||||
private void OnLocalPlayerHitTarget(SlingshotProjectile projectile, int targetValue)
|
||||
{
|
||||
projectile.localPlayerHitTarget -= OnLocalPlayerHitTarget;
|
||||
m_MiniGame.LocalPlayerHitTarget(targetValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3ea272e22c548240beeed591726da11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XRMultiplayer.MiniGames
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a projectile for the slingshot mini-game.
|
||||
/// </summary>
|
||||
public class SlingshotProjectile : Projectile
|
||||
{
|
||||
/// <summary>
|
||||
/// Event that is triggered when the local player hits a target with the projectile.
|
||||
/// </summary>
|
||||
public Action<SlingshotProjectile, int> localPlayerHitTarget;
|
||||
|
||||
/// <summary>
|
||||
/// The lifetime of the projectile.
|
||||
/// </summary>
|
||||
[SerializeField] float m_LifeTime = 10.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The collider for the projectile.
|
||||
/// </summary>
|
||||
[SerializeField] Collider m_Collider;
|
||||
|
||||
/// <summary>
|
||||
/// The rigidbody for the projectile.
|
||||
/// </summary>
|
||||
[SerializeField] Rigidbody m_Rigidbody;
|
||||
|
||||
/// <summary>
|
||||
/// Called before the first frame update.
|
||||
/// </summary>
|
||||
void Start()
|
||||
{
|
||||
Destroy(gameObject, m_LifeTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Launches the projectile with the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="launchForce">The force to launch the projectile with.</param>
|
||||
/// <param name="isLocalPlayer">Indicates whether the player launching the projectile is the local player.</param>
|
||||
/// <param name="playerColor">The color of the player launching the projectile.</param>
|
||||
public void LaunchProjectile(Vector3 launchForce, bool isLocalPlayer, Color playerColor)
|
||||
{
|
||||
Setup(isLocalPlayer, playerColor);
|
||||
m_Collider.enabled = false;
|
||||
m_Rigidbody.linearVelocity = launchForce;
|
||||
StartCoroutine(LaunchRoutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that enables the collider after a short delay.
|
||||
/// </summary>
|
||||
IEnumerator LaunchRoutine()
|
||||
{
|
||||
yield return new WaitForSeconds(.15f);
|
||||
m_Collider.enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the projectile hits a target.
|
||||
/// </summary>
|
||||
/// <param name="target">The target that was hit.</param>
|
||||
protected override void HitTarget(Target target)
|
||||
{
|
||||
base.HitTarget(target);
|
||||
localPlayerHitTarget?.Invoke(this, target.targetValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3285d21d1d58fa34b876d5042734f3bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,190 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XRMultiplayer.MiniGames
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the visual elements of the slingshot in the gameplay.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
public class SlingshotVisualUpdater : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The line renderer displaying the current trajectory.
|
||||
/// </summary>
|
||||
public LineRenderer trajectoryLineRenderer;
|
||||
|
||||
[SerializeField] Rigidbody m_Rigidbody;
|
||||
|
||||
/// <summary>
|
||||
/// The transform of the bucket.
|
||||
/// </summary>
|
||||
[SerializeField] Rigidbody m_BucketRigibody;
|
||||
|
||||
/// <summary>
|
||||
/// The transform of the shot center.
|
||||
/// </summary>
|
||||
[SerializeField] Transform m_ShotCenterTransform;
|
||||
|
||||
/// <summary>
|
||||
/// The line renderers of the slingshot.
|
||||
/// </summary>
|
||||
[SerializeField] LineRenderer[] m_LineRenderers;
|
||||
|
||||
/// <summary>
|
||||
/// The transform of the line end.
|
||||
/// </summary>
|
||||
[SerializeField] Transform[] m_LineEndTransforms;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum and maximum distance of the slingshot.
|
||||
/// </summary>
|
||||
[SerializeField] Vector2 m_MinMaxDistance = new Vector2(0.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum and maximum size of the line.
|
||||
/// </summary>
|
||||
[SerializeField] Vector2 m_MinMaxLineSize = new Vector2(.05f, .01f);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum and maximum shot force.
|
||||
/// </summary>
|
||||
[SerializeField] Vector2 m_MinMaxShotForce = new Vector2(5.0f, 20.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The force multiplier of the shot.
|
||||
/// </summary>
|
||||
[SerializeField] float m_ForceMultiplier = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum distance to display the debug line.
|
||||
/// </summary>
|
||||
[SerializeField] float m_MaxDebugShowDistance = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The time to reset the bucket position.
|
||||
/// </summary>
|
||||
[SerializeField] float m_BucketResetTime = .35f;
|
||||
|
||||
/// <summary>
|
||||
/// The color divisor of the debug line.
|
||||
/// </summary>
|
||||
[SerializeField] float m_ColorDivisor = 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The line display distance.
|
||||
/// </summary>
|
||||
[SerializeField] float m_LineDisplayDistance = 2.0f;
|
||||
|
||||
[SerializeField] bool m_LockBucketRotation = true;
|
||||
|
||||
/// <summary>
|
||||
/// The shot force of the slingshot.
|
||||
/// </summary>
|
||||
float m_ShotForce;
|
||||
|
||||
/// <summary>
|
||||
/// The aim direction of the slingshot.
|
||||
/// </summary>
|
||||
Vector3 m_AimDirection;
|
||||
|
||||
/// <summary>
|
||||
/// The reset position of the bucket.
|
||||
/// </summary>
|
||||
// Vector3 m_ResetPosition;
|
||||
|
||||
Pose m_ResetPose;
|
||||
|
||||
/// <inheritdoc/>
|
||||
void Awake()
|
||||
{
|
||||
trajectoryLineRenderer.enabled = false;
|
||||
m_ResetPose = new Pose(m_BucketRigibody.transform.localPosition, m_BucketRigibody.transform.localRotation);
|
||||
// m_ResetPosition = m_BucketTransform.localPosition;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void LateUpdate()
|
||||
{
|
||||
for (int i = 0; i < m_LineRenderers.Length; i++)
|
||||
{
|
||||
m_LineRenderers[i].SetPosition(0, m_LineRenderers[i].transform.position);
|
||||
m_LineRenderers[i].SetPosition(1, m_LineEndTransforms[i].position);
|
||||
|
||||
float distance = Vector3.Distance(m_LineRenderers[i].transform.position, m_LineEndTransforms[i].position);
|
||||
float perc = Utils.GetPercentOfValueBetweenTwoValues(m_MinMaxDistance.x, m_MinMaxDistance.y, distance);
|
||||
float size = Mathf.Lerp(m_MinMaxLineSize.x, m_MinMaxLineSize.y, perc);
|
||||
m_LineRenderers[i].startWidth = size;
|
||||
m_LineRenderers[i].endWidth = size;
|
||||
}
|
||||
|
||||
float lineDistance = Vector3.Distance(m_ShotCenterTransform.position, m_BucketRigibody.position);
|
||||
m_ShotForce = Mathf.Clamp(lineDistance * m_ForceMultiplier, m_MinMaxShotForce.x, m_MinMaxShotForce.y);
|
||||
m_AimDirection = (m_ShotCenterTransform.position - m_BucketRigibody.position).normalized;
|
||||
|
||||
Vector3 lineEndPos = m_BucketRigibody.position + (m_AimDirection * Mathf.Clamp(lineDistance * m_LineDisplayDistance, 0.0f, m_MaxDebugShowDistance));
|
||||
|
||||
Color forceColor = Color.Lerp(Color.yellow, Color.red, Mathf.Clamp01(m_ShotForce / m_MaxDebugShowDistance / m_ColorDivisor));
|
||||
Color transparentForceColor = new Color(forceColor.r, forceColor.g, forceColor.b, 0.0f);
|
||||
|
||||
trajectoryLineRenderer.startColor = forceColor;
|
||||
trajectoryLineRenderer.endColor = transparentForceColor;
|
||||
|
||||
trajectoryLineRenderer.SetPosition(0, m_BucketRigibody.position);
|
||||
trajectoryLineRenderer.SetPosition(1, lineEndPos);
|
||||
if (m_LockBucketRotation)
|
||||
m_BucketRigibody.transform.forward = m_AimDirection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the force of the shot.
|
||||
/// </summary>
|
||||
/// <returns>The force of the shot.</returns>
|
||||
public Vector3 GetShotForce()
|
||||
{
|
||||
return m_AimDirection * m_ShotForce;
|
||||
}
|
||||
|
||||
public void ResetSlingshot(float height)
|
||||
{
|
||||
StartCoroutine(ResetSlingshotHeightRoutine(height));
|
||||
ResetBucketPosition();
|
||||
}
|
||||
|
||||
IEnumerator ResetSlingshotHeightRoutine(float height)
|
||||
{
|
||||
bool wasKinematic = m_Rigidbody.isKinematic;
|
||||
m_Rigidbody.isKinematic = true;
|
||||
transform.localPosition = new Vector3(transform.localPosition.x, height, transform.localPosition.z);
|
||||
|
||||
yield return new WaitForSeconds(.15f);
|
||||
m_Rigidbody.isKinematic = wasKinematic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the position of the bucket.
|
||||
/// </summary>
|
||||
public void ResetBucketPosition()
|
||||
{
|
||||
StartCoroutine(ResetBucketPositionRoutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to reset the position of the bucket over time.
|
||||
/// </summary>
|
||||
/// <returns>An IEnumerator used for the coroutine.</returns>
|
||||
IEnumerator ResetBucketPositionRoutine()
|
||||
{
|
||||
bool wasKinematic = m_BucketRigibody.isKinematic;
|
||||
m_BucketRigibody.isKinematic = true;
|
||||
for (float i = 0; i < m_BucketResetTime; i += Time.deltaTime)
|
||||
{
|
||||
m_BucketRigibody.transform.localPosition = Vector3.Lerp(m_BucketRigibody.transform.localPosition, m_ResetPose.position, i / m_BucketResetTime);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
m_BucketRigibody.transform.SetLocalPositionAndRotation(m_ResetPose.position, m_ResetPose.rotation);
|
||||
m_BucketRigibody.isKinematic = wasKinematic;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa9bce44a758b044abdbe44f257ab085
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user