Files
5.24BuYu/Assets/MyGame/aa/UI/Game/SeatMirror.cs
T

116 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.
// ============================================================
// 文件:SeatMirror.cs
// 职责:4人捕鱼桌座位镜像转换(玩家永远看自己在下方)
//
// 座位定义(服务端与场景一致):
// 0 = 左下 1 = 右下
// 2 = 左上 3 = 右上
//
// 镜像规则:
// 本地玩家分到上方座位(2/3)时,整个桌面上下翻转显示:
// 显示座位 = 服务端座位 XOR 2(0↔2、1↔3 对调),本地玩家落回下方。
// 本地玩家在下方座位(0/1)时不镜像,显示 = 服务端。
//
// 两个坐标空间(必须严格区分,跨界必经本类转换):
// - 服务端空间(server):网络协议字段 SyncGameData.sitPos / RoomJoinNotice.Position
// / players[].position,服务端只认这个空间。
// - 显示空间(display):场景座位 SitPosData.posID 及一切本地表现
// (炮台朝向 IsFlipped、锁定圈、金币飞、bot AI 等)。
// SitPosData.posID 永远是显示座位,场景预制体不用动。
//
// 收发转换口诀:
// 收到网络数据(含 sitPos/position)→ 先 ToDisplay 再找场景座位;
// 向服务端发数据(含 sitPos)→ 用 ToServer 把显示座位转回服务端座位。
// ============================================================
public static class SeatMirror
{
/// <summary>本地玩家在服务端的座位号(OnShow 收到 RoomJoinNotice 时 Setup;-1=未初始化/已离房)</summary>
public static int LocalServerPos { get; private set; } = -1;
/// <summary>是否镜像生效(本地分到上方座位 2/3)</summary>
public static bool IsMirrored => LocalServerPos >= 2;
/// <summary>
/// 进房时设置本地服务端座位号(决定是否镜像)。
/// GameMainPanel_FishBase.OnShow 解析出 RoomJoinNotice 后第一时间调用;
/// 切换房间会重新调用(新房间座位可能变化)。
/// </summary>
public static void Setup(int localServerPos)
{
LocalServerPos = localServerPos;
UnityEngine.Debug.Log($"[SeatMirror] 本地服务端座位={localServerPos} " +
(IsMirrored ? "→ 在上方,启用全桌上下镜像(0↔2,1↔3)" : "→ 在下方,不镜像"));
}
/// <summary>离房时清空(OnHide 调用),防止跨房间残留旧镜像状态</summary>
public static void Clear()
{
LocalServerPos = -1;
}
/// <summary>
/// 服务端座位 → 显示座位(本地视角)。
/// 用于:接收网络数据后定位场景座位、给玩家分配 SitPosData、RoomJoinNotice/RoomChange 的 position。
/// 越界值原样返回(防御:后端异常数据不误转换)。
/// </summary>
public static int ToDisplay(int serverPos)
{
if (serverPos < 0 || serverPos > 3 || !IsMirrored) return serverPos;
return serverPos ^ 2; // 0↔2、1↔3 上下对调
}
/// <summary>
/// 显示座位 → 服务端座位(发送网络数据时用)。
/// XOR 2 自反,与 ToDisplay 互逆。
/// </summary>
public static int ToServer(int displayPos)
{
return ToDisplay(displayPos);
}
// ================================================================
// 炮台角度镜像
// ================================================================
// 角度约定(与 SitPosData.SetRotation 一致的 aim-90 世界角):
// 0=朝上, 90=朝左, 180=朝下, 270=朝右(Unity z 轴逆时针为正)。
//
// 座位镜像是"上下翻转"(0↔2、1↔3 同侧对调),不是旋转 180°,
// 所以方向向量的变换是 Y 轴翻转:(dx,dy) → (dx,−dy),
// 对应角度变换:angle' = 180 − angle(自反,与座位 XOR 2 一样互逆):
// - 垂直瞄准时(0↔180)恰好等于 +180;
// - 斜向/横向瞄准时左右侧不反(若用 +180 会把左右瞄反)。
//
// 网络帧里的炮台角度统一用"源角度":发送方永远以"自己在下方座位"
// 的坐标系发送(0 = 朝桌面中心)。每个玩家进房后自己永远显示在下方
// 座位,本地算出的就是源角度;bot 由房主代算,先转成源角度再广播。
// 接收端按该座位在自己场景里的显示位置还原成世界角度。
// ================================================================
/// <summary>角度归一化到 [0,360)</summary>
public static float NormalizeAngle(float angle)
{
angle %= 360f;
if (angle < 0f) angle += 360f;
return angle;
}
/// <summary>
/// 源角度(网络帧,下方座位坐标系)→ 本地世界角度。
/// seatFlipped = 该座位在本端场景里的显示位置在上方(SitPosData.IsFlipped)。
/// </summary>
public static float SourceAngleToWorld(float sourceAngle, bool seatFlipped)
{
return seatFlipped ? NormalizeAngle(180f - sourceAngle) : sourceAngle;
}
/// <summary>
/// 本地世界角度 → 源角度(发送网络帧时用)。
/// seatFlipped = 计算该角度的座位显示位置在上方(bot 由房主代算时可能为 true)。
/// 180−angle 自反,与 SourceAngleToWorld 互逆。
/// </summary>
public static float WorldAngleToSource(float worldAngle, bool seatFlipped)
{
return seatFlipped ? NormalizeAngle(180f - worldAngle) : worldAngle;
}
}