Esempio n. 1
0
        /// <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);
        }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
        /// <summary>
        /// Releases one or more particles.
        /// </summary>
        /// <param name="currentTime">The current time.</param>
        /// <param name="amount">The number of particles to release.</param>
        void ReleaseParticles(TickCount currentTime, int amount)
        {
            // Find how many we can actually release
            var lastIndex = Math.Min(Budget - 1, _lastAliveIndex + amount);

            // Ensure our particles array is large enough to fit the new particles.
            // When we resize the array, we use the "next power of two" sizing concept to reduce the
            // memory fragmentation (.NET internally does the same with most collections). To speed things up,
            // we just find the next power of two instead of looping until we have a large enough value.
            if (_particles.Length - 1 < lastIndex)
            {
                var newSize = BitOps.NextPowerOf2(lastIndex + 1);
                Debug.Assert(BitOps.IsPowerOf2(newSize),
                             "If this assert fails, something is probably wrong with BitOps.NextPowerOf2() or BitOps.IsPowerOf2().");
                Debug.Assert(newSize >= lastIndex + 1);
                Array.Resize(ref _particles, newSize);
            }

            // Start releasing the particles
            var hasReleaseModifiers = ParticleModifiers.HasReleaseModifiers;

            for (var i = _lastAliveIndex + 1; i <= lastIndex; i++)
            {
                var particle = _particles[i];
                if (particle == null)
                {
                    particle      = Particle.Create();
                    _particles[i] = particle;
                }

                // Set up the particle
                particle.Momentum  = Vector2.Zero;
                particle.LifeStart = currentTime;
                particle.LifeEnd   = (TickCount)(currentTime + ParticleLife.GetNext());
                particle.Rotation  = ReleaseRotation.GetNext();
                particle.Scale     = ReleaseScale.GetNext();
                ReleaseColor.GetNext(ref particle.Color);

                // Get the offset and force
                Vector2 offset;
                Vector2 force;
                GenerateParticleOffsetAndForce(particle, out offset, out force);

                // Set the position
                Vector2.Add(ref _origin, ref offset, out particle.Position);

                // Set the velocity
                Vector2.Multiply(ref force, ReleaseSpeed.GetNext(), out particle.Velocity);

                if (hasReleaseModifiers)
                {
                    ParticleModifiers.ProcessReleasedParticle(this, particle);
                }
            }

            // Increase the index of the last active particle
            _lastAliveIndex = lastIndex;
        }
Esempio n. 4
0
        /// <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()));
        }
Esempio n. 5
0
        /// <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);
        }