Files
DuckovMods/CharacterPreview/ModelMove.cs

300 lines
11 KiB
C#
Raw Normal View History

using System;
using UnityEngine;
namespace CharacterPreview
{
2025-11-18 18:45:14 +08:00
public class ModelMove : MonoBehaviour
{
2025-11-18 18:45:14 +08:00
// 摄像机属性
private Camera _camera;
2025-11-18 18:45:14 +08:00
public Camera CurrentCamera
{
get
{
if (!_camera)
{
_camera = Camera.main;
if (!_camera)
{
_camera = FindObjectOfType<Camera>();
}
}
2025-11-18 18:45:14 +08:00
return _camera;
}
}
2025-11-18 18:45:14 +08:00
public Vector2 horizontalMoveRange = new Vector2(-1f, 2f); // X轴范围
public Vector2 verticalMoveRange = new Vector2(-2f, 2f); // Y轴范围
public Vector2 depthMoveRange = new Vector2(-1, 10f); // Z轴范围 (深度)
public Vector2 scaleRange = new Vector2(0.5f, 3f); // 缩放范围 (x=min, y=max)
// 初始状态变量
private Vector3 _initialScale = Vector3.one;
private Quaternion _initialRotation = Quaternion.identity;
// 模型相对于摄像机的初始局部位置偏移量
private Vector3 _initialCameraLocalOffset;
// 用于Input输入的控制器对象
private GameObject _controlModelMoveGameObject;
public bool CanFix => ModBehaviour.config.data.canEdit;
// 模型在摄像机局部空间中的默认位置偏移量 (用于Reset)
public static readonly Vector3 DefaultCameraLocalOffset = new Vector3(0.38f, -0.92f, 2.00f);
private void Awake()
{
EnsureControlModelMoveGameObject();
}
private void Start()
{
2025-11-18 18:45:14 +08:00
if (ModBehaviour.config.data.use)
{
ApplyConfig();
}
else
{
RefreshPosition();
}
}
private void Update()
{
// // 调试功能,按下 "-" 打印模型相对于摄像机的局部位置
// if (Input.GetKeyDown(KeyCode.Minus))
// {
// if (CurrentCamera)
// {
// var localPositionRelativeToCamera =
// CurrentCamera.transform.InverseTransformPoint(transform.position);
// Debug.Log($"模型相对于摄像机的局部位置: {localPositionRelativeToCamera}");
// }
// else
// {
// Debug.LogWarning("找不到摄像机,无法计算局部位置。模型的全局位置为: " + transform.position);
// }
// }
}
private void OnDestroy()
{
// 销毁时清理生成的控制器GameObject
if (_controlModelMoveGameObject)
{
Destroy(_controlModelMoveGameObject);
_controlModelMoveGameObject = null;
}
}
/// <summary>
/// 确保ControlModelMove GameObject存在并绑定脚本。
/// </summary>
private void EnsureControlModelMoveGameObject()
{
if (_controlModelMoveGameObject == null)
{
// 尝试查找场景中已有的ControlModelMove
_controlModelMoveGameObject = GameObject.Find("ControlModelMove");
if (_controlModelMoveGameObject == null)
{
// 如果没有,则创建新的
_controlModelMoveGameObject = new GameObject("ControlModelMove");
}
// 确保ControlModelMove组件已添加
if (_controlModelMoveGameObject.GetComponent<ControlModelMove>() == null)
{
_controlModelMoveGameObject.AddComponent<ControlModelMove>();
}
}
}
/// <summary>
/// 将模型朝向当前摄像机。
/// </summary>
public void LookAtCamera()
{
if(!CanFix)
return;
if (CurrentCamera)
{
transform.LookAt(CurrentCamera.transform);
}
else
{
Debug.LogWarning("ModelMove.LookAtCamera: 找不到摄像机,无法使模型朝向摄像机。");
}
SaveDataToConfig();
}
/// <summary>
/// 重置模型的位置、旋转和缩放
/// </summary>
public void RefreshPosition()
{
if(!CanFix)
return;
// 重置缩放和旋转到初始状态
transform.localScale = _initialScale;
transform.rotation = _initialRotation;
if (CurrentCamera)
{
2025-11-18 18:45:14 +08:00
// 计算模型在世界空间中相对于摄像机的默认位置
var worldPos = CameraLocalToWorld(CurrentCamera, DefaultCameraLocalOffset);
transform.position = worldPos;
2025-11-18 18:45:14 +08:00
// 使模型朝向摄像机
LookAtCamera();
// 记录模型重置后的相对于摄像机的局部偏移量,作为后续移动的基准
_initialCameraLocalOffset = CurrentCamera.transform.InverseTransformPoint(transform.position);
}
else
{
2025-11-18 18:45:14 +08:00
Debug.LogWarning("ModelMove.RefreshPosition: 找不到摄像机。模型将保持其当前世界位置,且相机相对移动功能将无法正常工作。");
_initialCameraLocalOffset = transform.position;
transform.position = new Vector3(8, 8, -16);
}
2025-11-18 18:45:14 +08:00
SaveDataToConfig();
}
/// <summary>
/// 移动模型
/// </summary>
/// <param name="shift">相对于当前总偏移量的位移增量(在摄像机局部坐标系下)</param>
public void Move(Vector3 shift)
{
if(!CanFix)
return;
if (!CurrentCamera)
{
Debug.LogWarning("ModelMove.Move: 找不到摄像机,无法执行相机相对移动。");
return;
}
var currentLocalPos = CurrentCamera.transform.InverseTransformPoint(transform.position);
var currentOffsetFromInitial = currentLocalPos - _initialCameraLocalOffset;
var targetOffsetFromInitial = currentOffsetFromInitial + shift;
targetOffsetFromInitial.x = Mathf.Clamp(targetOffsetFromInitial.x, horizontalMoveRange.x, horizontalMoveRange.y);
targetOffsetFromInitial.y = Mathf.Clamp(targetOffsetFromInitial.y, verticalMoveRange.x, verticalMoveRange.y);
targetOffsetFromInitial.z = Mathf.Clamp(targetOffsetFromInitial.z, depthMoveRange.x, depthMoveRange.y);
var newLocalPos = _initialCameraLocalOffset + targetOffsetFromInitial;
transform.position = CurrentCamera.transform.TransformPoint(newLocalPos);
SaveDataToConfig();
}
/// <summary>
/// 等比例缩放模型
/// </summary>
/// <param name="increment">缩放增量通常来自鼠标滚轮或UI滑块</param>
public void Scale(float increment)
{
if(!CanFix)
return;
// 假设是等比例缩放所以只取x轴的值来计算
var currentScale = transform.localScale.x;
var newScale = currentScale + increment;
// 将新的缩放值限制在预设范围内
newScale = Mathf.Clamp(newScale, scaleRange.x, scaleRange.y);
// 应用新的等比例缩放值
SaveDataToConfig();
transform.localScale = new Vector3(newScale, newScale, newScale);
}
/// <summary>
/// 旋转模型
/// </summary>
/// <param name="rotationAmount">旋转量,通常来自鼠标拖拽的偏移量(delta)</param>
public void Rotate(Vector2 rotationAmount)
{
if(!CanFix)
return;
// 左右拖拽rotationAmount.x围绕世界坐标的Y轴旋转使模型保持直立
transform.Rotate(Vector3.up, -rotationAmount.x * Time.deltaTime, Space.World);
if (CurrentCamera)
{
// 上下拖拽rotationAmount.y围绕摄像机的右方向轴旋转感觉更自然
transform.Rotate(CurrentCamera.transform.right, rotationAmount.y * Time.deltaTime, Space.World);
}
else
{
// 如果找不到相机则退而求其次围绕世界坐标的X轴旋转
Debug.LogWarning("ModelMove.Rotate: 找不到摄像机上下旋转将使用世界X轴。");
transform.Rotate(Vector3.right, rotationAmount.y * Time.deltaTime, Space.World);
}
SaveDataToConfig();
}
2025-11-18 18:45:14 +08:00
/// <summary>
2025-11-18 18:45:14 +08:00
/// 沿摄像机的Z轴朝向旋转模型实现“滚动”或“倾斜”效果。
/// </summary>
2025-11-18 18:45:14 +08:00
/// <param name="angle">要旋转的角度。正值通常为顺时针,负值为逆时针。</param>
public void RotateAroundCameraZ(float angle)
{
if(!CanFix)
return;
if (CurrentCamera == null)
{
Debug.LogWarning("ModelMove.RotateAroundCameraZ: 无法执行围绕相机Z轴的旋转因为找不到相机。");
return;
}
var rotationAxis = CurrentCamera.transform.forward;
transform.Rotate(rotationAxis, angle, Space.World);
SaveDataToConfig();
}
public void SaveDataToConfig()
{
var configData = ModBehaviour.config.data;
configData.modelPosition = CurrentCamera.transform.InverseTransformPoint(transform.position);
configData.modelRotation = transform.eulerAngles;
configData.modelScale = CurrentCamera.transform.localScale.x;
}
public void ApplyConfig()
{
var configData = ModBehaviour.config.data;
transform.position = CameraLocalToWorld(CurrentCamera, configData.modelPosition);
transform.eulerAngles = configData.modelRotation;
transform.localScale = new Vector3(configData.modelScale, configData.modelScale, configData.modelScale);
}
/// <summary>
/// 将摄像机局部空间中的点转换为世界空间中的点。
/// </summary>
/// <param name="camera">参考摄像机。</param>
/// <param name="localPoint">摄像机局部空间中的点。</param>
/// <returns>世界空间中的点。</returns>
public static Vector3 CameraLocalToWorld(Camera camera, Vector3 localPoint)
{
if (camera == null)
2025-11-18 18:45:14 +08:00
throw new ArgumentNullException(nameof(camera),
"Camera cannot be null for CameraLocalToWorld conversion.");
2025-11-18 18:45:14 +08:00
var camTransform = camera.transform;
return camTransform.position + camTransform.rotation * localPoint;
}
}
2025-11-18 18:45:14 +08:00
}