Any less ugly way to calculate object current coords in 2D when start pt, end pt, speed and movement start time are given?

Viewed 53

Sorry, but geometry never was my favorite subject in school, so...

Currently I coding 2D MMORPG server emulator and want to improve my current coords detection algorithm.

Client send to server packets with start point coords and finish point coords. Server receive it and assign movement start variable to DateTime.Now. Server know what distance character can travel for 1 second (that's speed). How to calculate character position after some seconds since he started movement?

Since I am bad at geometry, I coded this... junk... based on percents of traveled distance. It's working through, but too much calculations.

        public ushort X;
        public ushort Y;
        public ushort TargetX;
        public ushort TargetY;

        public DateTime MovementStartTime;
        public ushort CalculatedX
        {
            get
            {
                if (this.X == this.TargetX && this.Y == this.TargetY)
                    return this.X;

                float total_dst = CF.GetEuclideanDistanceBetween(this.X, this.Y, this.TargetX, this.TargetY);
                float seconds_since_movement_start = (float)(DateTime.Now - this.MovementStartTime).TotalSeconds;
                float passed_dst = Limits.CharacterSpeed * seconds_since_movement_start;

                if (passed_dst > total_dst)
                    return this.TargetX;

                float passed_dst_in_normalized_percents = (passed_dst * 100 / total_dst) / 100;

                return (ushort)(this.X - ((this.X - this.TargetX) * passed_dst_in_normalized_percents));
            }
        }

        public ushort CalculatedY
        {
            get
            {
                if (this.X == this.TargetX && this.Y == this.TargetY)
                    return this.Y;

                float total_dst = CF.GetEuclideanDistanceBetween(this.X, this.Y, this.TargetX, this.TargetY);
                float seconds_since_movement_start = (float)(DateTime.Now - this.MovementStartTime).TotalSeconds;
                float passed_dst = Limits.CharacterSpeed * seconds_since_movement_start;

                if (passed_dst > total_dst)
                    return this.TargetY;

                float passed_dst_in_normalized_percents = (passed_dst * 100 / total_dst) / 100;

                return (ushort)(this.Y - ((this.Y - this.TargetY) * passed_dst_in_normalized_percents));
            }
        }

The idea is to determine how much percents of the line character already passed based on it's speed and then combine it with the flat distance between start X and end X.

Maybe someone know a less ugly way to do it?.. enter image description here

2 Answers

When object is moving with constant speed, dependence of X-coordinate against time is

X = X0 + Vx * t

where Vx is x-component of velocity:

Vx = CharacterSpeed * dx / sqrt(dx*dx + dy*dy) 

where dx, dy are distances along axes. In your case something like:

dx = this.TargetX - this.StartX
 

As I mentioned in the comments a simple vector class will vastly simplify this. If you are using a later version of .Net then you can use the Vector2 class in System.Numerics. If you do not then you can just add my simple vector class (Vec2d) which I have included at the end of this post.

Then you can rewrite your two properties as a single property like this:

Vec2d calculatedXY
{
    get
    {
        Vec2d source = new Vec2d((float)this.X, (float)this.Y);
        Vec2d target = new Vec2d((float)this.TargetX, (float)this.TargetY);

        Vec2d diff = target - source;

        if(source == target)
            return source;

        float total_dst = diff.length;
        float seconds_since_movement_start = (float)(DateTime.Now - this.MovementStartTime).TotalSeconds;
        float passed_dst = Limits.CharacterSpeed * seconds_since_movement_start;

        if(passed_dst > total_dst)
            return target;

        return source + ((diff/total_dst) * passed_dst);
    }
}

Here's the Vec2d struct (could also be written as a class) if you need it:

public struct Vec2d
{
    public float X;
    public float Y;

    public Vec2d(float x_, float y_)
    {
        X = x_;
        Y = y_;
    }

    // calculated properties:
    public float length
    {
        get { return (float)Math.Sqrt(X * X + Y * Y); }
    }


    // class operator definitions:

    // equality
    public static bool operator ==(Vec2d left, Vec2d right)
    {
        return (left.X == right.X) && (left.Y == right.Y);
    }
    public static bool operator !=(Vec2d left, Vec2d right)
    {
        return !(left == right);
    }

    // addition
    public static Vec2d operator +(Vec2d left, Vec2d right)
    {
        return new Vec2d(left.X + right.X, left.Y + right.Y);
    }
    // subtraction
    public static Vec2d operator -(Vec2d left, Vec2d right)
    {
        return new Vec2d(left.X - right.X, left.Y - right.Y);
    }

    // unary negation
    public static Vec2d operator -(Vec2d value)
    {
        return new Vec2d(-value.X, -value.Y);
    }

    // scalar multiplication
    public static Vec2d operator *(float scalar, Vec2d value)
    {
        return new Vec2d(scalar * value.X, scalar * value.Y);
    }
    public static Vec2d operator *(Vec2d value, float scalar)
    {
        return new Vec2d(scalar * value.X, scalar * value.Y);
    }

    // multiplication (vector dot product, NOT scalar dot product)
    public static Vec2d operator *(Vec2d left, Vec2d right)
    {
        return new Vec2d(left.X * right.X, left.Y * right.Y);
    }

    // scalar division
    public static Vec2d operator /(Vec2d value, float scalar)
    {
        return new Vec2d(value.X / scalar, value.Y / scalar);
    }
}
Related