/// <summary> /// Reads the state of the object from an <see cref="IValueReader"/>. Values should be read in the exact /// same order as they were written. /// </summary> /// <param name="reader">The <see cref="IValueReader"/> to read the values from.</param> public void ReadState(IValueReader reader) { // Read the primary values Name = reader.ReadString(_nameKeyName); BlendMode = reader.ReadEnum <BlendMode>(_blendModeKeyName); Budget = reader.ReadInt(_budgetKeyName); EmitterLife = reader.ReadInt(_emitterLifeKeyName); ParticleLife = reader.ReadVariableInt(_particleLifeKeyName); Origin = reader.ReadVector2(_originKeyName); ReleaseAmount = reader.ReadVariableUShort(_releaseAmountKeyName); ReleaseColor = reader.ReadVariableColor(_releaseColorKeyName); ReleaseRate = reader.ReadVariableUShort(_releaseRateKeyName); ReleaseRotation = reader.ReadVariableFloat(_releaseRotationKeyName); ReleaseScale = reader.ReadVariableFloat(_releaseScaleKeyName); ReleaseSpeed = reader.ReadVariableFloat(_releaseSpeedKeyName); Sprite.SetGrh(reader.ReadGrhIndex(_grhIndexKeyName)); // Read the custom values var customValuesReader = reader.ReadNode(_customValuesNodeName); ReadCustomValues(customValuesReader); // Read the modifier collection ParticleModifiers.Read(_particleModifiersNodeName, reader); EmitterModifiers.Read(_emitterModifiersNodeName, reader); }
/// <summary> /// Writes the state of the object to an <see cref="IValueWriter"/>. /// </summary> /// <param name="writer">The <see cref="IValueWriter"/> to write the values to.</param> public void WriteState(IValueWriter writer) { // Write the primary values writer.Write(_nameKeyName, Name); writer.Write(_blendModeKeyName, BlendMode); writer.Write(_budgetKeyName, Budget); writer.Write(_emitterLifeKeyName, EmitterLife); writer.Write(_particleLifeKeyName, ParticleLife); writer.Write(_originKeyName, Origin); writer.Write(_releaseAmountKeyName, ReleaseAmount); writer.Write(_releaseColorKeyName, ReleaseColor); writer.Write(_releaseRateKeyName, ReleaseRate); writer.Write(_releaseRotationKeyName, ReleaseRotation); writer.Write(_releaseScaleKeyName, ReleaseScale); writer.Write(_releaseSpeedKeyName, ReleaseSpeed); writer.Write(_grhIndexKeyName, Sprite.GrhData != null ? Sprite.GrhData.GrhIndex : GrhIndex.Invalid); // Write the custom values writer.WriteStartNode(_customValuesNodeName); { WriteCustomValues(writer); } writer.WriteEndNode(_customValuesNodeName); // Write the modifier collection ParticleModifiers.Write(_particleModifiersNodeName, writer); EmitterModifiers.Write(_emitterModifiersNodeName, writer); }
/// <summary> /// Copies the values in this <see cref="ParticleEmitter"/> to another. /// </summary> /// <param name="destination">The <see cref="ParticleEmitter"/> to copy the values to.</param> public void CopyValuesTo(IParticleEmitter destination) { var d = (ParticleEmitter)destination; d.BlendMode = BlendMode; d.Budget = Budget; d.EmitterLife = EmitterLife; d.ParticleLife = ParticleLife; d.Origin = Origin; d.Name = Name; d.ReleaseAmount = ReleaseAmount; d.ReleaseColor = ReleaseColor; d.ReleaseRate = ReleaseRate; d.ReleaseRotation = ReleaseRotation; d.ReleaseScale = ReleaseScale; d.ReleaseSpeed = ReleaseSpeed; d.Sprite.SetGrh(Sprite.GrhData, Sprite.AnimType, Sprite.LastUpdated); d.ParticleModifiers.Clear(); d.ParticleModifiers.AddRange(ParticleModifiers.Select(x => x.DeepCopy())); d.EmitterModifiers.Clear(); d.EmitterModifiers.AddRange(EmitterModifiers.Select(x => x.DeepCopy())); }
/// <summary> /// Updates the <see cref="ParticleEmitter"/> and all <see cref="Particle"/>s it has created. /// </summary> /// <param name="currentTime">The current time.</param>> internal void Update(TickCount currentTime) { var forceEmit = false; // Get the elapsed time // On the first update, just assume 33 ms have elapsed int elapsedTime; if (_lastUpdateTime == TickCount.MinValue) { // This is the very first update forceEmit = true; _nextReleaseTime = currentTime; elapsedTime = 33; } else { // Not the first update elapsedTime = (int)Math.Min(MaxDeltaTime, currentTime - _lastUpdateTime); } _lastUpdateTime = currentTime; // Update the emitter modifiers EmitterModifiers.ProcessEmitter(this, elapsedTime); // Check if the sprite is loaded if (Sprite == null || Sprite.GrhData == null) { EmitterModifiers.RestoreEmitter(this); return; } // Update the current time on the modifiers ParticleModifiers.UpdateCurrentTime(currentTime); // Update the sprite Sprite.Update(currentTime); // Check to spawn more particles if (forceEmit || (RemainingLife != 0 && ReleaseAmount.Max > 0 && ReleaseRate.Max > 0)) { // Do not allow the releasing catch-up time to exceed the _maxDeltaTime if (_nextReleaseTime < currentTime - MaxDeltaTime) { _nextReleaseTime = currentTime - MaxDeltaTime; } // Keep calculating the releases until we catch up to the current time var amountToRelease = 0; while (_nextReleaseTime <= currentTime) { amountToRelease += ReleaseAmount.GetNext(); _nextReleaseTime += ReleaseRate.GetNext(); } // Release the particles, if there are any if (amountToRelease > 0) { ReleaseParticles(currentTime, amountToRelease); } } else { // Set the next release time to now so that if the emitter starts releasing, it won't release a ton // as it catches back up _nextReleaseTime = currentTime - MaxDeltaTime; } // Update the particles var hasUpdateModifiers = ParticleModifiers.HasUpdateModifiers; var i = 0; Vector2 minParticlePosition = Vector2.Zero; Vector2 maxParticlePosition = Vector2.Zero; Vector2 spriteSize = Sprite.Size; while (i <= _lastAliveIndex) { var particle = _particles[i]; // Check if the particle has expired if (particle.LifeEnd <= currentTime) { // We do NOT increment because once we expire the particle, this slot will become in use by // another particle, so we will have to update at this index again ExpireParticle(i); continue; } // Process the particle with the modifiers if (hasUpdateModifiers) { ParticleModifiers.ProcessUpdatedParticle(this, particle, elapsedTime); } // Update the particle particle.Update(elapsedTime); // Update the min/max origin offsets Vector2 posMin = particle.Position; Vector2 particleSize; Vector2.Multiply(ref spriteSize, particle.Scale, out particleSize); Vector2 posMax; Vector2.Add(ref posMin, ref particleSize, out posMax); if (posMin.X < minParticlePosition.X) { minParticlePosition.X = posMin.X; } if (posMin.Y < minParticlePosition.Y) { minParticlePosition.Y = posMin.Y; } if (posMax.X > maxParticlePosition.X) { maxParticlePosition.X = posMax.X; } if (posMax.Y > maxParticlePosition.Y) { maxParticlePosition.Y = posMax.Y; } ++i; } _minParticlePosition = minParticlePosition; _maxParticlePosition = maxParticlePosition; EmitterModifiers.RestoreEmitter(this); }