using System; using System.Collections; using System.Collections.Generic; using Invector.vCharacterController.AI; using PixelCrushers.DialogueSystem; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using Random = UnityEngine.Random; namespace Beyond { public class WonderingShadow : MonoBehaviour { public enum Mode { DESTROY_ON_TARGET, DESTROY_WHEN_NEAR, WONDER } public enum State { STARTING, MOVING, STOPPING, IDLE, GONE }; [Serializable] public class ShadowData { public GameObject objectToSpawn; public float spawnedObjectYOffset = 0.1f; public Mode mode = Mode.DESTROY_WHEN_NEAR; public float distanceToDestroy = 7f; public LayerMask spawnedObjectRayMask = ~0; public Vector3 targetPosition; public UnityEvent OnDead; } public ShadowData m_shadowData = new ShadowData(); public UnityEvent m_OnStateChanged = new UnityEvent(); public GameObject m_endEffect; public float m_endEffectDestroyDelay = 7f; private State m_state; private const float MinDistance = 0.15f; // public float Distance = 30; public float Speed = 1; public float TimeDelay = 0; public float m_StartStopDelay = 1f; public float RandomMoveRadius = 0; public float RandomMoveSpeedScale = 0; private Vector3 startPosition; private Vector3 startPositionLocal; private Transform t; private Vector3 oldPos; private Quaternion startQuaternion; //private float currentSpeed; private float currentDelay; private bool isInitialized; private int dropFirstFrameForFixUnityBugWithParticles = 3; private Vector3 randomTimeOffset; private float m_timer; public ParticleSystem[] m_particlesToReset; private ShadowArea m_shadowArea; public void ResetParticles() { foreach (ParticleSystem p in m_particlesToReset) { p.Clear(); p.time = 0; p.Simulate(0, true, true); p.Play(); } } private void Start() { t = transform; SetState(State.STARTING); m_shadowArea = gameObject.FindComponentUpHierarchy(); Initialize(false); } private void Initialize(bool setTarget) { if (setTarget) { if (m_shadowArea != null) { m_shadowData.targetPosition = m_shadowArea.GetRandomPosition(); } else { //if there is no area, just come back to starting location m_shadowData.targetPosition = startPosition; } } startQuaternion = t.rotation; startPositionLocal = t.localPosition; startPosition = t.position; oldPos = startPosition; //currentSpeed = Speed; currentDelay = 0; //dropFirstFrameForFixUnityBugWithParticles = 3; randomTimeOffset = Random.insideUnitSphere * 10; //isInitialized = true; } public void SpawnEndEffect() { if (m_endEffect == null) return; var instance = Instantiate(m_endEffect, transform.position, transform.rotation); Destroy(instance, m_endEffectDestroyDelay); } public void SpawnEndObject() { if (m_shadowData.objectToSpawn == null) return; Vector3 pos = transform.position; RaycastHit hit = new RaycastHit(); if (Physics.Raycast(pos, Vector3.down, out hit, 100f, m_shadowData.spawnedObjectRayMask)) { pos.y = hit.point.y + m_shadowData.spawnedObjectYOffset; } var rotation = Quaternion.LookRotation(transform.rotation * Vector3.forward, Vector3.up); var instance = Instantiate(m_shadowData.objectToSpawn, pos, rotation); var spawnedAI = instance.GetComponent(); if (spawnedAI) { spawnedAI.onDead.AddListener(OnKilled); } if (Player.Instance != null) { Vector3 toPlayer = Player.Instance.transform.position - pos; toPlayer.y = 0f; rotation = Quaternion.LookRotation(transform.rotation * toPlayer, Vector3.up); instance.transform.rotation = rotation; } } public void OnKilled(GameObject go) { m_shadowData.OnDead?.Invoke(go); } private void SetState(State state) { switch (state) { case State.IDLE: break; case State.STARTING: //t.position = m_area.GetRandomPosition(); //m_targetPosition = m_area.GetRandomPosition(); //ResetParticles(); //ActivateEffects(true); // Initialize(); //Initialize(); //SetState(State.MOVING); break; case State.STOPPING: // SetState(State.STARTING); break; case State.MOVING: break; case State.GONE: SpawnEndEffect(); SpawnEndObject(); // Destroy(gameObject); gameObject.SetActive(false); break; } m_state = state; m_timer = 0f; m_OnStateChanged?.Invoke(state); } private void Update() { switch (m_state) { case State.MOVING: if (dropFirstFrameForFixUnityBugWithParticles <= 0) { UpdateWorldPosition(); } else { dropFirstFrameForFixUnityBugWithParticles--; } break; case State.STARTING: if (m_timer > m_StartStopDelay) { SetState(State.MOVING); } break; case State.STOPPING: if (m_timer > m_StartStopDelay) { //ResetParticles(); //ActivateEffects(false); if (m_shadowData.mode != Mode.DESTROY_ON_TARGET) { Initialize(true); SetState(State.STARTING); } else { SetState(State.GONE); } } break; case State.IDLE: break; } m_timer += Time.deltaTime; } /* private void LateUpdate() { if (dropFirstFrameForFixUnityBugWithParticles > 0) { Initialize(); } } */ private void UpdateWorldPosition() { currentDelay += Time.deltaTime; if (currentDelay < TimeDelay) return; Vector3 randomOffset = Vector3.zero; if (RandomMoveRadius > 0) { randomOffset = GetRadiusRandomVector() * RandomMoveRadius; var fade = Vector3.Distance(t.position, m_shadowData.targetPosition) / Vector3.Distance(startPosition, m_shadowData.targetPosition); randomOffset *= fade; } var frameMoveOffset = Vector3.zero; var frameMoveOffsetWorld = Vector3.zero; //if (!isCollided && !isOutDistance) { //currentSpeed = Mathf.Clamp(currentSpeed - Speed*Dampeen*Time.deltaTime, MinSpeed, Speed); if (m_shadowData.targetPosition == null) { var currentForwardVector = (Vector3.forward + randomOffset) * Speed * Time.deltaTime; frameMoveOffset = t.localRotation * currentForwardVector; frameMoveOffsetWorld = startQuaternion * currentForwardVector; } else { var forwardVec = (m_shadowData.targetPosition - t.position).normalized; var currentForwardVector = (forwardVec + randomOffset) * Speed * Time.deltaTime; frameMoveOffset = currentForwardVector; frameMoveOffsetWorld = currentForwardVector; } } var currentDistance = (t.localPosition + frameMoveOffset - startPositionLocal).magnitude; var targetDistance = (t.position + frameMoveOffset - m_shadowData.targetPosition).magnitude; Debug.DrawRay(t.position, frameMoveOffsetWorld.normalized * (currentDistance)); if (targetDistance < MinDistance + frameMoveOffset.magnitude) { SetState((State.STOPPING)); } else if (m_shadowData.mode == Mode.DESTROY_WHEN_NEAR) { if (Player.Instance == null) return; var dist = Vector3.Distance(Player.Instance.transform.position, t.position); if (dist < m_shadowData.distanceToDestroy) { SetState(State.GONE); } } t.position = oldPos + frameMoveOffsetWorld; oldPos = t.position; } private Vector3 GetRadiusRandomVector() { var x = Time.time * RandomMoveSpeedScale + randomTimeOffset.x; var vecX = Mathf.Sin(x / 7 + Mathf.Cos(x / 2)) * Mathf.Cos(x / 5 + Mathf.Sin(x)); x = Time.time * RandomMoveSpeedScale + randomTimeOffset.y; var vecY = Mathf.Cos(x / 8 + Mathf.Sin(x / 2)) * Mathf.Sin(Mathf.Sin(x / 1.2f) + x * 1.2f); x = Time.time * RandomMoveSpeedScale + randomTimeOffset.z; var vecZ = Mathf.Cos(x * 0.7f + Mathf.Cos(x * 0.5f)) * Mathf.Cos(Mathf.Sin(x * 0.8f) + x * 0.3f); return new Vector3(vecX, vecY, vecZ); } } }