using System; using MoreMountains.Feedbacks; using System.Collections; using System.Collections.Generic; using MoreMountains.Tools; using UnityEngine; using UnityEngine.UI; namespace MoreMountains.Feel { public struct StrikePin { public Rigidbody Rb; public Vector3 InitialPosition; public Quaternion InitialRotation; public void ResetPin() { Rb.transform.position = InitialPosition; Rb.transform.rotation = InitialRotation; Rb.velocity = Vector3.zero; Rb.angularVelocity = Vector3.zero; } } /// /// An example class part of the Feel demos /// This class handles the strike demo, detecting input and applying force to the ball /// public class Strike : MonoBehaviour { [Header("Input")] /// a key to use to throw the ball [Tooltip("a key to use to throw the ball")] public KeyCode ActionKey = KeyCode.Space; /// a secondary key to use to throw the ball [Tooltip("a secondary key to use to throw the ball")] public KeyCode ActionKeyAlt = KeyCode.Joystick1Button0; [Header("Bindings")] /// the rigidbody of the bowling ball [Tooltip("the rigidbody of the bowling ball")] public Rigidbody BowlingBallRb; /// a collider used to count points (still standing pins will overlap with it) [Tooltip("a collider used to count points (still standing pins will overlap with it)")] public Collider PointsCollider; /// the rigidbody of the pins [Tooltip("the rigidbody of the pins")] public List Pins; /// the wiggler that makes the launcher rotate [Tooltip("the wiggler that makes the launcher rotate")] public MMWiggle BowlingBallLauncherWiggler; /// the text component used to display the current last score [Tooltip("the text component used to display the current last score")] public Text LastScoreText; /// the text component used to display the total score [Tooltip("the text component used to display the total score")] public Text TotalScoreText; /// the text component used to display the number of consecutive strikes [Tooltip("the text component used to display the number of consecutive strikes")] public Text ConsecutiveStrikesText; /// a list of elements to turn on/off in case of strike [Tooltip("a list of elements to turn on/off in case of strike")] public List StrikeElements; [Header("Settings")] /// the force to apply when throwing the ball [Tooltip("the force to apply when throwing the ball")] public Vector3 ThrowingForce = new Vector3(0, 0, 10f); /// the gravity to apply [Tooltip("the gravity to apply")] public Vector3 Gravity = new Vector3(0f, -9.81f, 0f); /// the max duration before a reset [Tooltip("the max duration before a reset")] public float MaxDurationBeforeReset = 4f; /// the delay to wait for (in seconds) before resetting the scene [Tooltip("the delay to wait for (in seconds) before resetting the scene")] public float DelayBeforeReset = 1f; /// the delay to wait for (in seconds) while counting/displaying points [Tooltip("the delay to wait for (in seconds) while counting/displaying points")] public float DelayForPoints = 1f; [Header("Feedbacks")] /// a feedback to call when throwing the ball [Tooltip("a feedback to call when throwing the ball")] public MMFeedbacks ThrowBallFeedback; /// a feedback to call when resetting the scene [Tooltip("a feedback to call when resetting the scene")] public MMFeedbacks ResetFeedback; /// a feedback played when hitting a strike [Tooltip("a feedback played when hitting a strike")] public MMFeedbacks StrikeFeedback; /// a feedback played when missing a strike [Tooltip("a feedback played when missing a strike")] public MMFeedbacks NoStrikeFeedback; [Header("Scores")] /// the last score you hit [Tooltip("the last score you hit")] [MMReadOnly] public int LastScore = 0; /// The total amount of points since the start [Tooltip("The total amount of points since the start")] [MMReadOnly] public int TotalPoints = 0; /// the amount of consecutive strikes [Tooltip("the amount of consecutive strikes")] [MMReadOnly] public int ConsecutiveStrikes = 0; protected bool _ballThrown = false; protected Vector3 _initialBallPosition; protected Quaternion _initialBallRotation; protected List _strikePins; protected List _pinColliders; protected Coroutine _resetCoroutine; /// /// On Start we initialize our scene /// protected virtual void Start() { Initialization(); } /// /// Initializes physics settings, stores initial positions /// protected virtual void Initialization() { // we initialize our physics settings Physics.gravity = Gravity; Physics.bounceThreshold = 2; Physics.sleepThreshold = 0.005f; Physics.defaultContactOffset = 0.01f; Physics.defaultSolverIterations = 6; Physics.defaultSolverVelocityIterations = 1; Physics.queriesHitTriggers = true; // we initialize our point counters ConsecutiveStrikes = 0; LastScore = 0; TotalPoints = 0; ConsecutiveStrikesText.text = "0"; LastScoreText.text = "0"; TotalScoreText.text = "0"; SetStrikeElements(false); // we store our ball's position & rotation _initialBallPosition = BowlingBallRb.transform.position; _initialBallRotation = BowlingBallRb.transform.localRotation; // we store our pins' positions & rotations _strikePins = new List(); _pinColliders = new List(); foreach (Rigidbody rb in Pins) { StrikePin pin = new StrikePin(); pin.Rb = rb; pin.InitialPosition = rb.transform.position; pin.InitialRotation = rb.transform.rotation; _strikePins.Add(pin); _pinColliders.Add(pin.Rb.gameObject.GetComponent()); } } protected virtual void SetStrikeElements(bool status) { foreach (GameObject element in StrikeElements) { element.SetActive(status); } } /// /// On Update we look for input /// protected virtual void Update() { HandleInput(); } /// /// Detects input /// protected virtual void HandleInput() { if (FeelDemosInputHelper.CheckMainActionInputPressedThisFrame()) { StartBall(); } } /// /// Performs a jump if possible, otherwise plays a denied feedback /// protected virtual void StartBall() { if (!_ballThrown) { ThrowBallFeedback?.PlayFeedbacks(); BowlingBallLauncherWiggler.RotationActive = false; _ballThrown = true; } } /// /// This method, meant to be called by a feedback once it's ready to throw, will apply force to the ball. /// In a "normal" game this probably would be directly called by this class, but here we want to make sure all /// the previous feedbacks have played, and that's handled by the ThrowBallFeedback. /// public virtual void ThrowBall() { if (BowlingBallRb != null) { BowlingBallRb.AddRelativeForce(ThrowingForce, ForceMode.Impulse); BowlingBallRb.AddTorque(ThrowingForce, ForceMode.Impulse); _resetCoroutine = StartCoroutine(ResetCountdown()); } } /// /// When colliding with the catcher, we reset /// /// protected void OnTriggerEnter(Collider other) { if (other.gameObject.MMGetComponentNoAlloc() != null) { StartCoroutine(ResetSceneCo()); if (_resetCoroutine != null) { StopCoroutine(_resetCoroutine); } } } /// /// A countdown used to reset the scene after a max delay /// /// protected virtual IEnumerator ResetCountdown() { yield return MMCoroutine.WaitFor(MaxDurationBeforeReset); StartCoroutine(ResetSceneCo()); } /// /// Resets the whole scene /// /// protected virtual IEnumerator ResetSceneCo() { yield return MMCoroutine.WaitFor(DelayBeforeReset); CountPoints(); yield return MMCoroutine.WaitFor(DelayForPoints); ResetFeedback?.PlayFeedbacks(); yield return MMCoroutine.WaitFor(0.1f); // we reset the ball's position and forces BowlingBallRb.MovePosition(_initialBallPosition); BowlingBallRb.transform.localRotation = _initialBallRotation; BowlingBallRb.velocity = Vector3.zero; BowlingBallRb.angularVelocity = Vector3.zero; yield return MMCoroutine.WaitForFrames(1); BowlingBallRb.transform.position = _initialBallPosition; // we make our launcher rotate again BowlingBallLauncherWiggler.RotationActive = true; foreach (StrikePin pin in _strikePins) { pin.ResetPin(); } _ballThrown = false; } protected virtual void CountPoints() { int points = 10; foreach (Collider col in _pinColliders) { if (col.bounds.Intersects(PointsCollider.bounds)) { points--; } } LastScore = points; ConsecutiveStrikes = (points == 10) ? ConsecutiveStrikes + 1 : 0; if (points == 10) { StrikeFeedback?.PlayFeedbacks(); SetStrikeElements(true); } else { NoStrikeFeedback?.PlayFeedbacks(); SetStrikeElements(false); } TotalPoints += points; ConsecutiveStrikesText.text = ConsecutiveStrikes.ToString(); LastScoreText.text = LastScore.ToString(); TotalScoreText.text = TotalPoints.ToString(); } } }