using UnityEngine; using UnityEngine.SceneManagement; #if UNITY_EDITOR using UnityEditor.SceneManagement; #endif namespace DestroyIt { /// This script manages all player input. [DisallowMultipleComponent] public class InputManager : MonoBehaviour { public GameObject cannonballPrefab; // The cannonball prefab to launch. public float cannonballVelocity = 75f; // Launch velocity of the cannonball. public GameObject rocketPrefab; // The rocket prefab to launch. public GameObject bulletPrefab; public ParticleSystem muzzleFlash; public ParticleSystem cannonFire; public ParticleSystem rocketFire; public Light muzzleLight; public GameObject launcherRocket; // The rocket gameobject, seated inside the launcher while ready to fire. public int bulletDamage = 15; // the amount of damage the bullet does to its target public float bulletForcePerSecond = 25f; // the amount of force the bullet applies to impacted rigidbodies public float bulletForceFrequency = 10f; [Range(1, 30)] public int gunShotsPerSecond = 8; // The gun's shots per second (rate of fire) while Fire1 button is depressed. public float startDistance = 1.5f; // The distance projectiles/missiles will start in front of the player. public WeaponType startingWeapon = WeaponType.Rocket; // The weapon the player will start with. public GameObject nukePrefab; public float shockwaveSpeed = 800f; // How fast the shockwave expands (ie, how much force is applied to the shock walls). public GameObject shockWallPrefab; public GameObject dustWallPrefab; public int dustWallDistance = 120; // How far in front of the shockwave should the dust effect around a player trigger? public GameObject groundChurnPrefab; public int nukeDistance = 2500; // The distance the nuke starts away from the player. public int groundChurnDistance = 90; [Range(0.1f, .5f)] public float timeSlowSpeed = 0.25f; public GameObject windZone; public WeaponType SelectedWeapon { get; set; } private bool timeSlowed; private bool timeStopped; private float timeBetweenShots; private float meleeAttackDelay; private float lastShotTime; private float lastMeleeTime; private int playerPrefShowReticle = -1; private int playerPrefShowHud = -1; private float rocketTimer; private float nukeTimer; private CharacterController firstPersonController; private Transform gunTransform; // The transform where the gun will be fired from. private Transform cannonTransform; // The transform where the cannon will be fired from. private Transform rocketTransform; // The transform where the rocket will be fired from. private Transform nukeTransform; // The location of the nuke firing controller. private Transform axeTransform; // The location of the axe. private Transform repairWrenchTransform; // The location of the repair wrench. private readonly LoadSceneParameters lsp = new LoadSceneParameters { loadSceneMode = LoadSceneMode.Single, localPhysicsMode = LocalPhysicsMode.None }; // Hide the default constructor (use InputManager.Instance instead). private InputManager() { } public static InputManager Instance { get; private set; } private void Awake() { Instance = this; } private void Start() { if (Camera.main == null || Camera.main.transform.parent == null) return; firstPersonController = Camera.main.transform.parent.GetComponent(); if (firstPersonController == null) Debug.LogError("InputManager: Could not find Character Controller on Main Camera parent."); foreach (Transform trans in Camera.main.transform) { switch (trans.name) { case "WeaponPosition-Nuke": nukeTransform = trans; break; case "WeaponPosition-Gun": gunTransform = trans; break; case "WeaponPosition-Axe": axeTransform = trans; break; case "WeaponPosition-Cannon": cannonTransform = trans; break; case "WeaponPosition-Rocket": rocketTransform = trans; break; case "WeaponPosition-RepairWrench": repairWrenchTransform = trans; break; default: // default to gun gunTransform = trans; break; } } if (muzzleLight != null && muzzleLight.enabled) muzzleLight.enabled = false; timeBetweenShots = 1f / gunShotsPerSecond; meleeAttackDelay = 0.6f; // Limit melee attacks to one every 1/2 second. lastShotTime = 0f; lastMeleeTime = 0f; SetTimeScale(); // Set active weapon from player preferences. int playerPrefWeapon = PlayerPrefs.GetInt("SelectedWeapon", -1); if (playerPrefWeapon == -1) SelectedWeapon = startingWeapon; else SelectedWeapon = (WeaponType)playerPrefWeapon; // Set HUD visibility options from player preferences. playerPrefShowHud = PlayerPrefs.GetInt("ShowHud", -1); playerPrefShowReticle = PlayerPrefs.GetInt("ShowReticle", -1); #if UNITY_WEBGL if (SelectedWeapon == WeaponType.Nuke) SelectedWeapon = startingWeapon; #endif SetActiveWeapon(); } private void Update() { if (nukeTimer > 0f) nukeTimer -= Time.deltaTime; if (nukeTimer < 0f) nukeTimer = 0f; if (rocketTimer > 0f) rocketTimer -= Time.deltaTime; if (rocketTimer <= 0f) { if (launcherRocket != null) launcherRocket.SetActive(true); rocketTimer = 0f; } if (Input.GetButtonDown("Fire1")) { //Cursor.lockState = CursorLockMode.Locked; switch (SelectedWeapon) { case WeaponType.Cannonball: if (cannonFire != null) { cannonFire.GetComponent().Clear(true); cannonFire.Play(true); } WeaponHelper.Launch(cannonballPrefab, cannonTransform, startDistance, cannonballVelocity, true); break; case WeaponType.Rocket: if (rocketTimer <= 0f) { if (launcherRocket != null) launcherRocket.SetActive(false); if (rocketFire != null) { rocketFire.GetComponent().Clear(true); rocketFire.Play(true); } WeaponHelper.Launch(rocketPrefab, rocketTransform, startDistance + .1f, 6f, false); RocketLoading rocketLoading = launcherRocket.GetComponentInChildren(); if (rocketLoading != null) rocketLoading.isLoaded = false; rocketTimer = 1f; // one rocket every X seconds. } break; case WeaponType.Nuke: // Nuclear Blast and Rolling Shockwave Damage if (nukeTimer <= 0f) { FadeIn flashEffect = gameObject.AddComponent(); flashEffect.startColor = Color.white; flashEffect.fadeLength = 5f; // position the nuke 2500m in front of where the player is facing. Transform player = GameObject.FindGameObjectWithTag("Player").transform; Vector3 nukeForwardPos = player.position + player.forward * nukeDistance; Vector3 nukePos = new Vector3(nukeForwardPos.x, 0f, nukeForwardPos.z); if (groundChurnPrefab != null) { GameObject groundChurn = Instantiate(groundChurnPrefab, nukePos, Quaternion.identity) as GameObject; Follow followScript = groundChurn.AddComponent(); followScript.isPositionFixed = true; followScript.objectToFollow = player; followScript.facingDirection = FacingDirection.FixedPosition; followScript.fixedFromPosition = nukePos; followScript.fixedDistance = groundChurnDistance; } GameObject nuke = Instantiate(nukePrefab, nukePos, Quaternion.Euler(Vector3.zero)) as GameObject; nuke.transform.LookAt(player); // Configure Wind Zone if (windZone != null) { windZone.transform.position = nukeForwardPos; windZone.transform.LookAt(player); Invoke("EnableWindZone", 5f); DisableAfter disableAfter = windZone.GetComponent() ?? windZone.AddComponent(); disableAfter.seconds = 25f; disableAfter.removeScript = true; } // Configure Dust Wall if (dustWallPrefab != null) { GameObject dustWall = Instantiate(dustWallPrefab, nukeForwardPos, Quaternion.Euler(Vector3.zero)) as GameObject; dustWall.transform.LookAt(player); dustWall.transform.position += (dustWall.transform.forward * dustWallDistance); dustWall.GetComponent().AddForce(dustWall.transform.forward * shockwaveSpeed, ForceMode.Force); DustWall dwScript = dustWall.GetComponent(); dwScript.fixedFromPosition = nukePos; } // Configure Shock Wall if (shockWallPrefab != null) { GameObject shockWall = Instantiate(shockWallPrefab, nukeForwardPos, Quaternion.Euler(Vector3.zero)) as GameObject; shockWall.transform.LookAt(player); shockWall.GetComponent().AddForce(shockWall.transform.forward * shockwaveSpeed, ForceMode.Force); ShockWall swScript = shockWall.GetComponent(); swScript.origin = nukePos; } Invoke("BroadcastNukeStart", 0.1f); Invoke("BroadcastNukeEnd", 25f); nukeTimer = 30f; // only one nuke every x seconds. } break; case WeaponType.Gun: FireGun(); break; case WeaponType.Melee: if (Time.time >= (lastMeleeTime + meleeAttackDelay)) MeleeAttack(); break; case WeaponType.RepairWrench: if (Time.time >= (lastMeleeTime + meleeAttackDelay)) RepairByHand(); break; } } // Continuous fire from holding the button down if (Input.GetButton("Fire1") && SelectedWeapon == WeaponType.Gun && Time.time >= (lastShotTime + timeBetweenShots)) FireGun(); // Continuous melee attack from holding the button down (useful for chopping trees in an MMO/survival game) if (Input.GetButton("Fire1") && SelectedWeapon == WeaponType.Melee && Time.time >= (lastMeleeTime + meleeAttackDelay)) MeleeAttack(); // Repair continuously while holding the Fire1 button down. if (Input.GetButton("Fire1") && SelectedWeapon == WeaponType.RepairWrench && Time.time >= (lastMeleeTime + meleeAttackDelay)) RepairByHand(); // Time Slow if (Input.GetKeyUp("t")) { timeSlowed = !timeSlowed; SetTimeScale(); } // Time Stop if (Input.GetKeyUp("y")) { timeStopped = !timeStopped; SetTimeScale(); } // Do this every frame for rigidbodies that enter the scene, so they have smooth frame interpolation. // TODO: can probably run this more efficiently at a set rate, like a few times per second - not every frame. if (timeSlowed) { foreach (GameObject go in FindObjectsOfType(typeof(GameObject))) { foreach (Rigidbody rb in go.GetComponentsInChildren()) rb.interpolation = RigidbodyInterpolation.Interpolate; } } // Reset the scene if (Input.GetKey("r")) { TreeManager treeManager = TreeManager.Instance; if (treeManager != null) { //TODO: Black out the main camera first. You can see trees insta-respawn when restored, and it looks weird. treeManager.RestoreTrees(); } #if UNITY_EDITOR EditorSceneManager.LoadSceneAsyncInPlayMode(SceneManager.GetActiveScene().path, lsp); #else SceneManager.LoadSceneAsync($"Assets/DestroyIt/Demos (safe to delete)/{SceneManager.GetActiveScene().name}.unity", lsp); #endif } if (Input.GetKeyUp("q")) { SelectedWeapon = WeaponHelper.GetPrevious(SelectedWeapon); PlayerPrefs.SetInt("SelectedWeapon", (int)SelectedWeapon); SetActiveWeapon(); } if (Input.GetKeyUp("e")) { SelectedWeapon = WeaponHelper.GetNext(SelectedWeapon); PlayerPrefs.SetInt("SelectedWeapon", (int)SelectedWeapon); SetActiveWeapon(); } if (Input.GetKeyUp("o")) { if (playerPrefShowReticle == -1) playerPrefShowReticle = 0; else playerPrefShowReticle = -1; PlayerPrefs.SetInt("ShowReticle", playerPrefShowReticle); } if (Input.GetKeyUp("h")) { if (playerPrefShowHud == -1) playerPrefShowHud = 0; else playerPrefShowHud = -1; PlayerPrefs.SetInt("ShowHud", playerPrefShowHud); } if (Input.GetKeyUp("m")) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; // Always restore trees before changing scenes so you don't lose terrain tree data in the Unity editor. TreeManager treeManager = TreeManager.Instance; if (treeManager != null) treeManager.RestoreTrees(); #if UNITY_EDITOR EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/DestroyIt/Demos (safe to delete)/Choose Demo.unity", lsp); #else SceneManager.LoadSceneAsync("Assets/DestroyIt/Demos (safe to delete)/Choose Demo.unity", lsp); #endif } float scrollWheel = Input.GetAxis("Mouse ScrollWheel"); if (scrollWheel > 0f) // scroll up { SelectedWeapon = WeaponHelper.GetNext(SelectedWeapon); PlayerPrefs.SetInt("SelectedWeapon", (int)SelectedWeapon); SetActiveWeapon(); } if (scrollWheel < 0f) // scroll down { SelectedWeapon = WeaponHelper.GetPrevious(SelectedWeapon); PlayerPrefs.SetInt("SelectedWeapon", (int)SelectedWeapon); SetActiveWeapon(); } } private void EnableWindZone() { if (windZone != null) windZone.SetActive(true); } private void SetActiveWeapon() { gunTransform.gameObject.SetActive(SelectedWeapon == WeaponType.Gun); cannonTransform.gameObject.SetActive(SelectedWeapon == WeaponType.Cannonball); rocketTransform.gameObject.SetActive(SelectedWeapon == WeaponType.Rocket); nukeTransform.gameObject.SetActive(SelectedWeapon == WeaponType.Nuke); axeTransform.gameObject.SetActive(SelectedWeapon == WeaponType.Melee); repairWrenchTransform.gameObject.SetActive(SelectedWeapon == WeaponType.RepairWrench); } private void MeleeAttack() { Animation anim = axeTransform.GetComponentInChildren(); anim.Play("Axe Swinging"); lastMeleeTime = Time.time; Invoke("BroadcastMeleeDamage", .2f); } private void RepairByHand() { Animation anim = repairWrenchTransform.GetComponentInChildren(); anim.Play("Wrench Turn"); lastMeleeTime = Time.time; Invoke("BroadcastRepairDamage", .2f); } private void BroadcastMeleeDamage() { firstPersonController.BroadcastMessage("OnMeleeDamage"); } private void BroadcastRepairDamage() { firstPersonController.BroadcastMessage("OnMeleeRepair"); } private void BroadcastNukeStart() { firstPersonController.BroadcastMessage("OnNukeStart"); } private void BroadcastNukeEnd() { firstPersonController.BroadcastMessage("OnNukeEnd"); } private void FireGun() { // Play muzzle flash particle effect if (muzzleFlash != null) muzzleFlash.Emit(1); // Turn on muzzle flash point light if (muzzleLight != null && !muzzleLight.enabled) { muzzleLight.enabled = true; Invoke("DisableMuzzleLight", 0.1f); } WeaponHelper.Launch(bulletPrefab, gunTransform, 0f, false); // Launch bullet lastShotTime = Time.time; } private void DisableMuzzleLight() { if (muzzleLight != null && muzzleLight.enabled) muzzleLight.enabled = false; } private void SetTimeScale() { if (timeStopped) { Time.timeScale = 0f; return; } if (timeSlowed) { Time.timeScale = timeSlowSpeed; /* DISABLING RIGIDBODY INTERPOLATION TEMPORARILY DUE TO AN ONGOING UNITY BUG: https://issuetracker.unity3d.com/issues/in-order-to-call-gettransforminfoexpectuptodate-dot-dot-dot-error-message-appears-while-using-rigidbody-interpolate-slash-extrapolate */ foreach (GameObject go in FindObjectsOfType(typeof(GameObject))) { foreach (Rigidbody rb in go.GetComponentsInChildren()) rb.interpolation = RigidbodyInterpolation.Interpolate; } } else { Time.timeScale = 1.0f; /* DISABLING RIGIDBODY INTERPOLATION TEMPORARILY DUE TO AN ONGOING UNITY BUG: https://issuetracker.unity3d.com/issues/in-order-to-call-gettransforminfoexpectuptodate-dot-dot-dot-error-message-appears-while-using-rigidbody-interpolate-slash-extrapolate */ foreach (GameObject go in FindObjectsOfType(typeof(GameObject))) { foreach (Rigidbody rb in go.GetComponentsInChildren()) rb.interpolation = RigidbodyInterpolation.None; } } } } }