154 lines
5.1 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|