[Player][Interactions] Refactor common settings to be in the Game Settings SO. Update follow paramters and pathfinding for Pulver
This commit is contained in:
@@ -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<AIPath>();
|
||||
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<AIPath>();
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>("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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user