using System; using UnityEngine.Pool; // Note: Error handling has been excluded from this script since not the main focus of example. namespace UnityEngine.InputSystem.Samples.RebindUI { public class Player : MonoBehaviour { // Since since its expected to be assigned at run-time [HideInInspector] [Tooltip("The gameplay manager")] public GameplayManager manager; [Tooltip("The fire object")] public GameObject fireObject; [Tooltip("The omni-fire object")] public GameObject omniFireObject; [Tooltip("The bullet/particle object")] public GameObject particle; [Tooltip("The cannon belt")] public GameObject belt; [Tooltip("The cannon barrel")] public GameObject barrel; [Tooltip("The regular fire rate")] public float fireRate = 0.25f; [Tooltip("The omni-fire rate")] public float omniFireRate = 1.0f; [Tooltip("The change rate")] public float changeRate = 1.0f; [Tooltip("List of color animation targets")] public Renderer[] animatedRenderers; /// /// Specifies whether the player is firing or not. /// public bool firing { get; set; } /// /// The move vector of the player that specifies movement direction and magnitude. /// public Vector2 move { get; set; } /// /// Get the current color of the player. /// /// Current color. public Color GetColor() => GetColor(m_OmniFire); //m_Material != null ? m_Material.GetColor(Color1) : Color.black; /// /// Request mode change. /// public void Change() { m_ChangeRequested = true; } /// /// Rotate the player by the given angle. /// /// Angle in degrees (additive). public void Rotate(float angle) { m_RotationAngle += angle; } private static readonly int Color1 = Shader.PropertyToID("_Color"); private Material m_Material; private Vector3 m_TargetEulerAngles; private Color m_TargetColor; private Color m_Color; private float m_TargetScale; private int m_ColorIndex; private float m_TimeUntilNextFire; private float m_TimeUntilNextChange; private bool m_OmniFire; private bool m_ChangeRequested; private float m_TargetBeltAngle; private float m_BeltAngle; private float m_BarrelPosition; private float m_RotationAngle; private ObjectPool m_ObjectPool; private Rigidbody m_Rigidbody; private void Awake() { m_Rigidbody = GetComponent(); m_BarrelPosition = barrel.transform.localPosition.y; fireObject.transform.localScale = m_OmniFire ? Vector3.zero : Vector3.one; omniFireObject.transform.localScale = m_OmniFire ? Vector3.one : Vector3.zero; m_TargetColor = GetColor(m_OmniFire); } private void Start() { #if UNITY_EDITOR // Note that this creates a instance (copy) of the material we want to animate. // When then assign the instance to all tagged child renderers to benefit from // batching and allow animating color without affecting the asset in editor. foreach (var animatedRenderer in animatedRenderers) { if (animatedRenderer == null) continue; if (m_Material == null) m_Material = animatedRenderer.material; else animatedRenderer.sharedMaterial = m_Material; } #else // When not in editor we can safely modify the shared material without // indirectly changing the source material. m_Material = animatedRenderers[0].sharedMaterial; #endif // Create an object pool for bullets/projectiles m_ObjectPool = new ObjectPool( createFunc: () => { var bullet = Instantiate(particle).GetComponent(); bullet.Initialize(manager, m_ObjectPool); return bullet; }, actionOnGet: (bullet) => bullet.gameObject.SetActive(true), actionOnRelease: (bullet) => bullet.gameObject.SetActive(false), actionOnDestroy: (bullet) => Destroy(bullet.gameObject)); } private void OnEnable() { m_TimeUntilNextFire = 0.0f; m_TimeUntilNextChange = 0.0f; } private void UpdateFire(float deltaTime) { if (Throttle(ref m_TimeUntilNextFire, firing, deltaTime, m_OmniFire ? omniFireRate : fireRate)) return; // Fire in all directions with 45 degree offset for each bullet if (m_OmniFire) { for (var i = 0; i < 8; ++i) FireBullet(Quaternion.AngleAxis(i * 45.0f, Vector3.forward) * transform.up); return; } // Else: Fire in forward direction FireBullet(transform.up); } private static bool Throttle(ref float remainingTime, bool condition, float deltaTime, float timeUntilNextEvent) { remainingTime -= deltaTime; if (remainingTime > 0.0f) return true; // Enough time has not elapsed if (condition) remainingTime += timeUntilNextEvent; if (remainingTime < 0.0f) remainingTime = 0.0f; return !condition; } private void FireBullet(Vector3 direction) { // Fire a single bullet in the direction of the player, approximately originating from the muzzle. var bullet = m_ObjectPool.Get(); bullet.direction = direction; bullet.transform.position = transform.position + direction.normalized * (1.6f * transform.lossyScale.y); // Animate barrel to simulate recoil var pos = barrel.transform.localPosition; barrel.transform.localPosition = new Vector3(pos.x, m_BarrelPosition - 0.2f, pos.z); // Rotate the belt for each fired round, simulated a reload m_BeltAngle += 45.0f; } private void UpdateChangeWeapon(float deltaTime) { if (Throttle(ref m_TimeUntilNextChange, m_ChangeRequested, deltaTime, changeRate)) return; m_ChangeRequested = false; // Change weapon, animate change, belt rotation, color m_OmniFire = !m_OmniFire; m_TargetScale = m_OmniFire ? 1.0f : 0.0f; m_BeltAngle += 360.0f; m_TargetColor = GetColor(m_OmniFire); } private void UpdateRotate() { // We do not want to use physics for this rotation to give a more direct feel. transform.Rotate(Vector3.forward, m_RotationAngle, Space.World); // Reset rotation angle and let it accumulate until next update. m_RotationAngle = 0; } private void OnCollisionEnter(Collision other) { // If we collide with an enemy if (other.gameObject.GetComponent()) { // Create an explosion matching our current color Color.RGBToHSV(GetColor(), out float h, out float s, out float v); var explosionColor = Color.HSVToRGB(h, s * 0.5f, v); manager.Explosion(transform, other.GetContact(0).point, 0.5f, explosionColor, m_Material); // End the game manager.GameOver(); } } private void Update() { // Update game logic var deltaTime = Time.deltaTime; UpdateFire(deltaTime); UpdateChangeWeapon(deltaTime); UpdateRotate(); if (manager.TryTeleportOrthographicExtents(transform.position, out var result)) transform.position = result; // Animate AnimateChangeWeapon(deltaTime); AnimateFireWeapon(deltaTime); AnimateColors(deltaTime); } private void FixedUpdate() { // Use physics to animate player movement to get a feeling of inertia. //var moveValue = move.action.ReadValue(); var y = move.y; if (y < 0.0f) y *= 0.33f; #if UNITY_6000_0_OR_NEWER var velocityMagnitude = m_Rigidbody.linearVelocity.magnitude; #else var velocityMagnitude = m_Rigidbody.velocity.magnitude; #endif if (velocityMagnitude < 10.0f) m_Rigidbody.AddRelativeForce(Vector3.up * (10.0f * y) + Vector3.right * (5.0f * move.x), ForceMode.Acceleration); } private void AnimateChangeWeapon(float deltaTime) { // Animate scale of fire vs omni-fire to be the inverse of each other var omniFireScale = Mathf.Lerp(omniFireObject.transform.localScale.x, m_TargetScale, deltaTime * 10.0f); fireObject.transform.localScale = new Vector3(1.0f - omniFireScale, 1.0f - omniFireScale, 1.0f - omniFireScale); omniFireObject.transform.localScale = new Vector3(omniFireScale, omniFireScale, omniFireScale); } private void AnimateFireWeapon(float deltaTime) { // Animate belt angle to simulate bullet reload m_BeltAngle = Mathf.Lerp(m_BeltAngle, m_TargetBeltAngle, deltaTime * 10.0f); belt.transform.localEulerAngles = new Vector3(0, m_BeltAngle, 0); // Animate barrel back to rest position after bullet has been fired var localPosition = barrel.transform.localPosition; barrel.transform.localPosition = new Vector3( localPosition.x, Mathf.Lerp(localPosition.y, m_BarrelPosition, deltaTime * 10.0f), localPosition.z); } private void AnimateColors(float deltaTime) { var color = Color.Lerp(m_Material.color, m_TargetColor, deltaTime * 2.0f); if (color != GetColor()) { // Update material m_Material.SetColor(Color1, color); } } private static Color GetColor(bool omniFire) { return omniFire ? Color.yellow : Color.red; } } }