Click here to Skip to main content
Click here to Skip to main content

Learning XNA 2D Engine IceCream With 1945 Demo Project

By , 8 Aug 2012
 
1945_Code_Package.zip
1945 Code Package
IceCream
XNA4
IceCream
Attributes
Background.png
Components
Content
BloomCombineEffect.fx
BloomExtractEffect.fx
Content.contentproj.vspscc
DefaultParticleTexture.png
DefaultTileGridTexture.png
diagnosticFont.spritefont
GaussianBlurEffect.fx
IceCreamContent.contentproj
IceCreamContent.contentproj.user
RefractionLayerEffect.fx
Debug
Console
Drawing
IceEffects
IceCream.csproj.user
IceCreamMono.pidb
Input
Properties
QuadTree
Resources
WIN
Arial.xnb
blank.xnb
BloomCombineEffect.xnb
BloomExtractEffect.xnb
DefaultFont.xnb
DefaultParticleTexture.xnb
DefaultTexture.xnb
DefaultTileGridTexture.xnb
diagnosticFont.xnb
GaussianBlurEffect.xnb
RefractionLayerEffect.xnb
XBOX
BloomCombineEffect.xnb
BloomExtractEffect.xnb
DefaultFont.xnb
DefaultParticleTexture.xnb
DefaultTileGridTexture.xnb
diagnosticFont.xnb
GaussianBlurEffect.xnb
RefractionLayerEffect.xnb
SceneItems
AnimationClasses
CompositeEntityClasses
ParticlesClasses
TileGridClasses
TypeConverters
Serialization
Utility
Milkshake
Editors
AnimatedSprites
Components
CompositeEntities
Materials
Particles
PostProcessAnimations
Sprites
TileGrids
TileSheet
Graphics
GraphicsDeviceControls
IceCream_Icon_128px.ico
Lib
XPTable.dll
Milkshake_TemporaryKey.pfx
Properties
app.manifest
Resources
car.png
checker.png
color_swatch.png
cross.png
font.png
pivot.png
plugin.png
ppBackground.jpg
sport_soccer.png
user.png
weather_lightning.png
weather_sun.png
Selectors
Tools
Wizards
MilkshakeLibrary
Properties
Templates
IceComponent.zip
IceCreamWindowsGame.zip
IceCream1945
IceCream1945
IceCream1945
Components
Game.ico
Game.icproj
GameScenes
GameThumbnail.png
Global
Properties
IceCream1945Content
1945.png
Ending.icescene
EnemyBulletsAndIslands.png
global.ice
IceCream1945Content.contentproj
Level1.icescene
Music
Sounds
ExplosionBomb.wav
ExplosionHit.wav
Laser_Shoot_Player.wav
Laser_Shoot_Turret.wav
Laser_Shoot3.wav
Quick_Hit.wav
Splash.icescene
1945_Demo_Package-noexe.zip
1945_Demo_Package
Game.icproj
IceCream1945Content
1945.xnb
Ending.icescene
EnemyBulletsAndIslands.xnb
global.ice
Level1.icescene
Sounds
ExplosionBomb.xnb
ExplosionHit.xnb
Laser_Shoot_Player.xnb
Laser_Shoot_Turret.xnb
Laser_Shoot3.xnb
Quick_Hit.xnb
Splash.icescene
1945_Demo_Package.zip
Game.icproj
IceCream.dll
IceCream1945.exe
1945.xnb
Ending.icescene
EnemyBulletsAndIslands.xnb
global.ice
Level1.icescene
Music
ExplosionBomb.xnb
ExplosionHit.xnb
Laser_Shoot_Player.xnb
Laser_Shoot_Turret.xnb
Laser_Shoot3.xnb
Quick_Hit.xnb
Splash.icescene
#if XNATOUCH
using XnaTouch.Framework;
using XnaTouch.Framework.Audio;
using XnaTouch.Framework.Content;
using XnaTouch.Framework.GamerServices;
using XnaTouch.Framework.Graphics;
using XnaTouch.Framework.Input;
using XnaTouch.Framework.Media;
using XnaTouch.Framework.Net;
using XnaTouch.Framework.Storage;
#else
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


