… but nothing to announce yet. Taking a queue from animaleante, I’m moving to a model where I go from two distinct update steps (“do animations” and “update turns”) into one combined update step (“do animations if there are any, update turns if not”). This’ll require replacing the Actor list with a priority queue (to eventually support an Energy-based system where Actors are not locked to one-turn-per).
I’m still pondering the right way to incorporate real-time Animations in with a turn-based system. While that’s percolating, I did a bit of miscellaneous cleanup:
When the player targets an empty cell for an AoE skill, display the Cell background and Adornment (if any) in the targeting dialog
Slight refactoring to Cell.Render to handle how AoE range is rendered over out-of range cells (thanks animaleante!)
Actors regen health each turn (‘n’ turns after performing the last action). This was just adding some health directly each turn; I refactored that to use the Event (and eventually Buff) system. This will allow things like Levels that disallow any healing (including regen).
I cleaned up the DecideNextAction/PerformNextAction stuff a bit in anticipation of figuring out the Turn/Animation issue.
Here’s what the AoE targeting UX looks like when the area of effect falls outside the range of the Skill:
I’ve got vacation coming up next week (sunny Cabo San Lucas, hallelujah! ) - I’m hoping to resolve the Turn/Animation issue this week so that I can focus on more fun aspects of the Dungeon .
Quick update today on the Dungeon; I fixed the issue where firing the same Animation would cause it to not remove the emitter from the system (the result being that the particles wouldn’t stop emitting even after the Animation ended). In order to reduce memory allocations, I don’t put instance-specific variables in the derived keyframe classes. Or rather, I tried not to – I was allocating the particle emitter in the AnimationKeyFrame_Particle.Start function and deallocating it in AnimationKeyFrame_Particle.End. So two calls to Start would result in two allocations and only one deallocation. This was fixed by making the abstract AnimationKeyFrame.Start return a derived AnimationKeyFrameInstanceData object, which is then passed to Render and End. Most keyframe classes ignore this, but the particle one uses it to store the emitter (and system) and delete them in End.
Using that, the AnimationKeyFrame_Particle class is updated as follows:
public override KeyFrame_InstanceData Start(GameObject performer, GameObject target)
{
// Create the per-instance data object to store our particle emitter nad system.
AnimationKeyFrame_Particle_InstanceData data = new AnimationKeyFrame_Particle_InstanceData();
// Create the particle system for this keyframe if not already created.
data.ParticleSystem = new ParticleSystem(this, 10);
ParticleMgr.Systems.Add(data.ParticleSystem);
// Spin up a new particle emitter for this instance
data.ParticleEmitter = new ParticleEmitter(data.ParticleSystem, 10, GameState_InDungeon.MapCellToScreenCoords(performer.Location));
ParticleMgr.Emitters.Add(data.ParticleEmitter);
return data;
}
public override void End(KeyFrame_InstanceData data, GameObject performer, GameObject target)
{
AnimationKeyFrame_Particle_InstanceData myData = data as AnimationKeyFrame_Particle_InstanceData;
// Tear down our particle emitter
myData.ParticleSystem.RemoveWhenDone();
ParticleMgr.RemoveEmitter(myData.ParticleEmitter);
}
… and the end result is a correctly working Animation system (whew).
Here’s a pic of AoE in action:
No code drop today though; still some bugs in the AoE targeting system. More impactfully, it has raised some questions about turn timing and animations that I need to think through (specifically: user uses a skill like ice storm (in the picture above) – do the monsters move at the same time as the skill animation fires? after? before? automatically? etc…
Not much time to work on the Dungeon today, but was able to make some progress on AoE target (cell) selection for AoE skills. This is requiring some nontrivial (but not unexpected) tweaking to specifying Skill targets – before I was passing a GameObject, but now an AoE Skill can impact multiple targets. This has also caused a bit of refactoring in event Animations, causing me to move the animation into the Action definitions (in addition to the higher level Events being able to specify animations), and adding support for both success and failure animations.
Here’s what the definition for an AoE “Ice Storm” skill looks like:
<Skill Id="Priest3" Level="1" Name="Ice Storm" Tile="Skill_IceStorm" Description="Blast a group of targets with ice">
<Event Type="UseSkill" SelfSuccessAnimation="Rainbow">
<DoDamage Target="Cell" Amount="4+1d2" Range="3" AoERange="2"
OnSuccessText="A bolt of ice hits the {target} for {amount}"
TargetSuccessAnimation="IceBolt" TargetFailureAnimation="IceBoltFail" />
</Event>
</Skill>
Note that an AoE skill has both Range (how far away the Skill can be applied) and AoERange (the range of impact of the Skilll when used).
Here’s what the AoE selection currently looks like. It highlights the AoE range of impact in yellow:
No code as there remain many issues with the Animation refactoring and target refactoring…
I’ve worked out the last few kinks in single-mob target selection, although I’ve turned up a bug in the Animations that I need to track down (specifically: if you fire off the same animation twice in quick succession, it fails to remove the emitter from the system; some ref-counting issue somewhere…).
In order to minimize the amount of thumb movement while playing, the ContextualActionBar and SkillsBar work together:
Performing a Skill on a target mob selects that mob into the ContextualActionBar
Performing a skill from the SkillBar now sets the secondary skill in the ContextualActionBar to the same skill
Selecting a Skill from the SkillBar when there’s a target in the ContextualActionBar automatically selects that target for offensive skills (i.e. doesn’t open the targeting dialog
Here’s what it looks like after clicking on the skill in the skillbar and selecting the target:
Bunch of minor tweaks to the Target selection dialog/process; it’s working for Single-mob targets and most of the code is there for AoE effects, but I have a little ways to go before it’s done. Here’s what it looks like now:
Of note:
I’ve removed the “previous” button – I don’t think it’ll get used. This leaves the dialog unbalanced; something to fix later (perhaps with an “Info” button that brings up an Info dialog for the selected mob)
I’m highlighting out-of-range and invalid selection (e.g. wall) cells in Red.
I’ve made a number of tweaks to the selection logic; e.g. tracking the last selected mob, using and updating the ContextualActionBar’s selected mob, etc.
Still todo:
I’ve reached the point where I need to be able to inject values into event text messages. For instance, the text for the Bolt of Light skill will be something akin to “A bolt of light hits {target} for {amount}.” I need to be able to dynamically replace those {} values; I could pull that out of the Action.Info field, but that gets interesting for Events that have multiple Actions. Requires more thought…
I’ve somehow managed to break the data-driven Animations . I’ll get that fixed tonight and do a code drop then.
Currently, Skills can only be used against the “Self” target – e.g. you can cast a Heal spell on yourself, but not on another Actor. I’m extending Event Targets to include SingleMob, Cell (for Area of Effect skills), and later “Item” (e.g. for identify). Bunch of random infrastructure for this and some design work. I’m probably 80% done with the SingleMob target work, and 50% done with AoE.
To demo the UX, I’ve added a new “Bolt of Light” skill which does damage to a SingleMob target. Here’s what you see when you perform that skill:
One of the challenges with the UX is that I’m trying to support the ability for the user to play the game with minimal finger movement (I don’t want to force them to change how they’re holding the phone every time they use a skill). Simultaneously, I want them to be able to tap-to-select if so desired. The above UX should support either model…
I’m hoping to get at least the SingleMob targeting done tonight and will drop some code at that point.
This drop includes support for Xml-based particles as part of the Animation system. I’m not 100% happy with the implementation, but particles and animation isn’t where I’m looking to innovate . I am happy with how the Particle animations integrate in with the target and tile animations; particle systems can leverage the same scale and move (and to a lesser extent rotate) transforms that the other Animations types support. This’ll allow more involved animations later when I get around to creating them…
Particle Animations
I adopted and adapted the particle system from the Xml-driven particle sample up on create.msdn.com (here). The short version:
ParticleEmitter: Emits Particles (surprise!). This manages how often particles are emitted, what direction they’re emitted in, and their starting position. Multiple forms of emitters are supported: Cone, Circle, and Rectangle. For Circle and Rectangle emitters, you can define whether particles should originate within the emitter shape or on the edge of the shape. You can also define whether Particles should radiate outward from the center of the shape or in random directions.
ParticleSystem: Once Particles are emitted by a ParticleEmitter, the ParticleSystem manages them – so things like gravity/wind and ColorShifting or particle Scale Animations are managed by the ParticleSystem.
ParticleMgr: Single instance object that keeps track of all ParticleSystems and ParticleEmitters, keeping them alive and removing them when they’re done. This is the object with which the main game loops (update/draw) interact.
Defining Particle animations
These are defined in the same fashion as other data-driven animations; via Xml. Here’s an example of a simple particle animation that emits then in a cone:
Since the particles can tie into the transforms, you can do things like have the Cone emitter above rotate as it emits particles by just adding a Rotate animation:
I’ve created a video that demonstrates what those animations (and others) look like. Note that to demo this I just added a few skills to the Skills.xml file and set each Skill’s SelfAnimation attribute to one of the Animations in Animations.xml:
I’ve fixed the zooming issue and also implemented easing in and out. The main thing remaining to add is Particle Animations; those may wait a couple of days as my main goal was to get the data-driven animation infrastructure in place and running.
You can tie an Animation to any Event; in the Xml snippet below, it’s triggered by the “StartResting” Event that’s associated with the “SelfAnimation” field of the Item which the Player is wielding:
<Weapon Id="Axe1" Name="Rusty Hatchet of Resftul Mapping" Subclass="Axe" WeaponPower="10" ItemLevel="1" Level="1-3"
Tile="AxeBeaked" Description="A rusty hatchet">
<Event Type="StartResting" OnFireText="You sense the world around you" SelfAnimation="MoveTest">
<MagicMapping Amount="1%"/>
</Event>
</Weapon>
Here’s a quick video clip of what the Animation from my previous post looks like:
The data-driven Animation classes themselves are straightforward; the only part worth calling out is that I leverage a set of override values (ForcedMoveAmount, ForcedScaleAmount, etc), and instead of a boolean for “ForceMove”, I use a ref-count like model which supports multiple Animations working on the same object at the same time. Here’s the complete code for AnimationKeyFrame_Move; most of it is just determining which easing type to use:
class AnimationKeyFrame_Move : AnimationKeyFrame
{
public AnimationKeyFrame_Move(KeyframeAppliedTo appliedTo, int appliedToTileIndex)
: base(appliedTo, appliedToTileIndex)
{
}
public override void Render(int timeTick, float percentDone, GameObject performer, GameObject target)
{
switch (Easing)
{
case Easing.EaseOut:
performer.ForcedMoveAmount += (EndLoc - StartLoc) * percentDone * percentDone + StartLoc;
break;
case Easing.EaseIn:
performer.ForcedMoveAmount += (StartLoc - EndLoc) * (1 - percentDone) * (1 - percentDone) + EndLoc;
break;
case Easing.Linear:
performer.ForcedMoveAmount += StartLoc + (EndLoc - StartLoc) * percentDone;
break;
}
}
public override void Start(GameObject performer, GameObject target)
{
// We're within the keyframe's slot - force a move. Do this as a 'refcount' to
// support multiple simultaneous animations
performer.ForceMove++;
}
public override void End(GameObject performer, GameObject target)
{
performer.ForceMove--;
}
public Vector2 EndLoc;
public Vector2 StartLoc;
}
One bit of hackery to eventually replace is that I’ve modified the render loop to run as a series of render phases; this allows Animations to specify whether they should render on top of or underneath objects; it also ensures that tiles don’t draw over objects when the objects scale or move. I believe I can actually do away with the entire multipass approach and just leverage the depthbuffer using pre-defined depth values. For instance, if all tiles rendered at depth 1, and all objects rendered at depth .5, then an Animation can simply be rendered at .75 or .25 depending on whether it should appear under or over objects…
An interesting question that this raises: what happens if the user moves while a long animation is playing? What if they perform the same Animation-generating Action multiple times in quick succession? Should the animation stop (jarring)? Should the user not be allowed to move while animations are playing (forces slow gameplay)? Should all animations be required to be short (limiting)? I’m honestly not sure… I ran into the same issue with the floating combat text – if the user attacks a bunch of times in succession then they get overlapping text. I’m… not sure what the right answer is here yet.