Refactor animation classes.

Specify pause function in constructors of Animation if required, and remove the unused pause function from AnimationWithOffset.

Cleanup Animation.cs and reduce code duplication.
This commit is contained in:
RoosterDragon
2015-10-14 22:33:14 +01:00
parent af4667fc68
commit 082ea6ba73
15 changed files with 67 additions and 71 deletions

View File

@@ -17,36 +17,35 @@ namespace OpenRA.Graphics
{ {
public class Animation public class Animation
{ {
readonly int defaultTick = 40; // 25 fps == 40 ms
public ISpriteSequence CurrentSequence { get; private set; } public ISpriteSequence CurrentSequence { get; private set; }
public bool IsDecoration = false; public string Name { get; private set; }
public Func<bool> Paused; public bool IsDecoration { get; set; }
readonly Func<int> facingFunc;
int frame = 0;
bool backwards = false;
string name;
bool tickAlways;
public string Name { get { return name; } }
readonly SequenceProvider sequenceProvider; readonly SequenceProvider sequenceProvider;
readonly Func<int> facingFunc;
readonly Func<bool> paused;
int frame;
bool backwards;
bool tickAlways;
int timeUntilNextFrame;
Action tickFunc = () => { };
public Animation(World world, string name) public Animation(World world, string name)
: this(world, name, () => 0) { } : this(world, name, () => 0) { }
public Animation(World world, string name, Func<int> facingFunc) public Animation(World world, string name, Func<int> facingFunc)
: this(world.Map.SequenceProvider, name, facingFunc) { } : this(world, name, facingFunc, null) { }
public Animation(SequenceProvider sequenceProvider, string name, Func<int> facingFunc) public Animation(World world, string name, Func<bool> paused)
: this(world, name, () => 0, paused) { }
public Animation(World world, string name, Func<int> facingFunc, Func<bool> paused)
{ {
this.sequenceProvider = sequenceProvider; sequenceProvider = world.Map.SequenceProvider;
this.name = name.ToLowerInvariant(); Name = name.ToLowerInvariant();
this.tickFunc = () => { };
this.facingFunc = facingFunc; this.facingFunc = facingFunc;
this.paused = paused;
} }
public int CurrentFrame { get { return backwards ? CurrentSequence.Start + CurrentSequence.Length - frame - 1 : frame; } } public int CurrentFrame { get { return backwards ? CurrentSequence.Start + CurrentSequence.Length - frame - 1 : frame; } }
@@ -76,12 +75,23 @@ namespace OpenRA.Graphics
PlayThen(sequenceName, null); PlayThen(sequenceName, null);
} }
int CurrentSequenceTickOrDefault()
{
const int DefaultTick = 40; // 25 fps == 40 ms
return CurrentSequence != null ? CurrentSequence.Tick : DefaultTick;
}
void PlaySequence(string sequenceName)
{
CurrentSequence = GetSequence(sequenceName);
timeUntilNextFrame = CurrentSequenceTickOrDefault();
}
public void PlayRepeating(string sequenceName) public void PlayRepeating(string sequenceName)
{ {
backwards = false; backwards = false;
tickAlways = false; tickAlways = false;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName); PlaySequence(sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = 0; frame = 0;
tickFunc = () => tickFunc = () =>
@@ -97,9 +107,8 @@ namespace OpenRA.Graphics
if (!HasSequence(sequenceName)) if (!HasSequence(sequenceName))
return false; return false;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName); CurrentSequence = GetSequence(sequenceName);
var tick = CurrentSequence != null ? CurrentSequence.Tick : defaultTick; timeUntilNextFrame = Math.Min(CurrentSequenceTickOrDefault(), timeUntilNextFrame);
timeUntilNextFrame = Math.Min(tick, timeUntilNextFrame);
frame %= CurrentSequence.Length; frame %= CurrentSequence.Length;
return true; return true;
} }
@@ -108,8 +117,7 @@ namespace OpenRA.Graphics
{ {
backwards = false; backwards = false;
tickAlways = false; tickAlways = false;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName); PlaySequence(sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = 0; frame = 0;
tickFunc = () => tickFunc = () =>
@@ -134,8 +142,7 @@ namespace OpenRA.Graphics
{ {
backwards = false; backwards = false;
tickAlways = true; tickAlways = true;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName); PlaySequence(sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = func(); frame = func();
tickFunc = () => frame = func(); tickFunc = () => frame = func();
@@ -144,8 +151,7 @@ namespace OpenRA.Graphics
public void PlayFetchDirection(string sequenceName, Func<int> direction) public void PlayFetchDirection(string sequenceName, Func<int> direction)
{ {
tickAlways = false; tickAlways = false;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName); PlaySequence(sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = 0; frame = 0;
tickFunc = () => tickFunc = () =>
@@ -159,17 +165,12 @@ namespace OpenRA.Graphics
}; };
} }
int timeUntilNextFrame;
Action tickFunc;
public void Tick() public void Tick()
{ {
if (Paused == null || !Paused()) if (paused == null || !paused())
Tick(40); // tick one frame Tick(40); // tick one frame
} }
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(name, seq); }
public void Tick(int t) public void Tick(int t)
{ {
if (tickAlways) if (tickAlways)
@@ -180,7 +181,7 @@ namespace OpenRA.Graphics
while (timeUntilNextFrame <= 0) while (timeUntilNextFrame <= 0)
{ {
tickFunc(); tickFunc();
timeUntilNextFrame += CurrentSequence != null ? CurrentSequence.Tick : defaultTick; timeUntilNextFrame += CurrentSequenceTickOrDefault();
} }
} }
} }
@@ -189,17 +190,19 @@ namespace OpenRA.Graphics
{ {
newImage = newImage.ToLowerInvariant(); newImage = newImage.ToLowerInvariant();
if (name != newImage) if (Name != newImage)
{ {
name = newImage.ToLowerInvariant(); Name = newImage;
if (!ReplaceAnim(CurrentSequence.Name)) if (!ReplaceAnim(CurrentSequence.Name))
ReplaceAnim(newAnimIfMissing); ReplaceAnim(newAnimIfMissing);
} }
} }
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(Name, seq); }
public ISpriteSequence GetSequence(string sequenceName) public ISpriteSequence GetSequence(string sequenceName)
{ {
return sequenceProvider.GetSequence(name, sequenceName); return sequenceProvider.GetSequence(Name, sequenceName);
} }
public string GetRandomExistingSequence(string[] sequences, MersenneTwister random) public string GetRandomExistingSequence(string[] sequences, MersenneTwister random)