#endif

using System;
using System.Collections.Generic;
using IceCream.Drawing;
using System.Diagnostics;

namespace IceCream.SceneItems.ParticlesClasses
{
    public class Particle
    {
        #region Static Fields and Methods

        /// <summary>
        /// Get the linear interpolation value with an added random variation
        /// </summary>
        /// <param name="values">The key values</param>
        /// <param name="variations">The key variation values</param>
        /// <param name="lerpLife">The life, between 0 and 1</param>
        /// <returns></returns>
        public static float GetLerpValueWithVariation(List<Vector2> values, List<Vector2> variations, float lerpLife)
        {            
            return GetLerpValue(values, lerpLife) + Particle.GetRandomVariationValue(Particle.GetLerpValue(variations, lerpLife));
        }

        /// <summary>
        /// Get the liner interpolation value corresponding to the current life
        /// </summary>
        /// <param name="values">The key values</param>
        /// <param name="life">The life, between 0 and 1</param>
        /// <returns></returns>
        public static float GetLerpValue(List<Vector2> values, float life)
        {            
            // return 0 by default for an empty set
            if (values == null || values.Count == 0)
            {
                return 0;
            }
            // if there is only one value, return it
            else if (values.Count == 1)
            {
                return values[0].Y;
            }           
            if (life > values[values.Count - 1].X)
            {
                life = values[values.Count - 1].X;
            }
            for (int i = 0; i < values.Count -1; i++)
            {
                if (life <= values[i + 1].X || i == values.Count - 2)
                {                    
                    // return the linear interpolation between this key and the next one
                    return MathHelper.Lerp(values[i].Y, values[i + 1].Y, (life - values[i].X) / (values[i + 1].X - values[i].X));
                }
            }
            throw new Exception("Error with the Lerp values request!");
        }

        /// <summary>
        /// get a random value between -value/2 and value/2
        /// </summary>
        public static float GetRandomVariationValue(float value)
        {
            if (value == 0)
            {
                return 0;
            }
            else if (value < 0)
            {
                value = -value;
            }            
            return Randomizer.Float(-value / 2f, value / 2f);
        }

        #endregion

        #region Fields

        private DrawRequest _drawRequest;

        private bool _isActive;
        private bool _keepAspectRatio;
        private float _maxLife;
        private float _currentLife;
        private ParticleOrientationType _particleOrientationType;

        // initial values        
        private float _initialWidth;
        private float _initialHeight;
        private float _initialVelocity;        
        private float _initialWeight;
        private float _initialSpin;
        private float _initialMotionRandom;
        private float _initialDirection;
        private Vector4 _initialColorFloatValues;

        // working values (for over life changes)
        private float _velocity;
        private float _weight;
        private float _spin;
        private float _motionRandom;
        private Vector2 _deltaPos;
        private Vector2 _deltaPosAttractor;
        private Vector2 _gravity;
        
        private OverLifeSettings overLifeSettings;

        private ParticleType _parent;
        private float? _initialDistanceFromAttractor;

        private float _animationTotalTimeElapsed = 0;
        private int _currentTile = -1;
        private bool _animationIsStopped = false;

        #endregion

        #region Properties

        public bool IsActive
        {
            get
            {
                return _isActive;
            }
            set
            {
                _isActive = value;
            }
        }

        public Vector2 Position
        {
            get { return _drawRequest.position; }
            set { _drawRequest.position = value; }
        }

        #endregion

        #region Methods

        public void Draw(int layer, DrawingBlendingType blendingType)
        {
            if (_isActive)
            {                
                DrawingManager.DrawOnLayer(_drawRequest, layer, blendingType);                
            }
        }

        public Rectangle GetSourceRectOfTile()
        {
            if (_parent.TileCols > 0 && _parent.TileRows > 0)
            {
                int tileTotal = _parent.TileRows * _parent.TileCols - _parent.TileTotalOffset;
                if (_currentTile >= tileTotal)
                {
                    _currentTile = tileTotal - 1;
                }              
                // Extract the right area of the sprite for the given frame.
                int x = _currentTile % _parent.TileCols;
                int y = _currentTile / _parent.TileCols;
                Rectangle srcrect = new Rectangle(x * _parent.TileWidth + x * 2, y * _parent.TileHeight + y * 2,
                    _parent.TileWidth, _parent.TileHeight);                
                return srcrect;
            }
            return Rectangle.Empty;
        }

