Helco / zzio

Zanzarah - WIP modding tools and engine remake
MIT License
14 stars 3 forks source link

Fix FPS dep. collectable blinking #128

Open github-actions[bot] opened 2 years ago

github-actions[bot] commented 2 years ago

Fix FPS dep. collectable blinking

https://github.com/Helco/zzio/blob/8a440744df599e45eac6a473310e8bb6cef9e3c5/zzre/game/systems/model/BehaviourCollectablePhysics.cs#L96


using System;
using System.Numerics;
using DefaultEcs.System;

namespace zzre.game.systems
{
    public partial class BehaviourCollectablePhysics : AEntitySetSystem<float>
    {
        private const float MinVelocityY = 1.2f;
        private const float RangeVelocityY = 0.3f;
        private const float VelocityFactor = 2f;
        private const float CameraDirFactor = 0.15f;
        private const float RandomDirFactor = 0.1f;
        private const float MaxAge = 20f;
        private const float BlinkingAge = 19f;
        private const float YOffset = 0.5f;
        private const float Gravity = 3.1f;
        private const float BonkSpeed = 0.5f;
        private const float BonkYSpeed = -0.6f;
        private const float BonkDistance = 0.2f;
        private const float MinBonkYSpeed = 0.3f;

        private readonly rendering.Camera camera;
        private readonly WorldCollider worldCollider;
        private readonly IDisposable addedSubscription;

        public BehaviourCollectablePhysics(ITagContainer diContainer) : base(diContainer.GetTag<DefaultEcs.World>(), CreateEntityContainer, useBuffer: false)
        {
            camera = diContainer.GetTag<rendering.Camera>();
            worldCollider = diContainer.GetTag<WorldCollider>();
            addedSubscription = World.SubscribeComponentAdded<components.behaviour.CollectablePhysics>(HandleComponentAdded);
        }

        public override void Dispose()
        {
            base.Dispose();
            addedSubscription.Dispose();
        }

        private void HandleComponentAdded(in DefaultEcs.Entity entity, in components.behaviour.CollectablePhysics physics)
        {
            var random = GlobalRandom.Get;
            var cameraDir = camera.Location.InnerForward;

            entity.Set(physics with
            {
                Velocity = Vector3.Normalize(
                    (random.InCube() * RandomDirFactor + cameraDir * CameraDirFactor) with { Y = 0f }),
                YVelocity = random.NextFloat() * RangeVelocityY + MinVelocityY
            });
        }

        [Update]
        private void Update(
            float elapsedTime,
            in DefaultEcs.Entity entity,
            Location location,
            ref components.behaviour.Collectable collectable,
            ref components.behaviour.CollectablePhysics physics)
        {
            Aging(elapsedTime, entity, ref collectable);
            if (MathEx.CmpZero(physics.YVelocity))
                return;

            var oldPos = location.LocalPosition;
            var curVelocity = VelocityFactor * physics.Velocity with { Y = physics.YVelocity };
            var newPos = oldPos + elapsedTime * curVelocity - Vector3.UnitY * YOffset;
            physics.YVelocity -= elapsedTime * Gravity;

            var intersection = worldCollider.Cast(new Line(oldPos, newPos));
            if (intersection.HasValue)
            {
                physics.Velocity = Vector3.Normalize(intersection.Value.Normal) * BonkSpeed;
                physics.YVelocity *= BonkYSpeed;
                if (physics.YVelocity < MinBonkYSpeed)
                    physics.YVelocity = 0f; // stopping any jumps

                newPos = intersection.Value.Point * BonkDistance;
            }
            location.LocalPosition = newPos + Vector3.UnitY * YOffset;
        }

        private static void Aging(
            float elapsedTime,
            DefaultEcs.Entity entity,
            ref components.behaviour.Collectable collectable)
        {
            if (collectable.IsDying)
                return;

            collectable.Age += elapsedTime;
            if (collectable.Age > MaxAge)
                entity.Set<components.Dead>();
            if (collectable.Age > BlinkingAge)
            {
                // TODO: Fix FPS dep. collectable blinking
                if (entity.Has<components.Visibility>())
                    entity.Remove<components.Visibility>();
                else
                    entity.Set<components.Visibility>();
            }
        }
    }
}

2b3ab6b0e22c4f2933fe37e970133d1c9275d6dd

Helco commented 2 years ago

Notes for general fixing of FPS-dependent behaviours: A framerate to use for originally-dependent behaviours should be configurable so that the experience is comparable to playing the original Zanzarah on a computer of 2002.