Files
OpenRA/OpenRA.Game/Graphics/Animation.cs
RoosterDragon a0db80cb6a Changes to reduce allocations in the main loop.
Targeted some methods that generated allocated a lot of memory in the main game loop:
- Actor.Render - Changed LINQ calls into equivalent loops. No allocation for delegates.
- Animation.Render - Returned an array rather than a yield expression. The array uses less memory than the complier generated enumerable.
- FrozenActor and FrozenUnderFog - Materialize the footprint into an array: The enumerable is not-trivial to evaluate is and evaluated many times inside the Tick function. The memory needed is minimal. Changed LINQ into equivalent loops to prevent delegate allocation. Should result in overall much faster calls.
- Widget.GetEventBounds - Changed LINQ calls into equivalent loops.
- MobileInfo.CanEnterCell - Changed LINQ calls into equivalent loops. Don't materialize list of blocking actors every time, instead enumerate them and only when they need to be checked.
- FrozenUnderFog.TickRender - Generate the renderables lazily and also remove a no-op Select call.
2014-05-23 14:48:11 +01:00

175 lines
4.4 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
namespace OpenRA.Graphics
{
public class Animation
{
public Sequence CurrentSequence { get; private set; }
public bool IsDecoration = false;
public Func<bool> Paused;
readonly Func<int> facingFunc;
int frame = 0;
bool backwards = false;
bool tickAlways;
string name;
public string Name { get { return name; } }
readonly SequenceProvider sequenceProvider;
public Animation(World world, string name)
: this(world, name, () => 0) { }
public Animation(World world, string name, Func<int> facingFunc)
: this(world.Map.SequenceProvider, name, facingFunc) { }
public Animation(SequenceProvider sequenceProvider, string name, Func<int> facingFunc)
{
this.sequenceProvider = sequenceProvider;
this.name = name.ToLowerInvariant();
this.tickFunc = () => {};
this.facingFunc = facingFunc;
}
int CurrentFrame { get { return backwards ? CurrentSequence.Start + CurrentSequence.Length - frame - 1 : frame; } }
public Sprite Image { get { return CurrentSequence.GetSprite(CurrentFrame, facingFunc()); } }
public IEnumerable<IRenderable> Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
{
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration);
if (CurrentSequence.ShadowStart >= 0)
{
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true);
return new IRenderable[] { shadowRenderable, imageRenderable };
}
return new IRenderable[] { imageRenderable };
}
public IEnumerable<IRenderable> Render(WPos pos, PaletteReference palette)
{
return Render(pos, WVec.Zero, 0, palette, 1f);
}
public void Play(string sequenceName)
{
PlayThen(sequenceName, null);
}
public void PlayRepeating(string sequenceName)
{
backwards = false;
tickAlways = false;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame = 0;
tickFunc = () =>
{
++frame;
if (frame >= CurrentSequence.Length)
frame = 0;
};
}
public bool ReplaceAnim(string sequenceName)
{
if (!HasSequence(sequenceName))
return false;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame %= CurrentSequence.Length;
return true;
}
public void PlayThen(string sequenceName, Action after)
{
backwards = false;
tickAlways = false;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame = 0;
tickFunc = () =>
{
++frame;
if (frame >= CurrentSequence.Length)
{
frame = CurrentSequence.Length - 1;
tickFunc = () => { };
if (after != null) after();
}
};
}
public void PlayBackwardsThen(string sequenceName, Action after)
{
PlayThen(sequenceName, after);
backwards = true;
}
public void PlayFetchIndex(string sequenceName, Func<int> func)
{
backwards = false;
tickAlways = true;
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame = func();
tickFunc = () => frame = func();
}
int timeUntilNextFrame;
Action tickFunc;
public void Tick()
{
if (Paused == null || !Paused())
Tick(40); // tick one frame
}
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(name, seq); }
public void Tick(int t)
{
if (tickAlways)
tickFunc();
else
{
timeUntilNextFrame -= t;
while (timeUntilNextFrame <= 0)
{
tickFunc();
timeUntilNextFrame += CurrentSequence != null ? CurrentSequence.Tick : 40; // 25 fps == 40 ms
}
}
}
public void ChangeImage(string newImage, string newAnimIfMissing)
{
newImage = newImage.ToLowerInvariant();
if (name != newImage)
{
name = newImage.ToLowerInvariant();
if (!ReplaceAnim(CurrentSequence.Name))
ReplaceAnim(newAnimIfMissing);
}
}
public Sequence GetSequence(string sequenceName)
{
return sequenceProvider.GetSequence(name, sequenceName);
}
}
}