        /// <summary>
        /// Update the properties of a particle
        /// </summary>
        public void Update(float elapsed, float opacityModifier)
        {
            // get the percentage of life
            float lerpLife;
            Vector2 realDistanceVector = Vector2.Zero;
            //float dist = 0;
            if ( _initialDistanceFromAttractor != null)
            {
               
                //lerpLife = MathHelper.Clamp(1.0f - dist / _initialDistanceFromAttractor.Value, 0, 1);
                //if (dist <= 1.0f)
                //{
                //    // mark the particle as dead
                //    _isActive = false;
                //    return;
                //}
                if (_currentLife > _maxLife)
                {
                    // mark the particle as dead
                    _isActive = false;
                    return;
                }
                lerpLife = _currentLife / _maxLife;
            }
            else
            {
                if (_currentLife > _maxLife)
                {
                    // mark the particle as dead
                    _isActive = false;
                    return;
                }
                lerpLife = _currentLife / _maxLife;
            }
            // apply the tile source rectangle if needed
            if (_parent.TileWidth > 0 && _parent.TileHeight > 0)
            {
                if (_parent.AnimationFrameDelay > 0 && _animationIsStopped == false)
                {
                    int tileTotal = _parent.TileRows * _parent.TileCols - _parent.TileTotalOffset;
                    _animationTotalTimeElapsed += elapsed;
                    if (_animationTotalTimeElapsed > _parent.AnimationFrameDelay / 1000f)
                    {
                        _animationTotalTimeElapsed = 0;
                        _currentTile++;
                        if (_currentTile >= tileTotal)
                        {
                            if (_parent.AnimationLoop != true)
                            {
                                _animationIsStopped = true;                                
                            }
                            else
                            {
                                _currentTile = 0;
                            }
                        }
                    }
                }
                _drawRequest.sourceRectangle = GetSourceRectOfTile();
            }
            else
            {
                _drawRequest.sourceRectangle = null;
            }
            // apply Over Life settings
            this._drawRequest.scaleRatio.X = _initialWidth * GetLerpValue(overLifeSettings.widthOverLife.Values, lerpLife);
            if (_keepAspectRatio)
            {
                _drawRequest.scaleRatio.Y = _drawRequest.scaleRatio.X;
            }
            else
            {
                _drawRequest.scaleRatio.Y = _initialHeight * GetLerpValue(overLifeSettings.heightOverLife.Values, lerpLife);
            }
            _velocity = _initialVelocity * GetLerpValue(overLifeSettings.velocityOverLife.Values, lerpLife);
            _weight = _initialWeight * GetLerpValue(overLifeSettings.weightOverLife.Values, lerpLife);
            _spin = _initialSpin * GetLerpValue(overLifeSettings.spinOverLife.Values, lerpLife);
            _motionRandom = _initialMotionRandom * GetLerpValue(overLifeSettings.motionRandomOverLife.Values, lerpLife);
            _drawRequest.tint.R = (byte)(255 * (_initialColorFloatValues.X * GetLerpValue(overLifeSettings.redTintOverLife.Values, lerpLife)));
            _drawRequest.tint.G = (byte)(255 * (_initialColorFloatValues.Y * GetLerpValue(overLifeSettings.greenTintOverLife.Values, lerpLife)));
            _drawRequest.tint.B = (byte)(255 * (_initialColorFloatValues.Z * GetLerpValue(overLifeSettings.blueTintOverLife.Values, lerpLife)));
            _drawRequest.tint.A = (byte)(255 * (_initialColorFloatValues.W * GetLerpValue(overLifeSettings.opacityOverLife.Values, lerpLife) * opacityModifier));
            // calc the new position offset due to velocity
            _deltaPos = new Vector2(_velocity * (float)Math.Cos((double)_initialDirection), _velocity * (float)Math.Sin((double)_initialDirection));
            // if attraction is enabled
            if (_parent.Parent.AttractorEnabled)
            {
                realDistanceVector = _parent.Parent.AttractorPosition - _drawRequest.position;
                _deltaPosAttractor = realDistanceVector;
                // get the normal of the vector
                _deltaPosAttractor.Normalize();
                // multiply by the force to get the displacement vector
                _deltaPosAttractor *= _parent.Parent.AttractorForce;                
                float realDistance = realDistanceVector.LengthSquared();
                float newDistance = _deltaPosAttractor.LengthSquared();
                // if it has reached the attractor's point or past it
                if (realDistance <= newDistance)
                {                    
                    _isActive = false;
                    // make sure that the particle will be drawn in the attractor point
                    _deltaPosAttractor = realDistanceVector;
                }
            }
            // update the direction and angle according to the weight
            if (_weight != 0)
            {
                _gravity.Y += _initialWeight / 100.0f;
                _deltaPos += _gravity;
            }
            if (_particleOrientationType == ParticleOrientationType.FollowMovementAngle)
            {
                _drawRequest.rotation = (float)Math.Atan2((double)_deltaPos.Y, (double)_deltaPos.X);
            }
            else
            {
                _drawRequest.rotation += MathHelper.ToRadians(_spin);
            }
            _drawRequest.position += _deltaPos;
            if (_parent.Parent.AttractorEnabled)
            {
                _drawRequest.position += _deltaPosAttractor;               
            }
            // increment the life of the particle
            _currentLife++;
        }

