From d7bea7bbf3ba08255e87e79f133a4aa9c70e6875 Mon Sep 17 00:00:00 2001
From: Adrian Paschkowski <git@wasdennnoch.me>
Date: Thu, 8 Apr 2021 22:03:57 +0200
Subject: [PATCH] Add LayeredInterpolator, interpolate cam pos and not input

Allows you to animate to a specific world position on request.
---
 Assets/Scenes/Forest.unity                 |  2 +
 Assets/Scripts/BaseCameraController.cs     | 53 +++++++++-----
 Assets/Scripts/Box.cs                      |  2 +
 Assets/Scripts/Interpolator.cs             | 16 ++---
 Assets/Scripts/LayeredInterpolator.cs      | 83 ++++++++++++++++++++++
 Assets/Scripts/LayeredInterpolator.cs.meta | 11 +++
 6 files changed, 141 insertions(+), 26 deletions(-)
 create mode 100644 Assets/Scripts/LayeredInterpolator.cs
 create mode 100644 Assets/Scripts/LayeredInterpolator.cs.meta

diff --git a/Assets/Scenes/Forest.unity b/Assets/Scenes/Forest.unity
index 52721d6..80e03cb 100644
--- a/Assets/Scenes/Forest.unity
+++ b/Assets/Scenes/Forest.unity
@@ -440,6 +440,8 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: 4471af6bb2e057546a362ad75c91f770, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
+  allowUserControl: 1
+  inputVelocity: 2.5
   moveSpeed: 10
   edgeDetectionBoxInsets: {x: 0.1, y: 0.1}
   maxZoom: 5
diff --git a/Assets/Scripts/BaseCameraController.cs b/Assets/Scripts/BaseCameraController.cs
index 745a8a9..efbc50b 100644
--- a/Assets/Scripts/BaseCameraController.cs
+++ b/Assets/Scripts/BaseCameraController.cs
@@ -8,6 +8,7 @@ public class BaseCameraController : MonoBehaviour
     [SerializeField] bool allowUserControl = true;
 
     [Header("Movement")]
+    [SerializeField] float inputVelocity = 2f;
     [SerializeField] float moveSpeed = 10f;
     [SerializeField] Vector2 edgeDetectionBoxInsets = new Vector2(0.1f, 0.1f);
 
@@ -24,10 +25,10 @@ public class BaseCameraController : MonoBehaviour
 
     InsetCameraEdges mouseMovementEdges;
     bool waitForMouseOutsideEdges = false;
-    Interpolator zoomInterpolator;
-    Interpolator moveXInterpolator;
-    Interpolator moveYInterpolator;
-    Vector3 inputVector = Vector3.zero;
+    LayeredInterpolator zoomInterpolator;
+    LayeredInterpolator xPositionInterpolator;
+    LayeredInterpolator yPositionInterpolator;
+    Vector2 inputVector;
     float zoomRatio
     {
         get => camera.orthographicSize / maxZoom;
@@ -43,6 +44,13 @@ public class BaseCameraController : MonoBehaviour
         this.allowUserControl = allowUserControl;
     }
 
