using System; using System.Collections.Generic; using Ichni.RhythmGame.Beatmap; using UnityEngine; using Object = UnityEngine.Object; namespace Ichni.RhythmGame { public partial class ParticleEmitter : GameElement, IHaveParticles, IHaveTimeDurationSubmodule, IHaveTransformSubmodule, IHaveColorSubmodule { #region [暴露属性字段] Essential Configs public string themeBundleName; public string materialName; public bool prewarm; public float playTime; public float stopTime; public ParticleSystemSimulationSpace simulationSpace; public float density; public float lifeTime; public float speed; public float radius; public bool isAutoOrient; public Vector3 particleRotation; #endregion #region [计算与状态缓存] Calculated & Cached States private List themeBundleList; private List materialNameList; public bool haveBaseColor => true; public bool haveEmissionColor => true; #endregion #region [子模块接口] Submodules public ParticleSystem particle { get; set; } private IHaveParticles particlesContainer => this; public TimeDurationSubmodule timeDurationSubmodule { get; set; } public TransformSubmodule transformSubmodule { get; set; } public ColorSubmodule colorSubmodule { get; set; } #endregion #region [生命周期] Lifecycle & Factory public static ParticleEmitter GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, GameElement parentElement, string themeBundleName, string materialName, bool prewarm, float playTime, float stopTime, ParticleSystemSimulationSpace simulationSpace, float density, float lifeTime, float speed, float radius, bool isAutoOrient, Vector3 particleRotation) { ParticleEmitter particleEmitter = Instantiate(GameManager.Instance.basePrefabs.particleEmitter, parentElement.transform) .GetComponent(); particleEmitter.particle = particleEmitter.GetComponent(); particleEmitter.Initialize(elementName, id, tags, isFirstGenerated, parentElement); particleEmitter.playTime = playTime; particleEmitter.stopTime = stopTime; particleEmitter.themeBundleList = ThemeBundleManager.instance.loadedThemeBundleList.ConvertAll(x => x.themeBundleName); particleEmitter.materialNameList = new List(); particleEmitter.themeBundleName = themeBundleName; particleEmitter.materialName = materialName; particleEmitter.particlesContainer.SetParticleMaterial(themeBundleName, materialName); particleEmitter.SetParticleSettings(prewarm, simulationSpace, density, lifeTime, speed, radius, isAutoOrient, particleRotation, false); return particleEmitter; } public void SetParticleSettings(bool prewarm, ParticleSystemSimulationSpace simulationSpace, float density, float lifeTime, float speed, float radius, bool isAutoOrient, Vector3 particleRotation, bool mark) { //这个Mark没有任何作用,只是为了让解释器把interface中的函数和这个函数区分开。否则会Stackoverflow。 this.prewarm = prewarm; this.simulationSpace = simulationSpace; this.density = density; this.lifeTime = lifeTime; this.speed = speed; this.radius = radius; this.isAutoOrient = isAutoOrient; this.particleRotation = particleRotation; (this as IHaveParticles).SetParticleSettings(prewarm, simulationSpace, density, lifeTime, speed, radius, isAutoOrient, particleRotation); } public override void SetDefaultSubmodules() { timeDurationSubmodule = new TimeDurationSubmodule(this); transformSubmodule = new TransformSubmodule(this); colorSubmodule = new ColorSubmodule(this, Color.white, true, Color.white, 0); } #endregion } #region [轮询更新与交互重写] Main Update & Behavior Overrides public partial class ParticleEmitter { private void Update() { float songTime = CoreServices.TimeProvider.SongTime; if (playTime > songTime || stopTime < songTime) { particle.Stop(); } else { if (!particle.isPlaying) { particle.Play(); } } } public override void Refresh() { base.Refresh(); ParticleSystemRenderer particleSystemRenderer = particle.GetComponent(); particleSystemRenderer.material.SetColor("_BaseColor", colorSubmodule.currentBaseColor); if (colorSubmodule.emissionEnabled) { particleSystemRenderer.material.SetFloat("_EnableEmission", 1); particleSystemRenderer.material.SetColor("_EmissionColor", colorSubmodule.GetCurrentEmissionColor()); } else { particleSystemRenderer.material.SetFloat("_EnableEmission", 0); } } } #endregion #region [接口能力] Interface public interface IHaveParticles { public ParticleSystem particle { get; set; } public virtual void SetParticleMaterial(string themeBundleName, string materialName) { Material material = ThemeBundleManager.instance.GetObject(themeBundleName, materialName); if (material == null) { material = ThemeBundleManager.instance.GetObject("basic", "Basic_Track_Default"); } Renderer particleRenderer = particle.GetComponent(); particleRenderer.material = Object.Instantiate(material); particleRenderer.InitializeShader(); } public virtual void SetParticleSettings(bool prewarm, ParticleSystemSimulationSpace simulationSpace, float density, float lifeTime, float speed, float radius, bool isAutoOrient, Vector3 particleRotation) { SetPrewarm(prewarm); SetSimulationSpace(simulationSpace); SetDensity(density); SetLifeTime(lifeTime); SetSpeed(speed); SetRadius(radius); SetAlignment(isAutoOrient, particleRotation); } public void SetPrewarm(bool prewarm) { var mainModule = particle.main; mainModule.prewarm = prewarm; mainModule.maxParticles = 500; } public void SetSimulationSpace(ParticleSystemSimulationSpace simulationSpace) { var mainModule = particle.main; mainModule.simulationSpace = simulationSpace; } public void SetDensity(float density) { var emission = particle.emission; emission.rateOverTime = density; } public void SetLifeTime(float lifeTime) { var mainModule = particle.main; mainModule.startLifetime = lifeTime; } public void SetSpeed(float speed) { var mainModule = particle.main; mainModule.startSpeed = speed; } public void SetRadius(float radius) { var shapeModule = particle.shape; shapeModule.radius = radius; } public void SetAlignment(bool isAutoOrient, Vector3 particleRotation = default) { ParticleSystemRenderer particleSystemRenderer = particle.GetComponent(); var mainModule = particle.main; if (isAutoOrient) { particleSystemRenderer.alignment = ParticleSystemRenderSpace.View; mainModule.startRotation3D = false; // 禁用3D旋转 } else { particleSystemRenderer.alignment = ParticleSystemRenderSpace.Local; mainModule.startRotation3D = true; // 启用3D旋转 SetParticleRotation(particleRotation); } } public void SetParticleRotation(Vector3 particleRotation) { Vector3 vector3Rotation = particleRotation * Mathf.Deg2Rad; var mainModule = particle.main; mainModule.startRotationX = vector3Rotation.x; mainModule.startRotationY = vector3Rotation.y; mainModule.startRotationZ = vector3Rotation.z; } } #endregion }