diff --git a/Assets/Data/Settings/DefaultSettings.asset b/Assets/Data/Settings/DefaultSettings.asset index b6d26cb3..6f0dd9ce 100644 --- a/Assets/Data/Settings/DefaultSettings.asset +++ b/Assets/Data/Settings/DefaultSettings.asset @@ -14,3 +14,14 @@ MonoBehaviour: m_EditorClassIdentifier: playerStopDistance: 10 followerPickupDelay: 0.2 + followDistance: 5 + manualMoveSmooth: 2 + thresholdFar: 10 + thresholdNear: 7 + stopThreshold: 0.5 + moveSpeed: 15 + stopDistance: 0.1 + useRigidbody: 1 + followUpdateInterval: 0.1 + followerSpeedMultiplier: 1.2 + heldIconDisplayHeight: 2 diff --git a/Assets/Prefabs/Puzzles/AssPuzzlePickup.prefab b/Assets/Prefabs/Puzzles/AssPuzzlePickup.prefab index 1a5e2169..da88522c 100644 --- a/Assets/Prefabs/Puzzles/AssPuzzlePickup.prefab +++ b/Assets/Prefabs/Puzzles/AssPuzzlePickup.prefab @@ -10,15 +10,15 @@ PrefabInstance: m_Modifications: - target: {fileID: 530025588511586483, guid: 68fe09242bd73f34bad304c509872d90, type: 3} propertyPath: m_LocalScale.x - value: 5 + value: 4.25 objectReference: {fileID: 0} - target: {fileID: 530025588511586483, guid: 68fe09242bd73f34bad304c509872d90, type: 3} propertyPath: m_LocalScale.y - value: 5 + value: 4 objectReference: {fileID: 0} - target: {fileID: 530025588511586483, guid: 68fe09242bd73f34bad304c509872d90, type: 3} propertyPath: m_LocalScale.z - value: 5 + value: 1 objectReference: {fileID: 0} - target: {fileID: 530025588511586483, guid: 68fe09242bd73f34bad304c509872d90, type: 3} propertyPath: m_LocalPosition.x diff --git a/Assets/Scenes/AppleHillsOverworld.unity b/Assets/Scenes/AppleHillsOverworld.unity index a7b6cf74..58521d89 100644 --- a/Assets/Scenes/AppleHillsOverworld.unity +++ b/Assets/Scenes/AppleHillsOverworld.unity @@ -23045,6 +23045,30 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 3435632802124758411, guid: 8ac0210dbf9d7754e9526d6d5c214f49, type: 3} + propertyPath: acceleration + value: 15 + objectReference: {fileID: 0} + - target: {fileID: 3435632802124758411, guid: 8ac0210dbf9d7754e9526d6d5c214f49, type: 3} + propertyPath: thresholdFar + value: 12 + objectReference: {fileID: 0} + - target: {fileID: 3435632802124758411, guid: 8ac0210dbf9d7754e9526d6d5c214f49, type: 3} + propertyPath: thresholdNear + value: 7 + objectReference: {fileID: 0} + - target: {fileID: 3435632802124758411, guid: 8ac0210dbf9d7754e9526d6d5c214f49, type: 3} + propertyPath: followDistance + value: 5 + objectReference: {fileID: 0} + - target: {fileID: 3435632802124758411, guid: 8ac0210dbf9d7754e9526d6d5c214f49, type: 3} + propertyPath: manualMoveSmooth + value: 100 + objectReference: {fileID: 0} + - target: {fileID: 3435632802124758411, guid: 8ac0210dbf9d7754e9526d6d5c214f49, type: 3} + propertyPath: heldIconDisplayHeight + value: 2 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] diff --git a/Assets/Scripts/FollowerController.cs b/Assets/Scripts/FollowerController.cs index b73f0b4b..d91ed8c1 100644 --- a/Assets/Scripts/FollowerController.cs +++ b/Assets/Scripts/FollowerController.cs @@ -5,7 +5,6 @@ using UnityEngine.SceneManagement; public class FollowerController : MonoBehaviour { [Header("Follower Settings")] - public float followDistance = 1.5f; // Desired distance behind player public bool debugDrawTarget = true; public float followUpdateInterval = 0.1f; // How often to update follow logic public float manualMoveSmooth = 8f; // Smoothing factor for manual movement @@ -15,28 +14,30 @@ public class FollowerController : MonoBehaviour private AIPath aiPath; private Vector3 targetPoint; private float timer; - private bool usePathfinding = false; private bool isManualFollowing = true; // Default to manual following private Vector3 lastMoveDir = Vector3.right; // Default direction private float currentSpeed = 0f; - [Header("Speed Settings")] - public float acceleration = 10f; - public float deceleration = 12f; - public float thresholdFar = 2.5f; - public float thresholdNear = 0.5f; - public float stopThreshold = 0.1f; // Stop moving when within this distance - private float playerMaxSpeed = 5f; - private const float followerSpeedMultiplier = 1.2f; - private float followerMaxSpeed = 6f; // Default, will be set from player - private float defaultFollowerMaxSpeed = 6f; // Default fallback value - private Animator animator; private Transform artTransform; [Header("Held Item")] public PickupItemData currentlyHeldItem; public SpriteRenderer heldObjectRenderer; + public float heldIconDisplayHeight = 2.0f; // Desired height for held item icon + + private bool isReturningToPlayer = false; // Track if follower is returning after pickup + + // Speed fields for follower + private float playerMaxSpeed = 5f; + private float followerMaxSpeed = 6f; + private float defaultFollowerMaxSpeed = 6f; + + // Pickup events + public delegate void FollowerPickupHandler(); + public event FollowerPickupHandler OnPickupArrived; + public event FollowerPickupHandler OnPickupReturned; + private Coroutine pickupCoroutine; void Awake() { @@ -69,60 +70,30 @@ public class FollowerController : MonoBehaviour FindPlayerReference(); } - void FindPlayerReference() - { - GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); - if (playerObj != null) - { - playerTransform = playerObj.transform; - playerAIPath = playerObj.GetComponent(); - if (playerAIPath != null) - { - playerMaxSpeed = playerAIPath.maxSpeed; - defaultFollowerMaxSpeed = playerMaxSpeed; - followerMaxSpeed = playerMaxSpeed * followerSpeedMultiplier; - } - } - else - { - playerTransform = null; - playerAIPath = null; - } - } - - void Start() - { - FindPlayerReference(); - } - void UpdateFollowTarget() { if (playerTransform == null) { - // Try to reacquire reference if lost FindPlayerReference(); if (playerTransform == null) - return; // Still missing, skip update + return; } if (isManualFollowing) { - // Determine direction: use player's velocity if available, else lastMoveDir Vector3 playerPos = playerTransform.position; Vector3 moveDir = Vector3.zero; if (playerAIPath != null && playerAIPath.velocity.magnitude > 0.01f) { moveDir = playerAIPath.velocity.normalized; - lastMoveDir = moveDir; // Update last valid direction + lastMoveDir = moveDir; } else { - moveDir = lastMoveDir; // Use last direction if stationary + moveDir = lastMoveDir; } - // Calculate target point behind player - targetPoint = playerPos - moveDir * followDistance; - targetPoint.z = 0; // For 2D - - // Only disable aiPath if in manual mode + // Use GameSettings for followDistance + targetPoint = playerPos - moveDir * GameManager.Instance.FollowDistance; + targetPoint.z = 0; if (aiPath != null) { aiPath.enabled = false; @@ -134,73 +105,93 @@ public class FollowerController : MonoBehaviour { if (playerTransform == null) { - // Try to reacquire reference if lost FindPlayerReference(); if (playerTransform == null) - return; // Still missing, skip update + return; } timer += Time.deltaTime; - if (timer >= followUpdateInterval) + if (timer >= GameManager.Instance.FollowUpdateInterval) { timer = 0f; UpdateFollowTarget(); } - // Manual movement logic with acceleration/deceleration and stop threshold if (isManualFollowing) { - // 2D distance calculation (ignore z) Vector2 current2D = new Vector2(transform.position.x, transform.position.y); Vector2 target2D = new Vector2(targetPoint.x, targetPoint.y); float dist = Vector2.Distance(current2D, target2D); - if (dist > stopThreshold) + float minSpeed = followerMaxSpeed * 0.3f; + float lerpFactor = GameManager.Instance.ManualMoveSmooth * Time.deltaTime; + float targetSpeed = 0f; + if (dist > GameManager.Instance.StopThreshold) { - float desiredSpeed = followerMaxSpeed; - if (dist > thresholdFar) + if (dist > GameManager.Instance.ThresholdFar) { - // Accelerate to follower's max speed - desiredSpeed = Mathf.Min(currentSpeed + acceleration * Time.deltaTime, followerMaxSpeed); + targetSpeed = followerMaxSpeed; } - else if (dist <= thresholdNear && dist > stopThreshold) + else if (dist > GameManager.Instance.ThresholdNear && dist <= GameManager.Instance.ThresholdFar) { - // Decelerate as approaching target, but keep moving until stopThreshold - desiredSpeed = Mathf.Max(currentSpeed - deceleration * Time.deltaTime, 0.5f); + targetSpeed = followerMaxSpeed; } - else + else if (dist > GameManager.Instance.StopThreshold && dist <= GameManager.Instance.ThresholdNear) { - // Maintain follower's max speed - desiredSpeed = followerMaxSpeed; + targetSpeed = minSpeed; + } + currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, lerpFactor); + if (dist > GameManager.Instance.StopThreshold && dist <= GameManager.Instance.ThresholdNear) + { + currentSpeed = Mathf.Max(currentSpeed, minSpeed); } - currentSpeed = desiredSpeed; Vector3 dir = (targetPoint - transform.position).normalized; transform.position += dir * currentSpeed * Time.deltaTime; } else { - // Stop moving when close enough currentSpeed = 0f; } } - - // Update animator speed parameter + if (isReturningToPlayer && aiPath != null && aiPath.enabled && playerTransform != null) + { + aiPath.destination = playerTransform.position; + } if (animator != null) { float normalizedSpeed = 0f; if (isManualFollowing) { - // Use currentSpeed for manual following normalizedSpeed = currentSpeed / followerMaxSpeed; } else if (aiPath != null) { - // Use aiPath velocity for pathfinding mode normalizedSpeed = aiPath.velocity.magnitude / followerMaxSpeed; } animator.SetFloat("Speed", Mathf.Clamp01(normalizedSpeed)); } } + void FindPlayerReference() + { + GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); + if (playerObj != null) + { + playerTransform = playerObj.transform; + playerAIPath = playerObj.GetComponent(); + if (playerAIPath != null) + { + playerMaxSpeed = playerAIPath.maxSpeed; + defaultFollowerMaxSpeed = playerMaxSpeed; + followerMaxSpeed = playerMaxSpeed * GameManager.Instance.FollowerSpeedMultiplier; + } + } + else + { + playerTransform = null; + playerAIPath = null; + } + } + // Command follower to go to a specific point (pathfinding mode) public void GoToPoint(Vector2 worldPosition) { @@ -213,11 +204,6 @@ public class FollowerController : MonoBehaviour } } - public delegate void FollowerPickupHandler(); - public event FollowerPickupHandler OnPickupArrived; - public event FollowerPickupHandler OnPickupReturned; - private Coroutine pickupCoroutine; - // Command follower to go to a specific point and return to player public void GoToPointAndReturn(Vector2 itemPosition, Transform playerTransform) { @@ -237,6 +223,16 @@ public class FollowerController : MonoBehaviour { heldObjectRenderer.sprite = currentlyHeldItem.mapSprite; heldObjectRenderer.enabled = true; + // Scale held icon to fixed height, preserve aspect ratio + var sprite = currentlyHeldItem.mapSprite; + float spriteHeight = sprite.bounds.size.y; + float spriteWidth = sprite.bounds.size.x; + if (spriteHeight > 0f) + { + float scaleY = heldIconDisplayHeight / spriteHeight; + float scaleX = scaleY * (spriteWidth / spriteHeight); + heldObjectRenderer.transform.localScale = new Vector3(scaleX, scaleY, 1f); + } } else { @@ -249,6 +245,7 @@ public class FollowerController : MonoBehaviour private System.Collections.IEnumerator PickupSequence(Vector2 itemPosition, Transform playerTransform) { isManualFollowing = false; + isReturningToPlayer = false; if (aiPath != null) { aiPath.enabled = true; @@ -256,7 +253,7 @@ public class FollowerController : MonoBehaviour aiPath.destination = new Vector3(itemPosition.x, itemPosition.y, 0); } // Wait until follower reaches item (2D distance) - while (Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(itemPosition.x, itemPosition.y)) > stopThreshold) + while (Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(itemPosition.x, itemPosition.y)) > GameManager.Instance.StopThreshold) { yield return null; } @@ -264,7 +261,6 @@ public class FollowerController : MonoBehaviour // Set held item and destroy pickup if (heldObjectRenderer != null) { - // Find Pickup object at itemPosition Collider2D[] hits = Physics2D.OverlapCircleAll(itemPosition, 0.2f); foreach (var hit in hits) { @@ -284,11 +280,13 @@ public class FollowerController : MonoBehaviour aiPath.maxSpeed = followerMaxSpeed; aiPath.destination = playerTransform.position; } + isReturningToPlayer = true; // Wait until follower returns to player (2D distance) - while (playerTransform != null && Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(playerTransform.position.x, playerTransform.position.y)) > stopThreshold) + while (playerTransform != null && Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(playerTransform.position.x, playerTransform.position.y)) > GameManager.Instance.StopThreshold) { yield return null; } + isReturningToPlayer = false; OnPickupReturned?.Invoke(); // Reset follower speed to normal after pickup followerMaxSpeed = defaultFollowerMaxSpeed; diff --git a/Assets/Scripts/GameManager.cs b/Assets/Scripts/GameManager.cs index 5786a779..ac198d3a 100644 --- a/Assets/Scripts/GameManager.cs +++ b/Assets/Scripts/GameManager.cs @@ -32,5 +32,16 @@ public class GameManager : MonoBehaviour public float PlayerStopDistance => gameSettings != null ? gameSettings.playerStopDistance : 1.0f; public float FollowerPickupDelay => gameSettings != null ? gameSettings.followerPickupDelay : 0.2f; + public float FollowDistance => gameSettings != null ? gameSettings.followDistance : 1.5f; + public float ManualMoveSmooth => gameSettings != null ? gameSettings.manualMoveSmooth : 8f; + public float ThresholdFar => gameSettings != null ? gameSettings.thresholdFar : 2.5f; + public float ThresholdNear => gameSettings != null ? gameSettings.thresholdNear : 0.5f; + public float StopThreshold => gameSettings != null ? gameSettings.stopThreshold : 0.1f; + public float MoveSpeed => gameSettings != null ? gameSettings.moveSpeed : 5f; + public float StopDistance => gameSettings != null ? gameSettings.stopDistance : 0.1f; + public bool UseRigidbody => gameSettings != null ? gameSettings.useRigidbody : true; + public float FollowUpdateInterval => gameSettings != null ? gameSettings.followUpdateInterval : 0.1f; + public float FollowerSpeedMultiplier => gameSettings != null ? gameSettings.followerSpeedMultiplier : 1.2f; + public float HeldIconDisplayHeight => gameSettings != null ? gameSettings.heldIconDisplayHeight : 2.0f; // Add more accessors as needed } diff --git a/Assets/Scripts/GameSettings.cs b/Assets/Scripts/GameSettings.cs index 0dcf98e6..08ae6465 100644 --- a/Assets/Scripts/GameSettings.cs +++ b/Assets/Scripts/GameSettings.cs @@ -6,6 +6,51 @@ public class GameSettings : ScriptableObject [Header("Interactions")] public float playerStopDistance = 6.0f; public float followerPickupDelay = 0.2f; - // Add other settings here as needed -} + [Header("Follower Settings")] + public float followDistance = 1.5f; + public float manualMoveSmooth = 8f; + public float thresholdFar = 2.5f; + public float thresholdNear = 0.5f; + public float stopThreshold = 0.1f; + + [Header("Player Settings")] + public float moveSpeed = 5f; + public float stopDistance = 0.1f; + public bool useRigidbody = true; + + [Header("Backend Settings")] + [Tooltip("Technical parameters, not for design tuning")] + public float followUpdateInterval = 0.1f; + public float followerSpeedMultiplier = 1.2f; + public float heldIconDisplayHeight = 2.0f; + + // Singleton pattern + private static GameSettings _instance; + public static GameSettings Instance { + get { + if (_instance == null) { + _instance = Resources.Load("GameSettings"); + if (_instance == null) { + Debug.LogError("GameSettings asset not found in Resources folder!"); + } + } + return _instance; + } + } + + // Static property wrappers for easy access + public static float PlayerStopDistance => Instance.playerStopDistance; + public static float FollowerPickupDelay => Instance.followerPickupDelay; + public static float FollowDistance => Instance.followDistance; + public static float ManualMoveSmooth => Instance.manualMoveSmooth; + public static float ThresholdFar => Instance.thresholdFar; + public static float ThresholdNear => Instance.thresholdNear; + public static float StopThreshold => Instance.stopThreshold; + public static float MoveSpeed => Instance.moveSpeed; + public static float StopDistance => Instance.stopDistance; + public static bool UseRigidbody => Instance.useRigidbody; + public static float FollowUpdateInterval => Instance.followUpdateInterval; + public static float FollowerSpeedMultiplier => Instance.followerSpeedMultiplier; + public static float HeldIconDisplayHeight => Instance.heldIconDisplayHeight; +} diff --git a/Assets/Scripts/Pickup.cs b/Assets/Scripts/Pickup.cs index 4562ff54..9c06f31a 100644 --- a/Assets/Scripts/Pickup.cs +++ b/Assets/Scripts/Pickup.cs @@ -38,12 +38,10 @@ public class Pickup : MonoBehaviour void OnDrawGizmos() { - // Get stop distance from GameManager or default - float playerStopDistance = GameManager.Instance != null ? GameManager.Instance.PlayerStopDistance : 1.0f; - // Draw stop distance circle around pickup + // Use GameManager for playerStopDistance + float playerStopDistance = GameManager.Instance.PlayerStopDistance; Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, playerStopDistance); - // Draw stop point (where player is told to move) GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) { @@ -58,8 +56,11 @@ public class Pickup : MonoBehaviour { if (itemData != null) { - if (iconRenderer != null) + if (iconRenderer != null && itemData.mapSprite != null) + { iconRenderer.sprite = itemData.mapSprite; + // Removed scaling logic, just set sprite + } gameObject.name = itemData.itemName; // Optionally update other fields, e.g. description } @@ -69,7 +70,6 @@ public class Pickup : MonoBehaviour { if (pickupInProgress) return; pickupInProgress = true; - // Find player and follower controllers var playerObj = GameObject.FindGameObjectWithTag("Player"); var followerObj = GameObject.FindGameObjectWithTag("Pulver"); if (playerObj == null || followerObj == null) @@ -86,20 +86,17 @@ public class Pickup : MonoBehaviour pickupInProgress = false; return; } - // Get settings from GameManager - float playerStopDistance = GameManager.Instance != null ? GameManager.Instance.PlayerStopDistance : 1.0f; - float followerPickupDelay = GameManager.Instance != null ? GameManager.Instance.FollowerPickupDelay : 0.2f; - // Subscribe to player arrival event + // Use GameManager for playerStopDistance and followerPickupDelay + float playerStopDistance = GameManager.Instance.PlayerStopDistance; + float followerPickupDelay = GameManager.Instance.FollowerPickupDelay; void OnPlayerArrived() { playerController.OnArrivedAtTarget -= OnPlayerArrived; - // After player arrives, dispatch follower after delay StartCoroutine(DispatchFollower()); } System.Collections.IEnumerator DispatchFollower() { yield return new WaitForSeconds(followerPickupDelay); - // Subscribe to follower events followerController.OnPickupArrived += OnFollowerArrived; followerController.OnPickupReturned += OnFollowerReturned; followerController.GoToPointAndReturn(transform.position, playerObj.transform); @@ -107,7 +104,6 @@ public class Pickup : MonoBehaviour void OnFollowerArrived() { followerController.OnPickupArrived -= OnFollowerArrived; - // Optionally: play pickup animation, etc. } void OnFollowerReturned() { diff --git a/Assets/Scripts/PlayerTouchController.cs b/Assets/Scripts/PlayerTouchController.cs index 484b0fec..1e1665b7 100644 --- a/Assets/Scripts/PlayerTouchController.cs +++ b/Assets/Scripts/PlayerTouchController.cs @@ -8,10 +8,6 @@ using Pathfinding; // Add this at the top // Attach to the player GameObject. Works with or without Rigidbody/Rigidbody2D. public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer { - public float moveSpeed = 5f; - public float stopDistance = 0.1f; - public bool useRigidbody = true; - Vector3 targetPosition; bool hasTarget = false; @@ -81,6 +77,7 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer if (aiPath != null) { aiPath.destination = targetPosition; + aiPath.maxSpeed = GameManager.Instance.MoveSpeed; Debug.Log($"AIPath destination set to {targetPosition}"); } else @@ -124,14 +121,14 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer if (aiPath != null) { aiPath.destination = target; + aiPath.maxSpeed = GameManager.Instance.MoveSpeed; } while (!interruptMoveTo) { - // 2D distance calculation (ignore z) Vector2 current2D = new Vector2(transform.position.x, transform.position.y); Vector2 target2D = new Vector2(target.x, target.y); float dist = Vector2.Distance(current2D, target2D); - if (dist <= stopDistance + 0.2f) + if (dist <= GameManager.Instance.StopDistance + 0.2f) { break; }