Files

154 lines
5.1 KiB
C#

using UnityEngine;
namespace HotUpdate.Gameplay
{
/// <summary>
/// 营地伙伴闲逛控制器:在指定区域内随机漫步、呼吸待机,并在探险家靠近时好奇转身打招呼。
/// </summary>
public sealed class CampPetWanderer : MonoBehaviour
{
[Header("Wander Settings")]
public Vector3 wanderCenter;
public float wanderRadius = 2.8f;
public float moveSpeed = 1.6f;
public float turnSpeed = 480f;
public float stopDistance = 0.35f;
[Header("Timing")]
public float minIdleTime = 2.5f;
public float maxIdleTime = 6.0f;
[Header("Social")]
public float greetingDistance = 4.8f;
[Header("Model Facing Convention")]
[Tooltip("既有伙伴动物模型正面为局部 -Z(2026-10-24 之前旧资产规范),开启后使正面朝向移动方向")]
public bool modelFacesMinusZ = true;
Animator _animator;
Transform _playerTransform;
Vector3 _targetPosition;
bool _isMoving;
float _idleTimer;
int _animParamMotion;
public bool IsMoving => _isMoving;
public Vector3 TargetPosition => _targetPosition;
public Animator PetAnimator => _animator;
void Awake()
{
EnsureInit();
}
public void EnsureInit()
{
if (_animator == null) _animator = GetComponentInChildren<Animator>();
if (_animParamMotion == 0) _animParamMotion = Animator.StringToHash("Motion");
if (wanderCenter == Vector3.zero) wanderCenter = transform.position;
_targetPosition = transform.position;
if (_idleTimer <= 0f) _idleTimer = Random.Range(minIdleTime * 0.5f, maxIdleTime);
}
void Start()
{
var explorer = GameObject.Find("Explorer");
if (explorer != null) _playerTransform = explorer.transform;
}
/// <summary>
/// 指挥伙伴移动到指定目标点。
/// </summary>
public void MoveTo(Vector3 target)
{
EnsureInit();
_targetPosition = target;
_isMoving = true;
if (_animator != null) _animator.SetInteger(_animParamMotion, 1);
}
/// <summary>
/// 停步并进入待机状态。
/// </summary>
public void Stop()
{
EnsureInit();
_isMoving = false;
_targetPosition = transform.position;
_idleTimer = Random.Range(minIdleTime, maxIdleTime);
if (_animator != null) _animator.SetInteger(_animParamMotion, 0);
}
void Update()
{
float dt = Time.deltaTime;
// 探险家靠近时好奇注视
if (_playerTransform != null)
{
Vector3 toPlayer = _playerTransform.position - transform.position;
toPlayer.y = 0;
if (toPlayer.sqrMagnitude < greetingDistance * greetingDistance)
{
if (_isMoving) Stop();
if (toPlayer.sqrMagnitude > 0.001f)
{
Vector3 lookDir = modelFacesMinusZ ? -toPlayer.normalized : toPlayer.normalized;
Quaternion facePlayer = Quaternion.LookRotation(lookDir, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, facePlayer, turnSpeed * dt);
}
return;
}
}
if (_isMoving)
{
Vector3 toTarget = _targetPosition - transform.position;
toTarget.y = 0;
float dist = toTarget.magnitude;
if (dist <= stopDistance)
{
Stop();
return;
}
// 平滑转向并前进(结合模型正向规范)
if (toTarget.sqrMagnitude > 0.0001f)
{
Vector3 lookDir = modelFacesMinusZ ? -toTarget.normalized : toTarget.normalized;
Quaternion rot = Quaternion.LookRotation(lookDir, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, rot, turnSpeed * dt);
}
float step = Mathf.Min(moveSpeed * dt, dist);
transform.position += toTarget.normalized * step;
}
else
{
_idleTimer -= dt;
if (_idleTimer <= 0f)
{
PickNextDestination();
}
}
}
void PickNextDestination()
{
Vector2 rand2D = Random.insideUnitCircle * wanderRadius;
Vector3 dest = wanderCenter + new Vector3(rand2D.x, 0, rand2D.y);
// 避开喷泉中心
Vector2 fountainCenter = new Vector2(0f, 0.8f);
if (Vector2.Distance(new Vector2(dest.x, dest.z), fountainCenter) < 2.3f)
{
_idleTimer = 1.0f;
return;
}
MoveTo(dest);
}
}
}