//--------------------------------------------------------------------------------------------------------------------------------------------------- // // Copyright (C)2007 DarkWynter Studios. All rights reserved. // //--------------------------------------------------------------------------------------------------------------------------------------------------- // {Contact : darkwynter.com for licensing information //--------------------------------------------------------------------------------------------------------------------------------------------------- namespace DarkWynterEngine.GameObjects { #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Xml; using Physics; using Globals; using ObjectLib; #endregion /// /// Used to pass instance data to shaders through existing gpu pipelines. /// /// Location and orientation variables are stored as follows: /// Fog = GameObject scale /// Binormal = GameObject rotation /// Tangent = GameObject position /// public struct VertexFogBinormalTangent { /// /// scale /// public Vector4 Fog; /// /// rotation /// public Vector4 Binormal; /// /// position /// public Vector4 Tangent; } /// /// Used to pass instance data to shaders through existing gpu pipelines. /// /// World matrix values are stored as follows: /// Fog = World.M11, World.M12, World.M13, World.M14 /// Binormal = World.M21, World.M22, World.M23, World.M24 /// Tangent = World.M31, World.M32, World.M33, World.M34 /// Weight = World.M41, World.M42, World.M43, World.M44 /// public struct VertexFogBinormalTangentWeight { /// /// World.M11, World.M12, World.M13, World.M14 /// public Vector4 Fog; /// /// World.M21, World.M22, World.M23, World.M24 /// public Vector4 Binormal; /// /// World.M31, World.M32, World.M33, World.M34 /// public Vector4 Tangent; /// /// World.M41, World.M42, World.M43, World.M44 /// public Vector4 BlendWeight; } /// /// Methods of GameObject follow the GameFlow pattern to ensure quick and efficient loading and unloading procedures. /// Additional properties are included in GameObject and are used by Renderer and Update to manipulate the GameObject. /// Each object specifies its draw and update procedures internally and is passed to Physics for in game motion and rotation. /// public class GameObject { #region GameObject Interface Properties private Mass _mass; private Model _model; private Matrix _matrix; private XmlNode _xml; private List _textureList = new List(); private string _objectModelName; private bool _isCollectable; private bool _isKey; /// /// Mass object, handles physics of GameObject. /// public Mass mass { get { return _mass; } set { _mass = value; } } /// /// Model used by this GameObject. /// public Model model { get { return _model; } set { _model = value; } } /// /// Location and orientation matrix passed to shaders. /// public Matrix matrix { get { return _matrix; } set { _matrix = value; } } /// /// Xml data associated with this object. /// public XmlNode xml { get { return _xml; } set { _xml = value; } } /// /// Textures associated with this object. /// public List textureList { get { return _textureList; } set { _textureList = value; } } /// /// Text identifier for model name. /// public string objectModelName { get { return _objectModelName; } set { _objectModelName = value; } } /// /// Defines if this object is collectable by players. /// public bool isCollectable { get { return _isCollectable; } set { _isCollectable = value; } } /// /// Defines if this object is a Key object collected to pass the level. /// public bool isKey { get { return _isKey; } set { _isKey = value; } } /// /// Buffer to store vertices /// public VertexBuffer vertexBuffer; /// /// Array to store vertex Position, Normal and Texture information /// public VertexPositionNormalTexture[] vertices; /// /// Vertex Declaration. /// public VertexDeclaration vertexDeclaration; /// /// Used to pass instance data to shader. /// public VertexFogBinormalTangent objectValues; /// /// Tangential component of force calculations. /// public Vector3 tangentialComponent = new Vector3(); /// /// Normal component of force calculations. /// public Vector3 normalComponent = new Vector3(); /// /// Used in collision; surface normal calculation. /// public Vector3 surfaceNormal; /// /// Used in collision; gravity normal calculation. /// public Vector3 gravityNormalComponent; /// /// Used in collision; tangential gravity-force calculation. /// public Vector3 gravityTangentialComponent; /// /// Used in collision; normal velocity-force calculation. /// public Vector3 velocityNormalComponent; /// /// Used in collision; tangential velocity-force calculation. /// public Vector3 velocityTangentialComponent; /// /// Used in collision; normal totalForce calculation. /// public Vector3 totalForceNormalComponent; /// /// Used in collision; tangential totalForce calculation. /// public Vector3 totalForceTangentialComponent; /// /// Used in collision; combined velocity and totalForce normal calculation. /// public Vector3 combinedNormalForce; /// /// Used in collision; combined velocity and totalForce tangential calculation. /// public Vector3 combinedTangentialForce; /// /// Difference between object height and terrain at it's x/z location. /// HeightDifference is positive when object is below the terrain. /// public float heightDifference; /// /// Terrain height at object's x/z location. /// public float localTerrainHeight; /// /// Defines what kind of collision response this GameObject should have. /// public Enums.CollisionResponses collisionWithPlayerResponse = Enums.CollisionResponses.NONE; /// /// Defines the scale of damage or health an object bestows. /// public float collisionMultiplier = 0.001f; #endregion #region Methods /// /// Game object constructor. /// Gets a pre-initialized mass object from the Statics.LevelSettings.darkMatter list. /// public GameObject() { mass = Statics.LevelSettings.darkMatter[Statics.LevelSettings.darkMatterIterator++]; mass.gameObjectPointer = this; } /// /// Load object data from Xml. /// /// The Level xml node corresponding to this object /// ObjectLibrary that this object belongs to. public virtual bool Load(XmlNode node, ObjectLibrary objectLibrary) { // Set up Model Manager bool success = objectLibrary.modelManager.AddNewModel(node.Attributes["type"].Value, node.Attributes["model"].Value, node.Attributes["texture"].Value, node.Attributes["bumpTexture"].Value); // Return unsuccessfully if can't load user supplied XML data if (!success) { return true; } // WE SHOULD RETURN FALSE HERE... OTHERWISE WE'RE SAYING "YES, IT LOADED" WHEN IT DIDN'T // Get the Location Map Texture2D locationMap = Statics.SystemSettings.content.Load(node.Attributes["locationMap"].Value); int length = locationMap.Width * locationMap.Width; Color[] locationColorValues = new Color[length]; locationMap.GetData(locationColorValues); Random rand = new Random(); // Precalculate the vertexmap/locationmap ratio for use in the for loop below float vertexmapToObjectmapRelationship = Statics.TerrainSettings.vertexMapSize / locationMap.Width; // Create a ruler 1 terrain mod long (distance between verticies float terrainModWidth = Statics.TerrainSettings.terrainScaleFactor * vertexmapToObjectmapRelationship; // Get the color value of the node int mapColorR = int.Parse(node.Attributes["red"].Value); int mapColorG = int.Parse(node.Attributes["green"].Value); int mapColorB = int.Parse(node.Attributes["blue"].Value); // For each pixel of the treeMap for (int l = 0; l < length; l++) { // If the color matches create a prop if (locationColorValues[l].R == mapColorR && locationColorValues[l].G == mapColorG && locationColorValues[l].B == mapColorB) { // Get the 2D location from 1D array int h = l % locationMap.Width; int w = (int)l / locationMap.Width; // Start Type properties //gameObject.mass.objectType = objectType; mass.gameObjectPointer = this; objectModelName = node.Attributes["type"].Value; // Get terrain height at position float yPosition = objectLibrary.terrain.GetTerrainHeight(w * vertexmapToObjectmapRelationship, h * vertexmapToObjectmapRelationship); // Set the object world properties Vector3 locpos = new Vector3(w * terrainModWidth, yPosition, h * terrainModWidth); Vector3 refpos = new Vector3(w * terrainModWidth, yPosition, h * terrainModWidth - 1); mass.SetPosition(locpos, refpos); mass.Rotate(0.0f, 0.0f); mass.scale = new Vector3((float.Parse(node.Attributes["maxScale"].Value))); mass.mass = float.Parse(node.Attributes["mass"].Value); mass.isMoving = false; isCollectable = bool.Parse(node.Attributes["isCollectable"].Value); isKey = bool.Parse(node.Attributes["isKey"].Value); if (isKey) { collisionWithPlayerResponse = Enums.CollisionResponses.HEALTHBONUS; } objectValues = new VertexFogBinormalTangent(); objectValues.Fog = new Vector4(mass.scale, 0.0f); // Store the scale objectValues.Binormal = new Vector4(mass.currentRotation.X, mass.currentRotation.Y, mass.currentRotation.Z, mass.currentRotation.W); // Store the quaternion rotation objectValues.Tangent = new Vector4(mass.currentPosition, 0.0f); } } return true; } /// /// Update this object /// /// ObjectLibrary public virtual void Update(ref ObjectLibrary objectLibrary) { mass.UpdatePosition(); mass.boundingVolume.UpdateSphere(mass.currentPosition); objectValues = new VertexFogBinormalTangent(); objectValues.Fog = new Vector4(mass.scale, 0.0f); // Store the scale objectValues.Binormal = new Vector4(mass.currentRotation.X, mass.currentRotation.Y, mass.currentRotation.Z, mass.currentRotation.W); // Store the quaternion rotation objectValues.Tangent = new Vector4(mass.currentPosition, 0.0f); // Store the translation } /// /// Default behavior applies force based physics for rebound. /// Override to supplement behavior. /// Return true if collidedObject is absorbed or destroyed, else false. /// /// GameObject that collided with this GameObject. /// The force applied by the colliding GameObject to this GameObject /// ObjectLibrary that this object belongs to. /// True = this object has to be recycled public virtual bool ObjectCollisionResponse(GameObject collidedObject, Vector3 resultantForce, ObjectLibrary objectLibrary) { mass.numberOfCollision++; mass.isMoving = true; return false; } /// /// Default behavior applies force based physics for rebound. /// Override to supplement or replace behavior. /// /// A copy of the terrain, provided for overrides public virtual void TerrainCollisionResponse(Terrain terrain) { CalculateVectorComponents(surfaceNormal, Statics.GameSettings.accelDueToGravity); gravityNormalComponent = normalComponent + Vector3.Zero; gravityTangentialComponent = tangentialComponent + Vector3.Zero; // Calculate the components of velocity with reference to the surface normal CalculateVectorComponents(surfaceNormal, mass.velocity); velocityNormalComponent = normalComponent + Vector3.Zero; velocityTangentialComponent = tangentialComponent + Vector3.Zero; // Calculate the components of totalForce with reference to the surface normal CalculateVectorComponents(surfaceNormal, mass.totalForce); totalForceNormalComponent = normalComponent + Vector3.Zero; totalForceTangentialComponent = tangentialComponent + Vector3.Zero; // Calculate normal and tangential forces with reference to the their components combinedNormalForce = (mass.mass * velocityNormalComponent / Statics.SystemSettings.dt) + totalForceNormalComponent; combinedTangentialForce = (mass.mass * velocityTangentialComponent / Statics.SystemSettings.dt) + totalForceTangentialComponent + (mass.mass * gravityTangentialComponent); mass.numberOfCollision++; } /// /// Calculate components of collision. /// /// Normal of surface that is being collided against. /// Velocity of this GameObject. private void CalculateVectorComponents(Vector3 surfaceNormal, Vector3 objectVelocity) { float vectorToNormalAngle, vectorToSurfaceAngle; float normalComponentOfVector; if (objectVelocity.Length() >= 0.01f || !float.IsInfinity(objectVelocity.Length())) { normalComponent = Vector3.Dot(objectVelocity, surfaceNormal) * surfaceNormal; tangentialComponent = objectVelocity - normalComponent; /* // Based on vector dot product to get the angle between the two vectors vectorToNormalAngle = (float)Math.Acos(Vector3.Dot(surfaceNormal, objectVelocity) / objectVelocity.Length()); // Stupid check required due to XNA's inaccuracy if (float.IsNaN(vectorToNormalAngle)) { vectorToNormalAngle = 0.0f; } // The angle between the ground and the vector is pi/2 - previous angle vectorToSurfaceAngle = (float)Math.PI / 2.0f - vectorToNormalAngle; // Scalar component of the vector in the the normal direction normalComponentOfVector = objectVelocity.Length() * (float)Math.Sin(vectorToSurfaceAngle); if (float.IsNaN(normalComponentOfVector)) { //if objectVelocity.Length gives an invalid value normalComponentOfVector = 0.0f; } // Above scalar component multiplied by the surface normal gives us the normal component of the vector normalComponent = normalComponentOfVector * surfaceNormal; // The vector plus the negative normalComponent will give us the tangential component in the reverse direction tangentialComponent = -(objectVelocity + (-normalComponent)); */ } else { // Object velocity is equal to zero so set both components to zero normalComponent = Vector3.Zero; tangentialComponent = Vector3.Zero; } } /// /// Sets the heightDifference parameter for this GameObject. /// HeightDifference is positive when object is below the terrain. /// /// Terrain object to determine height difference with. public void GetObjectHeight(Terrain terrain) { // Used to calculate the average terrain height around the player localTerrainHeight = 0; // Error check mass.EnforceBoundary(); // Get X and Z positions int posX = (int)mass.currentPosition.X; int posZ = (int)mass.currentPosition.Z; // Add up all neighboring terrain points localTerrainHeight = terrain.GetTerrainHeight(posX / Statics.TerrainSettings.terrainScaleFactor, posZ / Statics.TerrainSettings.terrainScaleFactor); surfaceNormal = terrain.GetTerrainNormal(posX / Statics.TerrainSettings.terrainScaleFactor, posZ / Statics.TerrainSettings.terrainScaleFactor, mass.velocity); // Average all the neighboring terrain points float newHeight = localTerrainHeight + (mass.objectHeight / 2); // Get difference between average and player heightDifference = newHeight - mass.currentPosition.Y; } /// /// Draws a stand alone model with no instancing or triangle stripping. /// Uses model stored in ModelManager and referenced by objectModelName. /// /// ModelManager containing this object's model file. /// Shader technique to draw model with. public virtual void Draw(ModelManager modelManager, string technique) { // Calculate ObjectSpace(Rotation) and WorldSpace(Translation) Transformation Matrix matrix = Matrix.CreateScale(mass.scale) * Matrix.CreateFromQuaternion(mass.currentRotation) * Matrix.CreateTranslation(mass.currentPosition); // Matricies ShaderParameters.World.SetValue(matrix); // Lighting ShaderParameters.shininess.SetValue(7000.0f); // Load resources for that catagory ModelInfo modelInfo = modelManager.GetModelInfo(objectModelName); ShaderParameters.modelTexture1.SetValue(modelInfo.currentTexture); ShaderParameters.bumpTexture1.SetValue(modelInfo.bumpTexture); foreach (ModelMesh mesh in modelInfo.model.Meshes) { Statics.SystemSettings.graphics.GraphicsDevice.Indices = mesh.IndexBuffer; foreach (ModelMeshPart part in mesh.MeshParts) { Statics.SystemSettings.graphics.GraphicsDevice.VertexDeclaration = part.VertexDeclaration; Statics.SystemSettings.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride); part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; part.Effect.Begin(); for (int k = 0; k < part.Effect.CurrentTechnique.Passes.Count; k++) { EffectPass pass = part.Effect.CurrentTechnique.Passes[k]; pass.Begin(); Statics.SystemSettings.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount); pass.End(); } part.Effect.End(); } } } /// /// Draws a stand alone model with no instancing or triangle stripping. /// Uses model passed in from invoking function call. /// /// Model to draw. /// Shader technique to draw model with. public void DrawBasic(Model model, string technique) { // Calculate ObjectSpace(Rotation) and WorldSpace(Translation) Transformation Matrix matrix = Matrix.CreateScale(mass.scale) * Matrix.CreateFromQuaternion(mass.currentRotation) * Matrix.CreateTranslation(mass.currentPosition); // Matricies ShaderParameters.World.SetValue(matrix); // Lighting ShaderParameters.shininess.SetValue(7000.0f); foreach (ModelMesh mesh in model.Meshes) { Statics.SystemSettings.graphics.GraphicsDevice.Indices = mesh.IndexBuffer; foreach (ModelMeshPart part in mesh.MeshParts) { Statics.SystemSettings.graphics.GraphicsDevice.VertexDeclaration = part.VertexDeclaration; Statics.SystemSettings.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride); part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; part.Effect.Begin(); for (int k = 0; k < part.Effect.CurrentTechnique.Passes.Count; k++) { EffectPass pass = part.Effect.CurrentTechnique.Passes[k]; pass.Begin(); Statics.SystemSettings.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount); pass.End(); } part.Effect.End(); } } } /// /// Draws a stand alone model that uses the triangle-strip content processor for loading. /// /// Model to draw. /// Shader technique to draw model with. public void DrawTriStrips(Model model, string technique) { foreach (ModelMesh mesh in model.Meshes) { Statics.SystemSettings.graphics.GraphicsDevice.Indices = mesh.IndexBuffer; foreach (ModelMeshPart part in mesh.MeshParts) { part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; Statics.SystemSettings.graphics.GraphicsDevice.VertexDeclaration = part.VertexDeclaration; Statics.SystemSettings.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride); int numPrimitives; if (mesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(ushort); } else { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(int); } numPrimitives -= 2; part.Effect.Begin(); foreach (EffectPass pass in part.Effect.CurrentTechnique.Passes) { pass.Begin(); Statics.SystemSettings.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, part.BaseVertex, 0, part.NumVertices, part.StartIndex, numPrimitives); pass.End(); } part.Effect.End(); } } } /// /// Draws an instanced model that uses the standard content processor. /// /// Model to draw. /// Shader technique to draw model with. /// The number of bits between verticies. Use "vertexDeclaration.GetVertexStrideSize()" to find this. public void DrawInstancedBasic(Model model, string technique, int vertexStride) { foreach (ModelMesh mesh in model.Meshes) { // Set the index buffer Statics.SystemSettings.graphics.GraphicsDevice.Indices = mesh.IndexBuffer; // Tell the GPU how many times to run through the vertex data and set the stream. Statics.SystemSettings.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, 0, vertexStride); foreach (ModelMeshPart part in mesh.MeshParts) { part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; part.Effect.Begin(); for (int k = 0; k < part.Effect.CurrentTechnique.Passes.Count; k++) { EffectPass pass = part.Effect.CurrentTechnique.Passes[k]; pass.Begin(); Statics.SystemSettings.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount); pass.End(); } part.Effect.End(); } } } /// /// Draws an instanced model that uses the triangle-strip content processor. /// /// Model to draw. /// Shader technique to draw model with. /// The number of bits between verticies. Use "vertexDeclaration.GetVertexStrideSize()" to find this. public void DrawInstancedTriStrips(Model model, string technique, int vertexStride) { foreach (ModelMesh mesh in model.Meshes) { // Set the index buffer Statics.SystemSettings.graphics.GraphicsDevice.Indices = mesh.IndexBuffer; int numPrimitives; if (mesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(ushort); } else { numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(int); } numPrimitives -= 2; // Tell the GPU how many times to run through the vertex data and set the stream. Statics.SystemSettings.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, 0, vertexStride); foreach (ModelMeshPart part in mesh.MeshParts) { part.Effect.CurrentTechnique = part.Effect.Techniques[technique]; part.Effect.Begin(); for (int k = 0; k < part.Effect.CurrentTechnique.Passes.Count; k++) { EffectPass pass = part.Effect.CurrentTechnique.Passes[k]; pass.Begin(); Statics.SystemSettings.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, part.BaseVertex, 0, part.NumVertices, part.StartIndex, numPrimitives); pass.End(); } part.Effect.End(); } } } /// /// Called after Draw method. /// Allows GameObjects to add Billboards to be drawn. /// List is cleared each pass, or frame. /// /// ObjectLibrary that this object belongs to. /// Dynamic Billboard list. public virtual void Draw_Billboards(ObjectLibrary objectLibrary, BillboardList gameObjectBillboards) { //do stuff later } #endregion } } #region Legacy Code - Heat dissipation and State Change /* Legacy Code // Test Function to update energy distribution public void UpdateHeatDissipation(ObjectLibrary objectLibrary, float dt) { List newParticlesToAddToList = new List(); foreach (Particle particle in particles) { particle.mass.UpdatePosition(dt); } // Iterate through dynamic particles for (int i = 0; i < particles.Count; i++) { Particle particle = particles[i]; // List to store the pointers to neighboring particles List neighboringParticleList = new List(); // Used to calculate the average value of all neighboring particles float averageEnergy = particle.mass.energy; // Get the list of neighboring particle pointers neighboringParticleList = GetSurroundingParticles(particle.mass.currentPosition, objectLibrary); // Calculate their total energy foreach (Particle neigborParticle in neighboringParticleList) { averageEnergy += neigborParticle.mass.energy; } // Find the average averageEnergy /= neighboringParticleList.Count + 1; // If the average value is not equal to zero then we have some heat dissipation to do if (averageEnergy != particle.mass.energy && neighboringParticleList.Count != 0) { // Iterate through neighboring particles foreach (Particle neigborParticle in neighboringParticleList) { // If their value is not equal to the average set it and set the flag if (neigborParticle.mass.energy != averageEnergy) { neigborParticle.mass.SetEnergy(averageEnergy); if (!neigborParticle.mass.isChanging) { neigborParticle.mass.isChanging = true; newParticlesToAddToList.Add(neigborParticle); } } } // Set the starting particle's energy to average value as well particle.mass.SetEnergy(averageEnergy); } else if (particle.mass.energy != 0.0f) { DissipateHeat(particle); } else { // The particle and it's neighbors are in equilibrium so remove it from the list particle.mass.isChanging = false; if (!particle.mass.isMoving) { // Particle needs to be put back into the static list MoveParticle(particles, particles, particle); } else { particles.Remove(particle); } i--; } } foreach (Particle newParticle in newParticlesToAddToList) { if (particles.Contains(newParticle)) { continue; } if (newParticle.mass.isMoving) { CopyParticle(particles, particles, newParticle); } else { MoveParticle(particles, particles, newParticle); } } } public void StateChange(ObjectLibrary objectLibrary, float elementUse, GameObject gameObject) { Particle particle = (Particle)gameObject; gameObject.mass.ChangeEnergy(elementUse / 2.0f); if (gameObject.mass.isChanging) { return; } else if (gameObject.mass.isMoving) { // Particle is in the dynamic list so we need to copy it to the Thermal list switch (particle.particleType) { case Particle.ParticleType.Air: { objectLibrary.particleManager.air.CopyParticle(objectLibrary.particleManager.air.dynamicParticles, objectLibrary.particleManager.air.thermalParticles, particle); break; } case Particle.ParticleType.Water: { objectLibrary.particleManager.water.CopyParticle(objectLibrary.particleManager.water.dynamicParticles, objectLibrary.particleManager.water.thermalParticles, particle); break; } case Particle.ParticleType.Earth: { objectLibrary.particleManager.earth.CopyParticle(objectLibrary.particleManager.earth.dynamicParticles, objectLibrary.particleManager.earth.thermalParticles, particle); break; } } gameObject.mass.isChanging = true; } else { // It's in the static list, so move the particle to the dynamic list switch (particle.particleType) { case Particle.ParticleType.Air: { objectLibrary.particleManager.air.MoveParticle(objectLibrary.particleManager.air.staticParticles, objectLibrary.particleManager.air.thermalParticles, particle); break; } case Particle.ParticleType.Water: { objectLibrary.particleManager.water.MoveParticle(objectLibrary.particleManager.water.staticParticles, objectLibrary.particleManager.water.thermalParticles, particle); break; } case Particle.ParticleType.Earth: { objectLibrary.particleManager.earth.MoveParticle(objectLibrary.particleManager.earth.staticParticles, objectLibrary.particleManager.earth.thermalParticles, particle); break; } } gameObject.mass.isChanging = true; } } */ #endregion