- Implemented WeaponFeedback class for handling weapon-related feedbacks such as hit effects and attack sounds. - Added meta file for AddressableManagerWindow to manage addressable assets. - Included a new jump.data file for profiler data.
123 lines
5.3 KiB
C#
123 lines
5.3 KiB
C#
using UnityEngine;
|
||
using Unity.Cinemachine;
|
||
|
||
namespace BaseGames.Camera
|
||
{
|
||
/// <summary>
|
||
/// 相机像素对齐(Pixel-perfect Snapping)。
|
||
///
|
||
/// 每帧在 CinemachineBrain LateUpdate 完成后,将 Unity 主相机的世界坐标
|
||
/// 四舍五入到最近的像素网格,消除像素艺术精灵的亚像素抖动(sub-pixel jitter)。
|
||
///
|
||
/// <para>
|
||
/// 挂载位置:Persistent 场景中与 <see cref="CinemachineBrain"/> 同一 GameObject([Camera] 节点)。
|
||
/// </para>
|
||
/// <para>
|
||
/// 执行顺序:+1000,确保在 CinemachineBrain(默认 -1000)的 LateUpdate 之后运行。
|
||
/// 直接修改 Unity Camera 的 <c>transform.position</c>,不影响 Cinemachine 内部状态。
|
||
/// </para>
|
||
/// <para>
|
||
/// 混合进行中时可选择暂停对齐(<see cref="_disableDuringBlend"/> = true),
|
||
/// 避免两台 VCam 插值结果在整数像素间跳变,产生阶梯感。
|
||
/// </para>
|
||
/// </summary>
|
||
[DefaultExecutionOrder(1000)]
|
||
[AddComponentMenu("BaseGames/Camera/Camera Pixel Snapper")]
|
||
[RequireComponent(typeof(UnityEngine.Camera))]
|
||
public class CameraPixelSnapper : MonoBehaviour
|
||
{
|
||
[Tooltip("每世界单位的像素数(PPU)。须与项目精灵的 Pixels Per Unit 设置一致。\n" +
|
||
"0 = 禁用像素对齐。常用值:16(粗像素)、32(标准)、100(高分辨率)。")]
|
||
[Min(0f)]
|
||
[SerializeField] private float _pixelsPerUnit = 16f;
|
||
|
||
[Tooltip("相机混合动画(Blend)进行中时暂停像素对齐,待混合结束后恢复。\n" +
|
||
"开启时混合动画更平滑(无阶梯感);关闭时混合期间也精确对齐但可能有轻微跳帧。\n" +
|
||
"推荐保持开启。")]
|
||
[SerializeField] private bool _disableDuringBlend = true;
|
||
|
||
[SerializeField] private CinemachineBrain _brain;
|
||
|
||
private UnityEngine.Camera _camera;
|
||
private CinemachineCamera _cachedVCam;
|
||
private CinemachineConfiner3D _cachedConfiner;
|
||
|
||
private void Reset()
|
||
{
|
||
_brain = GetComponent<CinemachineBrain>();
|
||
_camera = GetComponent<UnityEngine.Camera>();
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
_camera = GetComponent<UnityEngine.Camera>();
|
||
}
|
||
|
||
private void LateUpdate()
|
||
{
|
||
if (_pixelsPerUnit <= 0f || _camera == null) return;
|
||
|
||
// 混合进行中时跳过,避免插值位置在像素网格间产生阶梯感
|
||
if (_disableDuringBlend && _brain != null && _brain.IsBlending) return;
|
||
|
||
float ppu = _pixelsPerUnit;
|
||
Vector3 pos = _camera.transform.position;
|
||
pos.x = Mathf.Round(pos.x * ppu) / ppu;
|
||
pos.y = Mathf.Round(pos.y * ppu) / ppu;
|
||
// Z 轴保持不变(透视深度不需要对齐)
|
||
|
||
// 像素取整可能将相机推出 Confiner 边界(最多 0.5/PPU 的微小超出)。
|
||
// 对取整后的位置施加限位矩形钳制,确保不超出当前激活区域的 Confiner 边界。
|
||
if (TryGetActiveConfinerBounds(out Bounds confinerBounds))
|
||
{
|
||
pos.x = Mathf.Clamp(pos.x, confinerBounds.min.x, confinerBounds.max.x);
|
||
pos.y = Mathf.Clamp(pos.y, confinerBounds.min.y, confinerBounds.max.y);
|
||
}
|
||
|
||
_camera.transform.position = pos;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前活跃 VCam 的 CinemachineConfiner3D 边界盒(世界空间 AABB)。
|
||
/// 缓存上次查询的 VCam 实例;仅在活跃 VCam 发生切换时重新调用 GetComponent,
|
||
/// 避免每帧 GetComponent 开销。
|
||
/// </summary>
|
||
private bool TryGetActiveConfinerBounds(out Bounds bounds)
|
||
{
|
||
bounds = default;
|
||
if (_brain == null) return false;
|
||
var vcam = _brain.ActiveVirtualCamera as CinemachineCamera;
|
||
if (vcam == null) return false;
|
||
// 只在活跃 VCam 切换时刷新缓存
|
||
if (!ReferenceEquals(vcam, _cachedVCam))
|
||
{
|
||
_cachedVCam = vcam;
|
||
_cachedConfiner = vcam.GetComponent<CinemachineConfiner3D>();
|
||
}
|
||
if (_cachedConfiner == null || !_cachedConfiner.IsValid) return false;
|
||
bounds = _cachedConfiner.BoundingVolume.bounds;
|
||
return true;
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
/// <summary>编辑器模式下实时预览对齐效果。</summary>
|
||
private void OnDrawGizmosSelected()
|
||
{
|
||
if (_pixelsPerUnit <= 0f || _camera == null) return;
|
||
float ppu = _pixelsPerUnit;
|
||
float cellW = 1f / ppu;
|
||
// 在 Scene 视图中围绕相机位置绘制 5×5 像素网格示意(仅辅助调试用)
|
||
Gizmos.color = new Color(0.6f, 1f, 0.6f, 0.4f);
|
||
Vector3 origin = _camera.transform.position;
|
||
origin.x = Mathf.Floor(origin.x / cellW) * cellW;
|
||
origin.y = Mathf.Floor(origin.y / cellW) * cellW;
|
||
for (int ix = -2; ix <= 3; ix++)
|
||
for (int iy = -2; iy <= 3; iy++)
|
||
Gizmos.DrawWireCube(
|
||
new Vector3(origin.x + ix * cellW, origin.y + iy * cellW, 0f),
|
||
new Vector3(cellW, cellW, 0.01f));
|
||
}
|
||
#endif
|
||
}
|
||
}
|