+    public void AnimateToPosition(Vector3 worldPos, float zoom, float duration)
+    {
+        zoomInterpolator.PushInterpolator(new Interpolator(duration, camera.orthographicSize, zoom));
+        xPositionInterpolator.PushInterpolator(new Interpolator(duration, transform.position.x, worldPos.x));
+        yPositionInterpolator.PushInterpolator(new Interpolator(duration, transform.position.y, worldPos.y));
+    }
+
     void Awake()
     {
         camera = GetComponent<Camera>();
@@ -51,15 +59,22 @@ public class BaseCameraController : MonoBehaviour
         rigidbody = GetComponent<Rigidbody2D>();
 
         mouseMovementEdges = new InsetCameraEdges(camera, edgeDetectionBoxInsets);
-        zoomInterpolator = new Interpolator(1 / zoomSpeed, camera.orthographicSize, camera.orthographicSize, minZoom, maxZoom);
-        moveXInterpolator = new Interpolator(1 / moveSpeed);
-        moveYInterpolator = new Interpolator(1 / moveSpeed);
+        zoomInterpolator = new LayeredInterpolator(new Interpolator(1 / zoomSpeed, camera.orthographicSize, camera.orthographicSize, minZoom, maxZoom));
+        xPositionInterpolator = new LayeredInterpolator(new Interpolator(1 / moveSpeed, transform.position.x));
+        yPositionInterpolator = new LayeredInterpolator(new Interpolator(1 / moveSpeed, transform.position.y));
         UpdateColliderSize();
     }
 
     void Update()
     {
-        var inputZoomDelta = allowUserControl ? Input.mouseScrollDelta.y * -zoomStepSize : 0;
+        if (Input.GetKeyDown(KeyCode.Z)) // TODO Remove evntually
+        {
+            var player = GameObject.FindGameObjectWithTag("Player");
+            AnimateToPosition(player.transform.position + new Vector3(0f, 0.75f), 1.5f, 0.1f);
+        }
+
+
+        var inputZoomDelta = (allowUserControl && !zoomInterpolator.hasLayers) ? Input.mouseScrollDelta.y * -zoomStepSize : 0;
         if (inputZoomDelta != 0)
         {
             zoomInterpolator.targetValue += inputZoomDelta;
@@ -70,8 +85,7 @@ public class BaseCameraController : MonoBehaviour
             UpdateColliderSize();
         }
 
-        inputVector.x = Input.GetAxisRaw("Horizontal");
-        inputVector.y = Input.GetAxisRaw("Vertical");
+        inputVector = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
         if (inputVector.magnitude != 0)
         {
             waitForMouseOutsideEdges = true;
@@ -87,18 +101,25 @@ public class BaseCameraController : MonoBehaviour
         {
             inputVector = mouseInputVector;
         }
-        moveXInterpolator.targetValue = inputVector.x;
-        moveYInterpolator.targetValue = inputVector.y;
-        inputVector.x = moveXInterpolator.Tick();
-        inputVector.y = moveYInterpolator.Tick();
     }
 
     void FixedUpdate()
     {
-        if (inputVector.magnitude != 0)
+        xPositionInterpolator.currentValue = rigidbody.position.x;
+        yPositionInterpolator.currentValue = rigidbody.position.y;
+        if (allowUserControl && !xPositionInterpolator.hasLayers && !yPositionInterpolator.hasLayers)
         {
-            rigidbody.MovePosition(transform.position + inputVector * moveSpeed * zoomRatio * Time.fixedDeltaTime);
+            if (inputVector.x != 0)
+            {
+                xPositionInterpolator.targetValue = rigidbody.position.x + inputVector.x * zoomRatio * inputVelocity;
+            }
+            if (inputVector.y != 0)
+            {
+                yPositionInterpolator.targetValue = rigidbody.position.y + inputVector.y * zoomRatio * inputVelocity;
+            }
         }
+        var targetPos = new Vector3(xPositionInterpolator.Tick(), yPositionInterpolator.Tick());
+        rigidbody.MovePosition(targetPos);
     }
 
     void UpdateColliderSize()
diff --git a/Assets/Scripts/Box.cs b/Assets/Scripts/Box.cs
index 415eb2a..615d29b 100644
--- a/Assets/Scripts/Box.cs
+++ b/Assets/Scripts/Box.cs
@@ -1,5 +1,7 @@
+using System;
 using UnityEngine;
 
+[Serializable]
 public readonly struct Box
 {
     public readonly Vector3 bottomLeft;
diff --git a/Assets/Scripts/Interpolator.cs b/Assets/Scripts/Interpolator.cs
index 4fe29f2..5e14bd4 100644
--- a/Assets/Scripts/Interpolator.cs
+++ b/Assets/Scripts/Interpolator.cs
@@ -19,13 +19,13 @@ public class Interpolator
         }
     }
     public bool running { get; private set; }