View File

@@ -18,22 +18,20 @@ namespace OpenRA.Graphics
public readonly Animation Animation; public readonly Animation Animation;
public readonly Func<WVec> OffsetFunc; public readonly Func<WVec> OffsetFunc;
public readonly Func<bool> DisableFunc; public readonly Func<bool> DisableFunc;
public readonly Func<bool> Paused;
public readonly Func<WPos, int> ZOffset; public readonly Func<WPos, int> ZOffset;
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable) public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable)
: this(a, offset, disable, () => false, null) { } : this(a, offset, disable, null) { }
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, int zOffset) public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, int zOffset)
: this(a, offset, disable, () => false, _ => zOffset) { } : this(a, offset, disable, _ => zOffset) { }
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, Func<bool> pause, Func<WPos, int> zOffset) public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, Func<WPos, int> zOffset)
{ {
this.Animation = a; Animation = a;
this.Animation.Paused = pause; OffsetFunc = offset;
this.OffsetFunc = offset; DisableFunc = disable;
this.DisableFunc = disable; ZOffset = zOffset;
this.ZOffset = zOffset;
} }
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr, PaletteReference pal, float scale) public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr, PaletteReference pal, float scale)
@@ -47,7 +45,7 @@ namespace OpenRA.Graphics
public static implicit operator AnimationWithOffset(Animation a) public static implicit operator AnimationWithOffset(Animation a)
{ {
return new AnimationWithOffset(a, null, null, null, null); return new AnimationWithOffset(a, null, null, null);
} }
} }
} }

View File

@@ -120,7 +120,7 @@ namespace OpenRA.Mods.Common.Effects
if (!string.IsNullOrEmpty(info.Image)) if (!string.IsNullOrEmpty(info.Image))
{ {
anim = new Animation(world, info.Image, GetEffectiveFacing); anim = new Animation(world, info.Image, new Func<int>(GetEffectiveFacing));
anim.PlayRepeating(info.Sequences.Random(world.SharedRandom)); anim.PlayRepeating(info.Sequences.Random(world.SharedRandom));
} }

View File

@@ -29,8 +29,7 @@ namespace OpenRA.Mods.Common.Effects
this.building = building; this.building = building;
rb = building.Trait<RepairableBuilding>(); rb = building.Trait<RepairableBuilding>();
anim = new Animation(building.World, rb.Info.IndicatorImage); anim = new Animation(building.World, rb.Info.IndicatorImage, () => !rb.RepairActive || rb.IsTraitDisabled);
anim.Paused = () => !rb.RepairActive || rb.IsTraitDisabled;
CycleRepairer(); CycleRepairer();
} }

View File

