- The emission rate of ParticleEmitters can be set as particles per second (instead of particles per frame).

- Particles that are outside the sub aren't visible inside hulls even if they overlap with the sub.
- ParticleManager takes the movement of the particles into account when determining which particles to cull. For example, a particle that will move upwards can be emitted even if it's below the camera view.
This commit is contained in:
Joonas Rikkonen
2017-08-20 19:05:25 +03:00
parent 3c0f099448
commit 4a460ff150
9 changed files with 94 additions and 38 deletions
@@ -8,7 +8,7 @@ namespace Barotrauma
{ {
private Sound sound; private Sound sound;
private ParticleEmitterPrefab particleEmitterPrefab; private ParticleEmitter particleEmitterPrefab;
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
@@ -23,18 +23,18 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "particleemitter": case "particleemitter":
particleEmitterPrefab = new ParticleEmitterPrefab(subElement); particleEmitterPrefab = new ParticleEmitter(subElement);
break; break;
} }
} }
} }
partial void DamageParticles(Vector2 worldPosition) partial void DamageParticles(float deltaTime, Vector2 worldPosition)
{ {
if (particleEmitterPrefab != null) if (particleEmitterPrefab != null)
{ {
particleEmitterPrefab.Emit(worldPosition); particleEmitterPrefab.Emit(deltaTime, worldPosition);
} }
if (sound != null) if (sound != null)
@@ -159,7 +159,7 @@ namespace Barotrauma.Lights
lightEffect.World = Matrix.CreateTranslation(offset) * transform; lightEffect.World = Matrix.CreateTranslation(offset) * transform;
GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.Additive); GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
if (Character.Controlled != null) if (Character.Controlled != null)
{ {
@@ -74,6 +74,11 @@ namespace Barotrauma.Particles
set { velocity = value; } set { velocity = value; }
} }
public Hull CurrentHull
{
get { return currentHull; }
}
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null) public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null)
{ {
this.prefab = prefab; this.prefab = prefab;
@@ -4,7 +4,7 @@ using System.Xml.Linq;
namespace Barotrauma.Particles namespace Barotrauma.Particles
{ {
class ParticleEmitterPrefab class ParticleEmitter
{ {
public readonly string Name; public readonly string Name;
@@ -16,9 +16,13 @@ namespace Barotrauma.Particles
public readonly float ScaleMin, ScaleMax; public readonly float ScaleMin, ScaleMax;
public readonly float ParticleAmount; public readonly int ParticleAmount;
public ParticleEmitterPrefab(XElement element) public readonly float ParticlesPerSecond;
private float emitTimer;
public ParticleEmitter(XElement element)
{ {
Name = element.Name.ToString(); Name = element.Name.ToString();
@@ -60,12 +64,31 @@ namespace Barotrauma.Particles
VelocityMax = VelocityMin; VelocityMax = VelocityMin;
} }
ParticleAmount = ToolBox.GetAttributeInt(element, "particleamount", 1); ParticlesPerSecond = ToolBox.GetAttributeInt(element, "particlespersecond", 0);
ParticleAmount = ToolBox.GetAttributeInt(element, "particleamount", 0);
} }
public void Emit(Vector2 position, Hull hullGuess = null) public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null)
{ {
emitTimer += deltaTime;
if (ParticlesPerSecond > 0)
{
float emitInterval = 1.0f / ParticlesPerSecond;
while (emitTimer > emitInterval)
{
Emit(position, hullGuess);
emitTimer -= emitInterval;
}
}
for (int i = 0; i<ParticleAmount; i++) for (int i = 0; i<ParticleAmount; i++)
{
Emit(position, hullGuess);
}
}
private void Emit(Vector2 position, Hull hullGuess = null)
{ {
float angle = Rand.Range(AngleMin, AngleMax); float angle = Rand.Range(AngleMin, AngleMax);
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Rand.Range(VelocityMin, VelocityMax); Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Rand.Range(VelocityMin, VelocityMax);
@@ -79,4 +102,3 @@ namespace Barotrauma.Particles
} }
} }
} }
}
@@ -66,11 +66,19 @@ namespace Barotrauma.Particles
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation = 0.0f, Hull hullGuess = null) public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation = 0.0f, Hull hullGuess = null)
{ {
if (!Submarine.RectContains(MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist), position)) return null;
//if (!cam.WorldView.Contains(position)) return null;
if (particleCount >= MaxParticles) return null; if (particleCount >= MaxParticles) return null;
//endPos = x + vt + 1/2 * at^2
Vector2 particleEndPos = position + speed * prefab.LifeTime + 0.5f * prefab.VelocityChange * prefab.LifeTime * prefab.LifeTime;
Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) return null;
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) return null;
if (particles[particleCount] == null) particles[particleCount] = new Particle(); if (particles[particleCount] == null) particles[particleCount] = new Particle();
particles[particleCount].Init(prefab, position, speed, rotation, hullGuess); particles[particleCount].Init(prefab, position, speed, rotation, hullGuess);
@@ -78,7 +86,6 @@ namespace Barotrauma.Particles
particleCount++; particleCount++;
return particles[particleCount - 1]; return particles[particleCount - 1];
} }
public ParticlePrefab FindPrefab(string prefabName) public ParticlePrefab FindPrefab(string prefabName)
@@ -131,7 +138,7 @@ namespace Barotrauma.Particles
} }
} }
public void Draw(SpriteBatch spriteBatch, bool inWater, ParticleBlendState blendState) public void Draw(SpriteBatch spriteBatch, bool inWater, bool? inSub, ParticleBlendState blendState)
{ {
ParticlePrefab.DrawTargetType drawTarget = inWater ? ParticlePrefab.DrawTargetType.Water : ParticlePrefab.DrawTargetType.Air; ParticlePrefab.DrawTargetType drawTarget = inWater ? ParticlePrefab.DrawTargetType.Water : ParticlePrefab.DrawTargetType.Air;
@@ -139,6 +146,7 @@ namespace Barotrauma.Particles
{ {
if (particles[i].BlendState != blendState) continue; if (particles[i].BlendState != blendState) continue;
if (!particles[i].DrawTarget.HasFlag(drawTarget)) continue; if (!particles[i].DrawTarget.HasFlag(drawTarget)) continue;
if (inSub.HasValue && (particles[i].CurrentHull == null) == inSub.Value) continue;
particles[i].Draw(spriteBatch); particles[i].Draw(spriteBatch);
} }
@@ -134,6 +134,27 @@ namespace Barotrauma
Level.Loaded.DrawBack(graphics, spriteBatch, cam, BackgroundCreatureManager); Level.Loaded.DrawBack(graphics, spriteBatch, cam, BackgroundCreatureManager);
} }
#if LINUX
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.NonPremultiplied,
null, DepthStencilState.DepthRead, null, null,
cam.Transform);
#else
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.AlphaBlend,
null, DepthStencilState.DepthRead, null, null,
cam.Transform);
#endif
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Additive,
null, DepthStencilState.Default, null, null,
cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.Additive);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront, spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend, BlendState.AlphaBlend,
null, null, null, null, null, null, null, null,
@@ -181,14 +202,14 @@ namespace Barotrauma
null, DepthStencilState.DepthRead, null, null, null, DepthStencilState.DepthRead, null, null,
cam.Transform); cam.Transform);
#endif #endif
GameMain.ParticleManager.Draw(spriteBatch, true, Particles.ParticleBlendState.AlphaBlend); GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End(); spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Additive, BlendState.Additive,
null, DepthStencilState.Default, null, null, null, DepthStencilState.Default, null, null,
cam.Transform); cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, Particles.ParticleBlendState.Additive); GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.Additive);
spriteBatch.End(); spriteBatch.End();
//---------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------
@@ -211,14 +232,14 @@ namespace Barotrauma
cam.Transform); cam.Transform);
#endif #endif
GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.AlphaBlend); GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End(); spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.Additive, BlendState.Additive,
null, DepthStencilState.DepthRead, null, null, null, DepthStencilState.DepthRead, null, null,
cam.Transform); cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.Additive); GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
spriteBatch.End(); spriteBatch.End();
if (Character.Controlled != null && GameMain.LightManager.LosEnabled) if (Character.Controlled != null && GameMain.LightManager.LosEnabled)
@@ -270,8 +270,8 @@
<StatusEffect type="OnUse" target="This" IsOn="true"/> <StatusEffect type="OnUse" target="This" IsOn="true"/>
<StatusEffect type="OnActive" target="This" Condition="-1.0"> <StatusEffect type="OnActive" target="This" Condition="-1.0">
<ParticleEmitter particle="flare"/> <ParticleEmitter particle="flare" particlespersecond="30"/>
<ParticleEmitter particle="bubbles"/> <ParticleEmitter particle="bubbles" particlespersecond="30"/>
</StatusEffect> </StatusEffect>
<StatusEffect type="OnBroken" target="This" IsOn="false"/> <StatusEffect type="OnBroken" target="This" IsOn="false"/>
@@ -143,7 +143,7 @@ namespace Barotrauma
public AttackResult DoDamage(IDamageable attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true) public AttackResult DoDamage(IDamageable attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true)
{ {
DamageParticles(worldPosition); DamageParticles(deltaTime, worldPosition);
var attackResult = target.AddDamage(attacker, worldPosition, this, deltaTime, playSound); var attackResult = target.AddDamage(attacker, worldPosition, this, deltaTime, playSound);
@@ -168,6 +168,6 @@ namespace Barotrauma
return attackResult; return attackResult;
} }
partial void DamageParticles(Vector2 worldPosition); partial void DamageParticles(float deltaTime, Vector2 worldPosition);
} }
} }
@@ -23,7 +23,7 @@ namespace Barotrauma
private List<RelatedItem> requiredItems; private List<RelatedItem> requiredItems;
#if CLIENT #if CLIENT
private List<ParticleEmitterPrefab> particleEmitters; private List<ParticleEmitter> particleEmitters;
private Sound sound; private Sound sound;
#endif #endif
@@ -77,7 +77,7 @@ namespace Barotrauma
requiredItems = new List<RelatedItem>(); requiredItems = new List<RelatedItem>();
#if CLIENT #if CLIENT
particleEmitters = new List<ParticleEmitterPrefab>(); particleEmitters = new List<ParticleEmitter>();
#endif #endif
IEnumerable<XAttribute> attributes = element.Attributes(); IEnumerable<XAttribute> attributes = element.Attributes();
@@ -179,7 +179,7 @@ namespace Barotrauma
break; break;
#if CLIENT #if CLIENT
case "particleemitter": case "particleemitter":
particleEmitters.Add(new ParticleEmitterPrefab(subElement)); particleEmitters.Add(new ParticleEmitter(subElement));
break; break;
#endif #endif
} }
@@ -280,9 +280,9 @@ namespace Barotrauma
} }
#if CLIENT #if CLIENT
foreach (ParticleEmitterPrefab emitter in particleEmitters) foreach (ParticleEmitter emitter in particleEmitters)
{ {
emitter.Emit(entity.WorldPosition, hull); emitter.Emit(deltaTime, entity.WorldPosition, hull);
} }
#endif #endif
} }