using System.Collections.Generic; using UnityEngine; using Lightbug.CharacterControllerPro.Core; using Lightbug.Utilities; namespace Lightbug.CharacterControllerPro.Demo { /// /// This component handles all the particles effects associated with the character. /// [AddComponentMenu("Character Controller Pro/Demo/Character/Character Particles")] public class CharacterParticles : MonoBehaviour { [HelpBox("This script contains a \"PlayFootstep\" method, which is intended to be used as an animation event function. " + "Please make sure the animation clip does trigger this event (if you want to see the effect in action).", HelpBoxMessageType.Warning)] [Tooltip("This prefab will be used by the grounded and the footsteps particles.")] [SerializeField] GameObject groundParticlesPrefab = null; [Header("Grounded particles")] [Tooltip("The character vertical speed at the moment of impact (on the horizontal axis) vs the particle system main module start speed (on the vertical axis).")] [SerializeField] AnimationCurve groundParticlesSpeed = AnimationCurve.Linear(0f, 0.5f, 10f, 3f); [Header("Footsteps particles")] [Tooltip("The character on ground speed (on the horizontal axis) vs the particle system main module start speed (on the vertical axis).")] [SerializeField] AnimationCurve footstepParticleSpeed = AnimationCurve.Linear(0f, 0.5f, 10f, 3f); [Tooltip("The character on ground speed (on the horizontal axis) vs the particle system main module start size (on the vertical axis).")] [SerializeField] AnimationCurve footstepParticleSize = AnimationCurve.Linear(0f, 0.5f, 10f, 3f); ParticleSystem[] groundParticlesArray = new ParticleSystem[10]; ParticleSystemPooler particlesPooler = null; MaterialController materialController = null; CharacterActor CharacterActor = null; void Awake() { CharacterActor = this.GetComponentInBranch(); materialController = this.GetComponentInBranch(); if (materialController == null) { Debug.Log("CharacterMaterial component missing"); this.enabled = false; return; } if (groundParticlesPrefab != null) particlesPooler = new ParticleSystemPooler(groundParticlesPrefab, CharacterActor.transform.position, CharacterActor.transform.rotation, 10); } void OnEnable() => CharacterActor.OnGroundedStateEnter += OnGroundedStateEnter; void OnDisable() => CharacterActor.OnGroundedStateEnter -= OnGroundedStateEnter; void OnGroundedStateEnter(Vector3 localVelocity) { Vector3 particlePosition = CharacterActor.transform.position; Quaternion particleRotation = Quaternion.LookRotation(CharacterActor.GroundContactNormal); float fallingSpeed = Mathf.Abs(localVelocity.y); float particlesStartSpeed = groundParticlesSpeed.Evaluate(fallingSpeed); particlesPooler.Instantiate( particlePosition, particleRotation, materialController.CurrentSurface.color, particlesStartSpeed ); } /// /// Public method used by the animation events to play the footsteps particles. /// public void PlayFootstep() { if (!enabled) return; Vector3 particlePosition = CharacterActor.transform.position; Quaternion particleRotation = CharacterActor.GroundContactNormal != Vector3.zero ? Quaternion.LookRotation(CharacterActor.GroundContactNormal) : Quaternion.identity; float groundedSpeed = CharacterActor.Velocity.magnitude; particlesPooler.Instantiate( particlePosition, particleRotation, materialController.CurrentSurface.color, footstepParticleSpeed.Evaluate(groundedSpeed), footstepParticleSize.Evaluate(groundedSpeed) ); } void Update() { particlesPooler.Update(); } } /// /// This class implements a simple "Particle System Pooler". By using this system it is possible to reuse a fixed number of particles, avoiding runtime instantiation (thus improving performance). /// public class ParticleSystemPooler { List activeList = new List(); List inactiveList = new List(); public ParticleSystemPooler(GameObject particlePrefab, Vector3 position, Quaternion rotation, int bufferLength) { for (int i = 0; i < bufferLength; i++) { GameObject gameObject = GameObject.Instantiate(particlePrefab, position, rotation); ParticleSystem particleSystem = gameObject.GetComponent(); ParticleSystem.MainModule mainModule = particleSystem.main; mainModule.playOnAwake = false; particleSystem.Stop(true); if (particleSystem != null) inactiveList.Add(particleSystem); } } ParticleSystem SelectParticle() { ParticleSystem selectedParticle = null; if (inactiveList.Count == 0) { selectedParticle = activeList[0]; } else { selectedParticle = inactiveList[0]; inactiveList.RemoveAt(0); activeList.Add(selectedParticle); } return selectedParticle; } /// /// Puts a particle from the pool into the scene. /// public void Instantiate(Vector3 position, Quaternion rotation) { ParticleSystem particleSystem = SelectParticle(); particleSystem.transform.position = position; particleSystem.transform.rotation = rotation; particleSystem.Play(true); } /// /// Puts a particle from the pool into the scene. /// public void Instantiate(Vector3 position, Quaternion rotation, Color color) { ParticleSystem particleSystem = SelectParticle(); ParticleSystem.MainModule mainModule = particleSystem.main; particleSystem.transform.position = position; particleSystem.transform.rotation = rotation; Color particleColor = mainModule.startColor.color; particleColor.r = color.r; particleColor.g = color.g; particleColor.b = color.b; mainModule.startColor = particleColor; particleSystem.Play(true); activeList.Add(particleSystem); } /// /// Puts a particle from the pool into the scene. /// public void Instantiate(Vector3 position, Quaternion rotation, Color color, float startSpeed) { ParticleSystem particleSystem = SelectParticle(); ParticleSystem.MainModule mainModule = particleSystem.main; particleSystem.transform.position = position; particleSystem.transform.rotation = rotation; Color particleColor = mainModule.startColor.color; particleColor.r = color.r; particleColor.g = color.g; particleColor.b = color.b; mainModule.startColor = particleColor; mainModule.startSpeed = startSpeed; particleSystem.Play(true); activeList.Add(particleSystem); } /// /// Puts a particle from the pool into the scene. /// public void Instantiate(Vector3 position, Quaternion rotation, Color color, float startSpeed, float startSize) { ParticleSystem particleSystem = SelectParticle(); ParticleSystem.MainModule mainModule = particleSystem.main; particleSystem.transform.position = position; particleSystem.transform.rotation = rotation; Color particleColor = mainModule.startColor.color; particleColor.r = color.r; particleColor.g = color.g; particleColor.b = color.b; mainModule.startColor = particleColor; mainModule.startSpeed = startSpeed; mainModule.startSize = startSize; particleSystem.Play(true); activeList.Add(particleSystem); } /// /// Updates the system. /// public void Update() { for (int i = activeList.Count - 1; i >= 0; i--) { ParticleSystem particleSystem = activeList[i]; if (!particleSystem.isPlaying) { activeList.RemoveAt(i); inactiveList.Add(particleSystem); } } } } }