@@ -165,7 +165,6 @@ namespace OpenRA.Mods.Common.Traits
var muzzleFlash = new AnimationWithOffset(muzzleAnim, var muzzleFlash = new AnimationWithOffset(muzzleAnim,
() => PortOffset(self, port), () => PortOffset(self, port),
() => false, () => false,
() => false,
p => RenderUtils.ZOffsetFromCenter(self, p, 1024)); p => RenderUtils.ZOffsetFromCenter(self, p, 1024));
muzzles.Add(muzzleFlash); muzzles.Add(muzzleFlash);

View File

@@ -79,8 +79,7 @@ namespace OpenRA.Mods.Common.Traits
this.info = info; this.info = info;
this.self = self; this.self = self;
image = info.Image ?? self.Info.Name; image = info.Image ?? self.Info.Name;
anim = new Animation(self.World, image); anim = new Animation(self.World, image, () => self.World.Paused);
anim.Paused = () => self.World.Paused;
anim.PlayRepeating(info.Sequence); anim.PlayRepeating(info.Sequence);
} }

View File

@@ -46,7 +46,6 @@ namespace OpenRA.Mods.Common.Traits
rs.Add(new AnimationWithOffset(anim, rs.Add(new AnimationWithOffset(anim,
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
() => !visible, () => !visible,
() => false,
p => ZOffsetFromCenter(self, p, 0)), info.Palette); p => ZOffsetFromCenter(self, p, 0)), info.Palette);
} }

View File

@@ -69,7 +69,8 @@ namespace OpenRA.Mods.Common.Traits
var body = self.Trait<BodyOrientation>(); var body = self.Trait<BodyOrientation>();
buildComplete = !self.Info.HasTraitInfo<BuildingInfo>(); // always render instantly for units buildComplete = !self.Info.HasTraitInfo<BuildingInfo>(); // always render instantly for units
overlay = new Animation(self.World, rs.GetImage(self)); overlay = new Animation(self.World, rs.GetImage(self),
() => (info.PauseOnLowPower && self.IsDisabled()) || !buildComplete);
if (info.StartSequence != null) if (info.StartSequence != null)
overlay.PlayThen(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.StartSequence), overlay.PlayThen(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.StartSequence),
() => overlay.PlayRepeating(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.Sequence))); () => overlay.PlayRepeating(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.Sequence)));
@@ -79,7 +80,6 @@ namespace OpenRA.Mods.Common.Traits
var anim = new AnimationWithOffset(overlay, var anim = new AnimationWithOffset(overlay,
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
() => IsTraitDisabled || !buildComplete, () => IsTraitDisabled || !buildComplete,
() => (info.PauseOnLowPower && self.IsDisabled()) || !buildComplete,
p => RenderUtils.ZOffsetFromCenter(self, p, 1)); p => RenderUtils.ZOffsetFromCenter(self, p, 1));
rs.Add(anim, info.Palette, info.IsPlayerPalette); rs.Add(anim, info.Palette, info.IsPlayerPalette);

View File

@@ -69,7 +69,6 @@ namespace OpenRA.Mods.Common.Traits
new AnimationWithOffset(muzzleFlash, new AnimationWithOffset(muzzleFlash,
() => info.IgnoreOffset ? WVec.Zero : armClosure.MuzzleOffset(self, barrel), () => info.IgnoreOffset ? WVec.Zero : armClosure.MuzzleOffset(self, barrel),
() => IsTraitDisabled || !visible[barrel], () => IsTraitDisabled || !visible[barrel],
() => false,
p => RenderUtils.ZOffsetFromCenter(self, p, 2))); p => RenderUtils.ZOffsetFromCenter(self, p, 2)));
} }
} }

View File

@@ -105,7 +105,6 @@ namespace OpenRA.Mods.Common.Traits
anim = new AnimationWithOffset(overlay, anim = new AnimationWithOffset(overlay,
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
() => IsTraitDisabled && !renderProlonged, () => IsTraitDisabled && !renderProlonged,
() => false,
p => RenderUtils.ZOffsetFromCenter(self, p, 1)); p => RenderUtils.ZOffsetFromCenter(self, p, 1));
var rs = self.Trait<RenderSprites>(); var rs = self.Trait<RenderSprites>();

View File