        /// <summary>
        /// Modify the properties of a particles (recycling)
        /// </summary>
        public void ModifiyParticle(ParticleType parent, Material material, int tileDefault, bool keepAspectRatio, 
            ParticleOrientationType particleOrientation, float life, float width, float height, float velocity, 
            float weight, float spin, float motionRandom, float direction, Vector2 position, float rotation,
            float redTint, float greenTint, float blueTint, float opacity, Vector2 pivot, bool isPivotRelative, OverLifeSettings overLifeSettings)
        {
            this._drawRequest.texture = material.Texture;
            this._keepAspectRatio = keepAspectRatio;
            this._particleOrientationType = particleOrientation;
            this._maxLife = life;
            this._currentLife = 0;
            this._currentTile = tileDefault;
            this._animationTotalTimeElapsed = 0;
            this._animationIsStopped = false;
            this._initialWidth = width;
            this._initialHeight = height;
            this._initialVelocity = velocity;
            this._initialWeight = weight;
            this._initialSpin = spin;
            this._initialMotionRandom = motionRandom;
            this._initialDirection = direction;
            _drawRequest.position = position;
            _drawRequest.rotation = MathHelper.ToRadians(rotation);
            this._initialColorFloatValues.X = redTint;
            this._initialColorFloatValues.Y = greenTint;
            this._initialColorFloatValues.Z = blueTint;
            this._initialColorFloatValues.W = opacity;              
            _drawRequest.pivot = pivot;
            _drawRequest.isPivotRelative = isPivotRelative;
            // TO-DO: add pivot support
            this.overLifeSettings = overLifeSettings;
            this._gravity = Vector2.Zero;
            this._parent = parent;
            if (parent.Parent.AttractorEnabled)
            {
                Vector2 distanceVector = _parent.Parent.AttractorPosition - position;
                _initialDistanceFromAttractor = distanceVector.Length();
                if (_initialDistanceFromAttractor == 0.0f)
                {
                    _initialDistanceFromAttractor = 0.00001f;
                }
            }
            else
            {
                _initialDistanceFromAttractor = null;
            }
        }

        #endregion

        #region Constructor

        public Particle()
        {            
            this._isActive = false;
            this._initialColorFloatValues = new Vector4();
            this._drawRequest = default(DrawRequest);           
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of use and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Tyler Forsythe
Software Developer (Senior)
United States United States
Member
No Biography provided

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 8 Aug 2012
Article Copyright 2012 by Tyler Forsythe
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid