Archive for April, 2009

Throwing potions II

Not too much progress this week, been busy with other stuff. Most of my development effort went towards potions – there’s a simple HUD that shows the player’s selected potion and you can cycle through them using a button on the controller. I started working on the stone potion, which creates a short-lived rock outcropping when it impacts a surface. The basic functionality is there, but I still need to do lots of tweaking – the shape of the outcropping isn’t quite right, and it needs to decay after a little while instead of lasting forever. It also spawns at the wrong angle when hitting some surfaces, so I still need to figure that out as well.

Tags: , , ,

Damage effects

My goals for the damage effect code were as follows:

  1. I should be able to set a desired amount of damage for an effect to deal over its entire duration
  2. Effects should deal a portion of their damage every frame while an entity is within range
  3. Effects should not deal damage to an entity if it has moved out of range
  4. I should be able to set a curve for a given effect so that I can sync the damage with the onscreen animation (pulsing, fading in, fading out, etc)
  5. Effects should be able to deal a different amount of damage based on how close an entity is to the effect

The first thing I did was to set a pair of values that specify the damage multiplier for the inside and outside edges of the damage effect – this allowed me to create effects with a smooth damage gradient from the inside to the outside, along with effects that have an inverted gradient or effects that deal the same amount of damage over their entire area of effect. I also added a pair of values that control the radius of the inner and outer parts of the damage ramp. The end result looks something like this:

Once I have the damage ramp figured out, it’s easy to map a given point within the circle to a damage value, and I can use that approach to figure out how much damage an entity should take based on its distance from the center.

The next step is to apply a second multiplier, based on a damage curve. This lets me control how long a damage effect is and the nature of the effect – pulsing, fading, etc. The curve takes the time elapsed since the start of the effect as an input, and outputs a damage multiplier (in the range 0 to 1) so that I can multiply it by the distance value from the damage ramp.

Once I have the two multipliers for a given entity, all that’s left is to determine how much damage to deal during the given frame. In order to get this value, I have to come up with a multiplier that will cause the effect’s total damage dealt to sum up to the desired amount.

As an example, if I want the effect to deal a total of 50 damage, and the damage curve I specified is 0.13 seconds long, that maps to around 8 frames. Given that, I can walk over the entire length of the curve, sampling it in single-frame steps. The sum of those values gives me the total value of the curve, which I can then map to the desired total damage value using simple arithmetic. Combining that with the other two multipliers (from the damage ramp and the damage curve) gives me the actual amount of damage to deal during the current frame.

The final implementation ends up looking like this:

public void Initialize () {
    CurveSum = 0.0f;

    float frameLength = 1.0f / 60.0f;
    int numFrames = (int)Math.Ceiling(DamageCurve.End * 60);
    float t = 0.0f;

    for (int i = 0; i < numFrames; i++) {
        CurveSum += DamageCurve[t];

        t += frameLength;
    }
}

public void Update (GameTime gameTime) {
    var t = (float)Game.AnimationTimeProvider.Seconds - StartTime;
    var curve = DamageCurve[t];

    float frameDamage = (TotalDamage / CurveSum) * curve;

    foreach (var hit in FindHits()) {
        float rampPosition = MathHelper.Clamp((hit.Distance - InnerRadius) / (OuterRadius - InnerRadius), 0.0f, 1.0f);
        float hitDamage = frameDamage * MathHelper.Lerp(InnerMultiplier, OuterMultiplier, rampPosition);
        hit.Entity.Injure(hitDamage);
    }

    if (t > DamageCurve.End)
        Game.Components.Remove(this);
}

internal IEnumerable<HitInfo> FindHits () {
    var bounds = new Bounds(
        new Vector2(Center.X - OuterRadius, Center.Y - OuterRadius),
        new Vector2(Center.X + OuterRadius, Center.Y + OuterRadius)
    );

    RuntimeEntityList.ItemInfo itemInfo;
    using (var e = Game.RuntimeLevel.Entities.GetItemsFromBounds(bounds))
    while (e.GetNext(out itemInfo)) {
        if (!bounds.Intersects(itemInfo.Bounds))
            continue;

        float minDistanceSquared = float.MaxValue;

        foreach (var poly in itemInfo.Item.GetCollisionList()) {
            int count = poly.Count;
            for (int i = 0; i < count; i++) {
                var edge = poly.GetEdge(i);
                var closestPoint = Geometry.ClosestPointOnLine(Center, edge.Start, edge.End);
                minDistanceSquared = Math.Min(minDistanceSquared, (closestPoint - Center).LengthSquared());
            }
        }

        if (minDistanceSquared < float.MaxValue) {
            yield return new HitInfo {
                Entity = itemInfo.Item,
                Distance = (float)Math.Sqrt(minDistanceSquared)
            };
        }
    }
}

Tags: , , ,

Throwing potions

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.

Tags: , , ,

Trajectories

Spent some time this week tuning various physics parameters to make things fit with the new size of the player sprites. Things still aren’t quite perfect, but the feel of things is pretty close to what I want.

I also spent some time working on support for one of the player characters’ special abilities – throwing potions. It took a little effort to come up with a good set of constants to give the potion a convincing arc, but I’ve got the ability to aim a potion working and I’m pretty happy with how it feels. Next, I need to get some basic art for the potion into the game so you can see the player throw it, and implement the collision detection so that a potion can shatter when it hits a surface.

Tags: , , ,

luminance is Digg proof thanks to caching by WP Super Cache