+    public float currentValue;
+    public float velocity;
 
     private float duration;
     private float minValue;
     private float maxValue;
-    private float currentValue;
     private float _targetValue;
-    private float velocity;
 
     public Interpolator(float duration) : this(duration, 0) { }
 
@@ -39,8 +39,7 @@ public class Interpolator
         this.minValue = minValue;
         this.maxValue = maxValue;
         currentValue = startValue;
-        _targetValue = targetValue;
-        running = !Mathf.Approximately(startValue, targetValue);
+        this.targetValue = targetValue;
     }
 
     public float Tick()
@@ -48,18 +47,15 @@ public class Interpolator
         if (running)
         {
             currentValue = Mathf.SmoothDamp(currentValue, _targetValue, ref velocity, duration);
-            if (Mathf.Abs(velocity) < 0.001f)
+            if (Mathf.Abs(velocity) < 0.01f)
             {
                 running = false;
                 currentValue = targetValue;
             }
         }
-        return GetCurrentValue();
-    }
-
-    public float GetCurrentValue()
-    {
         return currentValue;
     }
 
+    public override string ToString() => $"Interpolator{{ Running: {running} Duration: {duration} Current Value: {currentValue} Target Value: {targetValue} Velocity: {velocity} }}";
+
 }
diff --git a/Assets/Scripts/LayeredInterpolator.cs b/Assets/Scripts/LayeredInterpolator.cs
new file mode 100644
index 0000000..0042f04
--- /dev/null
+++ b/Assets/Scripts/LayeredInterpolator.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+[Serializable]
+public class LayeredInterpolator
+{
+
+    private List<Interpolator> interpolators = new List<Interpolator>();
+
+    public bool hasLayers
+    {
+        get => interpolators.Count >= 2;
+    }
+
+    public bool running
+    {
+        get => interpolators.Any(i => i.running);
+    }
+    public float currentValue
+    {
+        get => GetCurrentInterpolator().currentValue;
+        set => GetCurrentInterpolator().currentValue = value;
+    }
+    public float targetValue
+    {
+        get => GetCurrentInterpolator().targetValue;
+        set => GetCurrentInterpolator().targetValue = value;
+    }
+    public float velocity
+    {
+        get => GetCurrentInterpolator().velocity;
+        set => GetCurrentInterpolator().velocity = value;
+    }
+
+    public LayeredInterpolator(Interpolator baseInterpolator)
+    {
+        interpolators.Add(baseInterpolator);
+    }
+
+    public void PushInterpolator(Interpolator newInterpolator)
+    {
+        if (newInterpolator.running)
+        {
+            interpolators.Add(newInterpolator);
+        }
+    }
+
+    public void PopInterpolator()
+    {
+        if (interpolators.Count >= 2)
+        {
+            interpolators.RemoveAt(interpolators.Count - 1);
+        }
+    }
+
+    public float Tick()
+    {
+        Interpolator current = GetCurrentInterpolator();
+        float tickValue = current.Tick();
+        for (var i = 0; i < interpolators.Count - 1; i++)
+        {
+            var interpolator = interpolators[i];
+            interpolator.velocity = current.velocity;
+            interpolator.currentValue = tickValue;
+            if (i == 0)
+            {
+                interpolator.targetValue = tickValue;
+            }
+        }
+        while (!GetCurrentInterpolator().running && interpolators.Count >= 2)
+        {
+            PopInterpolator();
+        }
+        return tickValue;
+    }
+
+    private Interpolator GetCurrentInterpolator()
+    {
+        return interpolators.Last();
+    }
+
+}
diff --git a/Assets/Scripts/LayeredInterpolator.cs.meta b/Assets/Scripts/LayeredInterpolator.cs.meta
new file mode 100644
index 0000000..2836f0c
--- /dev/null
+++ b/Assets/Scripts/LayeredInterpolator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 23fcc68f9aec3c7468d2ba352e9cc26a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
-- 
GitLab