@@ -47,13 +47,13 @@ namespace OpenRA.Mods.Common.Traits
var body = self.Trait<BodyOrientation>(); var body = self.Trait<BodyOrientation>();
buildComplete = !self.Info.HasTraitInfo<BuildingInfo>(); // always render instantly for units buildComplete = !self.Info.HasTraitInfo<BuildingInfo>(); // always render instantly for units
overlay = new Animation(self.World, rs.GetImage(self)); overlay = new Animation(self.World, rs.GetImage(self),
() => info.PauseOnLowPower && self.IsDisabled());
overlay.PlayThen(info.Sequence, () => visible = false); overlay.PlayThen(info.Sequence, () => visible = false);
var anim = new AnimationWithOffset(overlay, var anim = new AnimationWithOffset(overlay,
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
() => !visible || !buildComplete, () => !visible || !buildComplete,
() => info.PauseOnLowPower && self.IsDisabled(),
p => RenderUtils.ZOffsetFromCenter(self, p, 1)); p => RenderUtils.ZOffsetFromCenter(self, p, 1));
rs.Add(anim, info.Palette, info.IsPlayerPalette); rs.Add(anim, info.Palette, info.IsPlayerPalette);

View File

@@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.Traits
DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => turreted.TurretFacing); DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => turreted.TurretFacing);
DefaultAnimation.PlayRepeating(NormalizeSequence(self, Info.Sequence)); DefaultAnimation.PlayRepeating(NormalizeSequence(self, Info.Sequence));
rs.Add(new AnimationWithOffset( rs.Add(new AnimationWithOffset(
DefaultAnimation, () => BarrelOffset(), () => IsTraitDisabled, () => false, p => RenderUtils.ZOffsetFromCenter(self, p, 0))); DefaultAnimation, () => BarrelOffset(), () => IsTraitDisabled, p => RenderUtils.ZOffsetFromCenter(self, p, 0)));
// Restrict turret facings to match the sprite // Restrict turret facings to match the sprite
turreted.QuantizedFacings = DefaultAnimation.CurrentSequence.Facings; turreted.QuantizedFacings = DefaultAnimation.CurrentSequence.Facings;

View File

@@ -51,7 +51,12 @@ namespace OpenRA.Mods.Common.Traits
{ {
var rs = init.Self.Trait<RenderSprites>(); var rs = init.Self.Trait<RenderSprites>();
DefaultAnimation = new Animation(init.World, rs.GetImage(init.Self), baseFacing); Func<bool> paused = null;
if (info.PauseAnimationWhenDisabled)
paused = () => init.Self.IsDisabled() &&
DefaultAnimation.CurrentSequence.Name == NormalizeSequence(init.Self, Info.Sequence);
DefaultAnimation = new Animation(init.World, rs.GetImage(init.Self), baseFacing, paused);
rs.Add(new AnimationWithOffset(DefaultAnimation, null, () => IsTraitDisabled)); rs.Add(new AnimationWithOffset(DefaultAnimation, null, () => IsTraitDisabled));
if (info.StartSequence != null) if (info.StartSequence != null)
@@ -59,10 +64,6 @@ namespace OpenRA.Mods.Common.Traits
() => PlayCustomAnimationRepeating(init.Self, info.Sequence)); () => PlayCustomAnimationRepeating(init.Self, info.Sequence));
else else
DefaultAnimation.PlayRepeating(NormalizeSequence(init.Self, info.Sequence)); DefaultAnimation.PlayRepeating(NormalizeSequence(init.Self, info.Sequence));
if (info.PauseAnimationWhenDisabled)
DefaultAnimation.Paused = () =>
init.Self.IsDisabled() && DefaultAnimation.CurrentSequence.Name == NormalizeSequence(init.Self, Info.Sequence);
} }
public string NormalizeSequence(Actor self, string sequence) public string NormalizeSequence(Actor self, string sequence)

View File

@@ -60,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits
rotorAnim.PlayRepeating(info.Sequence); rotorAnim.PlayRepeating(info.Sequence);
rs.Add(new AnimationWithOffset(rotorAnim, rs.Add(new AnimationWithOffset(rotorAnim,
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))), () => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
null, () => false, p => ZOffsetFromCenter(self, p, 1))); null, p => ZOffsetFromCenter(self, p, 1)));
} }
public void Tick(Actor self) public void Tick(Actor self)

View File

@@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.Traits
DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => t.TurretFacing); DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => t.TurretFacing);
DefaultAnimation.PlayRepeating(NormalizeSequence(self, info.Sequence)); DefaultAnimation.PlayRepeating(NormalizeSequence(self, info.Sequence));
rs.Add(new AnimationWithOffset( rs.Add(new AnimationWithOffset(
DefaultAnimation, () => TurretOffset(self), () => IsTraitDisabled, () => false, p => RenderUtils.ZOffsetFromCenter(self, p, 1))); DefaultAnimation, () => TurretOffset(self), () => IsTraitDisabled, p => RenderUtils.ZOffsetFromCenter(self, p, 1)));
// Restrict turret facings to match the sprite // Restrict turret facings to match the sprite
t.QuantizedFacings = DefaultAnimation.CurrentSequence.Facings; t.QuantizedFacings = DefaultAnimation.CurrentSequence.Facings;