0
\$\begingroup\$

What I want to achieve is to calculate the real object position based on its speed and a destination position. In my server, I have a MapInstance that have a list of MapLayer that have MapRegions.

To resume we have:

  • 1 MapInstance
    • N MapLayers
    • X MapRegions

Each MapInstance will update in an asynchronous task all his map layers and the common object such as NPCs and the MapLayer will update his regions of entities (monsters/items).

On the client side, when I click somewhere on the map, it sends a packet to the server with the destination position. Then, on the server-side I receive the position and set the entity.Movable.DestinationPosition property.

As I said earlier, each map instance are updated in an asynchronous task and updates the map layers and common entities. My problem is, my realtime position calculation formula is wrong somewhere, and I can't find where.

To understand the problem, there is some inputs:

  • Entity speed: 0.08 (8%)
  • Position and DestinationPosition are Vector3 (class) instance, where X, Y and Z are properties.

This is my code:

public void StartUpdateTask(int delay)
{
    Task.Run(async () =>
    {
        const double FrameRatePerSeconds = 0.66666f;
        double previousTime = Environment.TickCount;

        while (true)
        {
            if (this._cancellationToken.IsCancellationRequested)
                break;

            double currentTime = Environment.TickCount;
            double deltaTime = currentTime - previousTime;
            previousTime = currentTime;

            this.GameTime = (deltaTime * FrameRatePerSeconds) / 1000f;

            lock (SyncRoot)
            {
                foreach (var entity in this.Entities)
                    Walk(entity);

                foreach (var mapLayer in this._layers)
                     mapLayer.Update();
            }

            await Task.Delay(delay, this._cancellationToken).ConfigureAwait(false);
        }
    }, this._cancellationToken);
}

mapLayer.Update() method:

public override void Update()
{
    this.GameTime = this.Parent.GameTime;

    foreach (var entity in this.Entities)
        Walk(entity);

    foreach (var region in this._regions)
    {
        if (region.IsActive && region is IMapRespawnRegion respawnRegion)
        {
            foreach (var entity in respawnRegion.Entities)
            {
                Walk(entity);
            }
        }
    }
}

And the calculation formula:

private void Walk(IMovableEntity entity)
{
    if (entity.MovableComponent.DestinationPosition.IsZero())
        return;

    if (entity.MovableComponent.DestinationPosition.IsInCircle(entity.Object.Position, 0.1f))
    {
        entity.MovableComponent.HasArrived = true;
        entity.MovableComponent.DestinationPosition.Reset();
    }
    else
    {
        entity.MovableComponent.HasArrived = false;
        double entitySpeed = entity.MovableComponent.Speed * entity.MovableComponent.SpeedFactor;
        double speed = ((entitySpeed * 100f) * entity.Context.GameTime);
        float distanceX = entity.MovableComponent.DestinationPosition.X - entity.Object.Position.X;
        float distanceZ = entity.MovableComponent.DestinationPosition.Z - entity.Object.Position.Z;
        double distance = Math.Sqrt(distanceX * distanceX + distanceZ * distanceZ);

        // Normalize
        double deltaX = distanceX / distance;
        double deltaZ = distanceZ / distance;
        double offsetX = deltaX * speed;
        double offsetZ = deltaZ * speed;

        if (Math.Abs(offsetX) > Math.Abs(distanceX))
            offsetX = distanceX;
        if (Math.Abs(offsetZ) > Math.Abs(distanceZ))
            offsetZ = distanceZ;

        entity.Object.Position.X += (float)offsetX;
        entity.Object.Position.Z += (float)offsetZ;
    }
}

With this current formula, my players and monsters arrive to destination between 1-2 seconds sooner on the server side compared to the client-side... Do you have any idea what I'm doing wrong ? Appriciate if someone could guide me and explain me how to solve my problem. :-)

Thank you

\$\endgroup\$
0
\$\begingroup\$

What is the FrameRatePerSeconds supposed to accomplish in the main task update? Game time is independent of the framerate refresh cycle and your server most probably doesn't even have display frames in the first place. It looks like every update you're multiplying in the framerate, unless I read that wrong...

\$\endgroup\$
  • \$\begingroup\$ The frame rate was to achieve a fixed time step on the server side. Since the game is displaying ~60 frames per seconds, I thought a fixed time step could do the job, but apparently not... :-/ \$\endgroup\$ – Eastrall Jan 13 '19 at 18:25
  • \$\begingroup\$ Your FrameRatePerSeconds property at 60fps should be 16.67 milliseconds, I think. Every frame rendered at 60fps lasts for 16.67 milliseconds before the next frame. I have no idea why "delay" is passed in and kept static, I think that what you really need is to remove that parameter and change the await to something like this: "await Task.Delay(FrameRatePerSeconds-deltaTime, this._cancellationToken).ConfigureAwait(false);" That runs this task adjusted for 60fps taking into account how much time it took since last frame. (I wish I had more time to format properly) Does that sound right? \$\endgroup\$ – Patrick Hughes Jan 14 '19 at 19:15
  • \$\begingroup\$ So if I understand correctly, I need to defined my FrameRatePerSeconds to 60 and then substract the tick delta in order to have a fixed timestep right? but what if the substraction is negative? like: 60fps - 73ticks this will give me -13 and my delay will not work. Should I use a Math.Abs on the result or i'm missing something on my calculation? \$\endgroup\$ – Eastrall Jan 15 '19 at 19:27
  • \$\begingroup\$ If you go negative then you're trying to do more than your code+hardware can handle. In that case you need to pursue optimization and/or re-design to handle less so you never go there. \$\endgroup\$ – Patrick Hughes Jan 15 '19 at 20:47
  • \$\begingroup\$ I see, it makes sense thought. But now, since we've touched the GameTime property, the speed is now wrong. I had double speed = ((entitySpeed * 100f) * entity.Context.GameTime);where entitySpeed is 0.08. Should I change the formula, or i forgot something on the GameTime calculation ? \$\endgroup\$ – Eastrall Jan 16 '19 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.