Particle additions:

- Support for subemitters (= particles can emit particles).
- Option to set a different velocity change value for particles in water (can be used to simulate buouancy).
- Fixed drag vector & timer not being reset when initializing a particle.
This commit is contained in:
Joonas Rikkonen
2017-12-29 20:58:29 +02:00
parent a8749f3d87
commit 454e0def3b
5 changed files with 98 additions and 22 deletions
@@ -293,6 +293,7 @@ namespace Barotrauma
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
ParticleManager = new ParticleManager("Content/Particles/ParticlePrefabs.xml", GameScreen.Cam); ParticleManager = new ParticleManager("Content/Particles/ParticlePrefabs.xml", GameScreen.Cam);
ParticleManager.LoadPrefabs();
DecalManager = new DecalManager("Content/Particles/DecalPrefabs.xml"); DecalManager = new DecalManager("Content/Particles/DecalPrefabs.xml");
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -38,6 +38,7 @@ namespace Barotrauma.Particles
private float lifeTime; private float lifeTime;
private Vector2 velocityChange; private Vector2 velocityChange;
private Vector2 velocityChangeWater;
private Vector2 drawPosition; private Vector2 drawPosition;
private float drawRotation; private float drawRotation;
@@ -46,6 +47,8 @@ namespace Barotrauma.Particles
private List<Gap> hullGaps; private List<Gap> hullGaps;
private List<ParticleEmitter> subEmitters = new List<ParticleEmitter>();
private float animState; private float animState;
private int animFrame; private int animFrame;
@@ -71,6 +74,12 @@ namespace Barotrauma.Particles
set { velocityChange = value; } set { velocityChange = value; }
} }
public Vector2 VelocityChangeWater
{
get { return velocityChangeWater; }
set { velocityChangeWater = value; }
}
public Vector2 Velocity public Vector2 Velocity
{ {
get { return velocity; } get { return velocity; }
@@ -90,6 +99,8 @@ namespace Barotrauma.Particles
animState = 0; animState = 0;
animFrame = 0; animFrame = 0;
dragWait = 0;
dragVec = Vector2.Zero;
currentHull = Hull.FindHull(position, hullGuess); currentHull = Hull.FindHull(position, hullGuess);
@@ -121,9 +132,16 @@ namespace Barotrauma.Particles
alpha = prefab.StartAlpha; alpha = prefab.StartAlpha;
velocityChange = prefab.VelocityChangeDisplay; velocityChange = prefab.VelocityChangeDisplay;
velocityChangeWater = prefab.VelocityChangeWaterDisplay;
OnChangeHull = null; OnChangeHull = null;
subEmitters.Clear();
foreach (ParticleEmitterPrefab emitterPrefab in prefab.SubEmitters)
{
subEmitters.Add(new ParticleEmitter(emitterPrefab));
}
if (prefab.DeleteOnCollision || prefab.CollidesWithWalls) if (prefab.DeleteOnCollision || prefab.CollidesWithWalls)
{ {
hullGaps = currentHull == null ? new List<Gap>() : currentHull.ConnectedGaps; hullGaps = currentHull == null ? new List<Gap>() : currentHull.ConnectedGaps;
@@ -158,19 +176,26 @@ namespace Barotrauma.Particles
rotation += angularVelocity * deltaTime; rotation += angularVelocity * deltaTime;
} }
if (prefab.WaterDrag > 0.0f && bool inWater = (currentHull == null || (currentHull.Submarine != null && position.Y - currentHull.Submarine.DrawPosition.Y < currentHull.Surface));
(currentHull == null || (currentHull.Submarine != null && position.Y - currentHull.Submarine.DrawPosition.Y < currentHull.Surface))) if (inWater)
{ {
ApplyDrag(prefab.WaterDrag, deltaTime); velocity.X += velocityChangeWater.X * deltaTime;
velocity.Y += velocityChangeWater.Y * deltaTime;
if (prefab.WaterDrag > 0.0f)
{
ApplyDrag(prefab.WaterDrag, deltaTime);
}
} }
else if (prefab.Drag > 0.0f) else
{ {
ApplyDrag(prefab.Drag, deltaTime); velocity.X += velocityChange.X * deltaTime;
velocity.Y += velocityChange.Y * deltaTime;
if (prefab.Drag > 0.0f)
{
ApplyDrag(prefab.Drag, deltaTime);
}
} }
velocity.X += velocityChange.X * deltaTime;
velocity.Y += velocityChange.Y * deltaTime;
size.X += sizeChange.X * deltaTime; size.X += sizeChange.X * deltaTime;
size.Y += sizeChange.Y * deltaTime; size.Y += sizeChange.Y * deltaTime;
@@ -191,6 +216,11 @@ namespace Barotrauma.Particles
lifeTime -= deltaTime; lifeTime -= deltaTime;
if (lifeTime <= 0.0f || alpha <= 0.0f || size.X <= 0.0f || size.Y <= 0.0f) return false; if (lifeTime <= 0.0f || alpha <= 0.0f || size.X <= 0.0f || size.Y <= 0.0f) return false;
foreach (ParticleEmitter emitter in subEmitters)
{
emitter.Emit(deltaTime, position, currentHull);
}
if (!prefab.DeleteOnCollision && !prefab.CollidesWithWalls) return true; if (!prefab.DeleteOnCollision && !prefab.CollidesWithWalls) return true;
if (currentHull == null) if (currentHull == null)
@@ -201,7 +231,6 @@ namespace Barotrauma.Particles
if (prefab.DeleteOnCollision) return false; if (prefab.DeleteOnCollision) return false;
OnWallCollisionOutside(collidedHull); OnWallCollisionOutside(collidedHull);
} }
} }
else else
{ {
@@ -281,8 +310,11 @@ namespace Barotrauma.Particles
} }
if (Math.Abs(velocity.X) < 0.0001f && Math.Abs(velocity.Y) < 0.0001f) return; if (Math.Abs(velocity.X) < 0.0001f && Math.Abs(velocity.Y) < 0.0001f) return;
//TODO: some better way to handle particle drag
//this doesn't work that well because the drag vector is only updated every 0.5 seconds, allowing the particle to accelerate way more than it should
//(e.g. a falling particle can freely accelerate for 0.5 seconds before the drag takes effect)
dragWait--; dragWait--;
if (dragWait<=0) if (dragWait <= 0)
{ {
dragWait = 30; dragWait = 30;
@@ -32,13 +32,11 @@ namespace Barotrauma.Particles
this.cam = cam; this.cam = cam;
particles = new Particle[MaxParticles]; particles = new Particle[MaxParticles];
LoadPrefabs(configFile);
} }
public void LoadPrefabs(string file) public void LoadPrefabs()
{ {
XDocument doc = XMLExtensions.TryLoadXml(file); XDocument doc = XMLExtensions.TryLoadXml(ConfigFile);
if (doc == null || doc.Root == null) return; if (doc == null || doc.Root == null) return;
prefabs = new Dictionary<string, ParticlePrefab>(); prefabs = new Dictionary<string, ParticlePrefab>();
@@ -47,7 +45,7 @@ namespace Barotrauma.Particles
{ {
if (prefabs.ContainsKey(element.Name.ToString())) if (prefabs.ContainsKey(element.Name.ToString()))
{ {
DebugConsole.ThrowError("Error in " + file + "! Each particle prefab must have a unique name."); DebugConsole.ThrowError("Error in " + ConfigFile + "! Each particle prefab must have a unique name.");
continue; continue;
} }
prefabs.Add(element.Name.ToString(), new ParticlePrefab(element)); prefabs.Add(element.Name.ToString(), new ParticlePrefab(element));
@@ -101,6 +101,20 @@ namespace Barotrauma.Particles
} }
} }
private Vector2 velocityChangeWater;
public Vector2 VelocityChangeWaterDisplay { get; private set; }
[Editable(ToolTip = "How much the velocity of the particle changes per second when in water."), Serialize("0.0,0.0", false)]
public Vector2 VelocityChangeWater
{
get { return velocityChangeWater; }
private set
{
velocityChangeWater = value;
VelocityChangeWaterDisplay = ConvertUnits.ToDisplayUnits(value);
}
}
[Editable(0.0f, 10000.0f, ToolTip = "Drag applied to the particle when it's moving through water."), Serialize(0.0f, false)] [Editable(0.0f, 10000.0f, ToolTip = "Drag applied to the particle when it's moving through water."), Serialize(0.0f, false)]
public float CollisionRadius { get; private set; } public float CollisionRadius { get; private set; }
@@ -164,6 +178,8 @@ namespace Barotrauma.Particles
//---------------------------------------------------- //----------------------------------------------------
public readonly List<ParticleEmitterPrefab> SubEmitters = new List<ParticleEmitterPrefab>();
public Dictionary<string, SerializableProperty> SerializableProperties public Dictionary<string, SerializableProperty> SerializableProperties
{ {
get; get;
@@ -191,9 +207,20 @@ namespace Barotrauma.Particles
case "animatedsprite": case "animatedsprite":
Sprites.Add(new SpriteSheet(subElement)); Sprites.Add(new SpriteSheet(subElement));
break; break;
case "particleemitter":
case "emitter":
case "subemitter":
SubEmitters.Add(new ParticleEmitterPrefab(subElement));
break;
} }
} }
//if velocity change in water is not given, it defaults to the normal velocity change
if (element.Attribute("velocitychangewater") == null)
{
VelocityChangeWater = VelocityChange;
}
if (element.Attribute("angularvelocity") != null) if (element.Attribute("angularvelocity") != null)
{ {
AngularVelocityMin = element.GetAttributeFloat("angularvelocity", 0.0f); AngularVelocityMin = element.GetAttributeFloat("angularvelocity", 0.0f);
@@ -16,7 +16,9 @@ namespace Barotrauma
{ {
public float EmitTimer; public float EmitTimer;
[Editable(), Serialize("0.0,0.0", false)] public float BurstTimer;
[Editable(), Serialize("0.0,360.0", false)]
public Vector2 AngleRange { get; private set; } public Vector2 AngleRange { get; private set; }
[Editable(), Serialize("0.0,0.0", false)] [Editable(), Serialize("0.0,0.0", false)]
@@ -26,8 +28,12 @@ namespace Barotrauma
public Vector2 ScaleRange { get; private set; } public Vector2 ScaleRange { get; private set; }
[Editable(), Serialize(0, false)] [Editable(), Serialize(0, false)]
public int ParticleAmount { get; private set; } public int ParticleBurstAmount { get; private set; }
[Editable(), Serialize(0.0f, false)]
[Editable(), Serialize(1.0f, false)]
public float ParticleBurstInterval { get; private set; }
[Editable(), Serialize(1.0f, false)]
public float ParticlesPerSecond { get; private set; } public float ParticlesPerSecond { get; private set; }
public string Name public string Name
@@ -46,6 +52,11 @@ namespace Barotrauma
public Emitter() public Emitter()
{ {
ScaleRange = Vector2.One;
AngleRange = new Vector2(0.0f, 360.0f);
ParticleBurstAmount = 1;
ParticleBurstInterval = 1.0f;
SerializableProperties = SerializableProperty.GetProperties(this); SerializableProperties = SerializableProperty.GetProperties(this);
} }
} }
@@ -210,6 +221,8 @@ namespace Barotrauma
if (selectedPrefab != null) if (selectedPrefab != null)
{ {
emitter.EmitTimer += (float)deltaTime; emitter.EmitTimer += (float)deltaTime;
emitter.BurstTimer += (float)deltaTime;
if (emitter.ParticlesPerSecond > 0) if (emitter.ParticlesPerSecond > 0)
{ {
@@ -221,10 +234,15 @@ namespace Barotrauma
} }
} }
for (int i = 0; i < emitter.ParticleAmount; i++) if (emitter.BurstTimer > emitter.ParticleBurstInterval)
{ {
Emit(Vector2.Zero); for (int i = 0; i < emitter.ParticleBurstAmount; i++)
{
Emit(Vector2.Zero);
}
emitter.BurstTimer = 0.0f;
} }
} }
GameMain.ParticleManager.Update((float)deltaTime); GameMain.ParticleManager.Update((float)deltaTime);