Files
zeling_v2/Assets/_Game/Scripts/Camera/CameraPixelSnapper.cs
Joywayer b7baf7ad6a Add WeaponFeedback component and AddressableManagerWindow meta file
- 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.
2026-05-22 22:03:32 +08:00

123 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}
}