Most of my work this week was centered around adding the ability to throw potions of various types.
The first task was to implement basic collision detection for potions. I based this on the code I already wrote for drawing the trajectory of a potion when aiming, as shown in the previous post. The basic structure is that the potion has a position and a velocity, and has a gravitational pull applied every ‘step’. To compute the trajectory for drawing, I iteratively ‘step’ forward along the path of the potion, drawing dots onscreen every few steps, to show the predicted path of the potion if thrown in a given direction. Since I already had the code for that, it was easy to pull the step code out into a function of its own, and then call that from the Update function for the actual thrown potion. The end result looks like this:
internal static bool UpdateMotion (Sapphira sapphira, ref Vector2 position, ref Vector2 velocity) {
Vector2 newPosition = position + velocity;
if (sapphira.Game.ResolveMotion(position, newPosition, ref newPosition, sapphira)) {
position = newPosition;
velocity = new Vector2(0.0f, 0.0f);
return false;
} else {
position = newPosition;
velocity.Y = velocity.Y + GravitationRate;
float dist = velocity.Length();
if (dist > TravelVelocity)
dist = Math.Max(TravelVelocity, dist - DecelerationRate);
velocity = Vector2.Normalize(velocity) * dist;
return true;
}
}
internal static IEnumerable<Vector2> ComputeTrajectory (Sapphira sapphira, Vector2 startPosition, Vector2 direction) {
var position = startPosition;
var velocity = direction * InitialVelocity;
for (int i = 0; i < 200; i++) {
yield return position;
UpdateMotion(sapphira, ref position, ref velocity);
if (velocity.LengthSquared() <= 0)
yield break;
}
}
The ComputeTrajectory iterator is used by the drawing routines to show the predicted trajectory of a potion, and the UpdateMotion function is used by both the iterator and the actual thrown potion to update motion. Pretty simple, and it works fairly well. One thing that may need improvement is the length of ComputeTrajectory’s projection – right now it just steps 200 times, since that’s ‘about enough’ in all of my test cases to draw a full onscreen trajectory. I may need to come up with a better technique for that in the future if things change.
My initial implementation of UpdateMotion had a bug for a while where if you threw a potion directly upward, it would never fall – this was pretty sneaky, since it resulted from a minor mistake, where I was computing ‘dist’ based on the velocity of the potion before I applied gravity. I spent a little time messing around before I finally realized what was happening, because the visualization I was using to look at the results wasn’t making it clear what was going on. Luckily I caught the bug before writing too much code, otherwise I might have had to waste a lot of time trying to track it down.
Once I had the collision detection working, I spent some time implementing basic animation for the potions, so they leave a trail in the air as they fly and spray out some fluid when they collide with a surface and shatter. Tuning particle effects is a tough job, so I ended up having to make myself stop fiddling with it after a while and move onto something more productive, but it at least does the job.
My work-in-progress is the code for allowing potions to deal damage when shattered – right now, I’m modeling a potion’s impact as a circle, with a pair of radiuses that specify the two ends of a damage ramp. This allows me to create damage effects with a hard edge, damage effects with a softer damage ramp, and damage effects that combine the two (a hard-edged center with a ramp on the outside), which should be sufficient for the potions I have designed currently. To handle the other half of it – the fading in/out of the damage effect, based on the type of the potion – right now I’m just using a Curve that represents damage over time, as a multiplier based on the location of the target relative to the damage effect. I’m not totally certain this will work, but it seems to be effective so far.
public class FirePotion : ThrownPotion {
public CircularDamageEffect DamageEffect = new CircularDamageEffect {
InnerMultiplier = 1.0f,
OuterMultiplier = 0.0f,
InnerRadius = 16.0f,
OuterRadius = 64.0f,
DamageCurve = new Curve<float> {
{0.0f, 0.25f, Interpolators<float>.Cubic},
{0.25f, 1.0f, Interpolators<float>.Cubic},
{0.75f, 0.0f, Interpolators<float>.Cubic},
}
};
public FirePotion (Sapphira sapphira, Vector2 initialPosition, Vector2 direction)
: base (sapphira, initialPosition, direction) {
Color1 = new Color(0.9f, 0.4f, 0.2f, 1.0f);
Color2 = new Color(0.825f, 0.225f, 0.125f, 1.0f);
}
public override void Impact () {
base.Impact();
Game.SpawnSparks(Position);
DamageEffect.TriggerAt(Game, Position);
}
}
Right now you can’t choose which kind of potion to throw, so the game just randomly selects a type every time you throw one. I plan to address this once damage effects are fully working. I’m also not totally happy with the way you aim potions right now – you just choose the initial trajectory of the potion, and you can’t adjust how hard you throw it, so it is a bit difficult to aim in certain directions. I’m thinking I might allow the player to aim at a position instead, and create a trajectory that passes through that point at a given distance, based on how hard you push the analog stick, in order to make it easier to aim.
In the video above, you can see the two different potion types have slightly different particle effects, and different damage ramps (drawn as solid white circles). While the damage effects detect collision with an entity (highlighted with a thin red box), they don’t actually deal damage to the entity yet, since I haven’t worked out the math for it. My goal is for an entity directly hit by a potion to take exactly N damage, since the ramp at the center is always exactly 1. If the entity is only somewhat near a potion, and the ramp’s value is 0.5, I want the entity to take a total of 0.5 * N damage, if he remains within the area of effect for the entire duration. Since damage is dealt over time, I have to determine the total length of the damage effect, and based on that compute how much of the target damage (N) to deal each frame while the entity is inside the area of effect.


