using UnityEngine; public class CowardEnemyHoverMovement : HoverMovement { public GameObject minePrefab; public float mineSpawnChance; public float mineSpawnCooldown; private float _mineSpawnTick; private bool _canSpawnMine; // Game object to track the player GO private GameObject _player; private GameObject _blackHole; // Residual Thrust variables to handle the "glide" after the gameObject has moved public float residualThrustStep; public float residualThrustMax; public float residualThrustTimeTickMax; public float residualThrustDecayRate; public float residualThrustIdleDecayRate; public float jumpForce; public float jumpMaxTime; private float _residualThrust; private float _residualThrustCurrentTime; private float _currentJumpTime; private bool _isJumping; // Variables for RNG movement private float _aiVert; private float _aiHorz; private bool _aiJump; public float cowerDistance; public float emergencyBoostSpeed; public float emergencyBoostCooldownSpeed; public float emergencyBoostTime; public float emergencyBoostCooldown; private bool _emergencyBoosting; private bool _emergencyBoosted; private float _currentEmergencyBoostingTick; private float _currentEmergencyBoosterCooldownTick; private float _currentHoldHeight; private float _heightChangeTick; public float heightChangeInterval; public override void DoAwakeTasks() { base.DoAwakeTasks(); } // Start is called before the first frame update void Start() { // We have to find the player since setting it in the editor doesnt work correctly _player = GameObject.Find("Player"); _blackHole = GameObject.Find("BlackHole"); _residualThrust = 0.0f; _residualThrustCurrentTime = 0.0f; _currentJumpTime = 0.0f; _isJumping = false; _aiJump = false; _aiHorz = 0.0f; _aiVert = 0.0f; _emergencyBoosting = false; _emergencyBoosted = false; _currentEmergencyBoosterCooldownTick = 0.0f; _currentHoldHeight = GetCurrentHeight(); _heightChangeTick = 0.0f; BaseOnStart(); // Custom start stuff can go here } void DoAIPhysics(float currentTimeStep) { } /// /// Calculates some RNG movement for the attached Enemy. /// /// The time unit currently used, typically deltatime or deltafixed time depending on if its the fixed update or not. void DoAI(float currentTimeStep) { // Get the Up vector of each gameObject so we can compare its X values rather than the Y Vector3 playerDirection = _player.transform.position - transform.position; Vector3 playerLocation = Vector3.ProjectOnPlane(_player.transform.position, Vector3.up); Vector3 myLocation = Vector3.ProjectOnPlane(transform.position, Vector3.forward); Vector3 direction = playerLocation - myLocation; float facingPlayer = Vector3.Dot(playerDirection.normalized, transform.forward); // Added a check to see if the difference is big enough to warrant a turn otherwise they just get stuck turning forever float playerHeight = _player.GetComponent().GetCurrentHeight(); _aiVert = 1; _aiHorz = 0.0f; if (playerDirection.magnitude <= cowerDistance) { if (!_emergencyBoosting && !_emergencyBoosted) { // Try to boost away _emergencyBoosting = true; _canSpawnMine = false; _mineSpawnTick = 0.0f; // Force a height change for fun _heightChangeTick = heightChangeInterval; } } if (!(facingPlayer < 0.5f && facingPlayer > -0.5f)) { _aiHorz = -direction.normalized.x; } if (_currentHoldHeight >= GetCurrentHeight()) { RaycastHit aboveCheck; Physics.SphereCast(transform.position, 3.0f, transform.up, out aboveCheck, 5.0f, LayerMask.GetMask("Player")); if (aboveCheck.rigidbody != null) { if ((GetCurrentHeight() < playerHeight)) { _objectRigidbody.velocity = Vector3.zero; _aiJump = false; } else { _aiJump = true; } } else { _aiJump = true; } } else { _aiJump = false; } if (minePrefab != null) { if (_canSpawnMine && _emergencyBoosting) { DoAMineSpawn(); } } } private void DoAMineSpawn() { GameObject newMine = Instantiate(minePrefab); MineHoverMovement mineMovement = newMine.GetComponent(); if (mineMovement) { mineMovement.startPositionVector = transform.position + (-transform.forward * 2.0f); mineMovement.startHeight = GetCurrentHeight() - 0.5f; } newMine.SetActive(true); _canSpawnMine = false; } /// /// Processes the inputs needed to move the gameObject, the turn direction, thrust direction and any residual thrust calculations so that we dont just stop in place. /// Anything in this method can be removed, as long as the _turnDirection and _thrustDirection are set then the parent script with handle the orbit and positioning. /// /// The time unit currently used, typically deltatime or deltafixed time depending on if its the fixed update or not. public override void ProcessPreMovement(float currentTimeStep) { // Check if movement has been frozen if (!freezeMovement) { // Get the Horz and Vert axis from the RNG (or whatever vars you want) float horzInput = _aiHorz; float vertInput = _aiVert; // Check horz direction value, pos = Right, neg = Left if (horzInput > 0) { _turnDirection = 1; } else if (horzInput < 0) { _turnDirection = -1; } else { _turnDirection = 0; } // Check if the vert is pushed forwards or back, pos = forward, neg = back if (vertInput > 0) { _thrusting = true; _thrustDirection = 1; if (_emergencyBoosting) { _thrustDirection *= emergencyBoostSpeed; } else if (_emergencyBoosted) { _thrustDirection *= emergencyBoostCooldownSpeed; } if (_residualThrust < residualThrustMax) { _residualThrust += residualThrustStep * currentTimeStep; } } // If the Enemy is holding backwards else if (vertInput < 0) { if (_residualThrust > 0.0f) { if (_residualThrustCurrentTime >= residualThrustTimeTickMax) { _residualThrust -= residualThrustDecayRate; } _thrustDirection = _residualThrust / residualThrustMax; } else { _thrusting = true; _thrustDirection = -1; } } else { if (_residualThrust > 0.0f) { if (_residualThrustCurrentTime >= residualThrustTimeTickMax) { _residualThrust -= residualThrustIdleDecayRate; } _thrustDirection = _residualThrust / residualThrustMax; } else { _thrusting = false; _thrustDirection = 0; } } // Clamp the residual thrust so we cant fall below 0 or greater than the max value set _residualThrust = Mathf.Clamp(_residualThrust, 0, residualThrustMax); if (_residualThrustCurrentTime >= residualThrustTimeTickMax) { _residualThrustCurrentTime = 0.0f; } else { _residualThrustCurrentTime += currentTimeStep; } // Check if the jump button if (_aiJump && !_isJumping) { _isJumping = true; } // Run any base class stuff base.ProcessPreMovement(currentTimeStep); } } /// /// Method for dealing with the jump physics, this should be in the fixed update since it uses rigidbody physics /// void ProcessJump() { // If the enemy is jumping add the jump force if (_isJumping) { _objectRigidbody.AddRelativeForce(Vector3.up * jumpForce * Time.fixedDeltaTime, ForceMode.Impulse); if (_currentJumpTime >= jumpMaxTime) { _currentJumpTime = 0.0f; _isJumping = false; _aiJump = false; } else { _currentJumpTime += Time.fixedDeltaTime; } } } void ProcessCooldowns(float currentTimeStep) { if (_emergencyBoosting) { if (_currentEmergencyBoostingTick >= emergencyBoostTime) { _emergencyBoosted = true; _emergencyBoosting = false; _currentEmergencyBoostingTick = 0.0f; } else { _currentEmergencyBoostingTick += currentTimeStep; } } if (_emergencyBoosted) { if (_currentEmergencyBoosterCooldownTick >= emergencyBoostCooldown) { _emergencyBoosted = false; _currentEmergencyBoosterCooldownTick = 0.0f; } else { _currentEmergencyBoosterCooldownTick += currentTimeStep; } } if (_heightChangeTick >= heightChangeInterval) { _currentHoldHeight = Random.Range(startHeight, startHeight + 5.0f); _heightChangeTick = 0.0f; } else { _heightChangeTick += currentTimeStep; } if (!_canSpawnMine) { if (_mineSpawnTick >= mineSpawnCooldown) { _mineSpawnTick = 0.0f; _canSpawnMine = true; } else { _mineSpawnTick += currentTimeStep; } } } private void FixedUpdate() { if (objectIsActive) { DoAIPhysics(Time.fixedDeltaTime); ProcessJump(); } } // Update is called once per frame void Update() { if (objectIsActive) { DoAI(Time.deltaTime); ProcessPreMovement(Time.deltaTime); ProcessCurrentMovement(Time.deltaTime); ProcessPostMovement(); ProcessCooldowns(Time.deltaTime); } } }