4坐座位

This commit is contained in:
2026-09-27 18:02:59 +08:00
parent 1a36f70de4
commit 4bc30fef04
30 changed files with 232 additions and 61 deletions
@@ -1556,7 +1556,8 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
// ⭐ sitPos 用击杀者座位(含人机);房主代人机击杀 Boss 时,这里传人机座位,
// 否则所有客户端都会判定为房主击杀 → 房主自己看金色(错)
int posID = killerSitPos >= 0 ? killerSitPos : currGameType.currSitData.posID;
// ⭐ 座位镜像:网络帧里的 sitPos 必须是服务端座位号,显示座位转回服务端空间
int posID = SeatMirror.ToServer(killerSitPos >= 0 ? killerSitPos : currGameType.currSitData.posID);
var sgd = new SyncGameData
{
sitPos = posID,
@@ -1588,7 +1589,8 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
{
if (coinNum == null || coinNum.Count == 0) return;
int posID = killerSitPos >= 0 ? killerSitPos : currGameType.currSitData.posID;
// ⭐ 座位镜像:网络帧里的 sitPos 必须是服务端座位号,显示座位转回服务端空间
int posID = SeatMirror.ToServer(killerSitPos >= 0 ? killerSitPos : currGameType.currSitData.posID);
var sgd = new SyncGameData
{
sitPos = posID,
@@ -149,7 +149,8 @@ namespace Assets.MyGame.Scripts.Net
if (panel is GameMainPanel_FishBase fishPanel && notice != null)
{
// 如果座位被bot占用则替换
var targetSit = fishPanel.list_Sits.Find(s => s.posID == notice.Position);
// ⭐ 座位镜像:服务端座位转显示座位再找场景座位(JoinGame 内部也做同样转换)
var targetSit = fishPanel.list_Sits.Find(s => s.posID == SeatMirror.ToDisplay(notice.Position));
if (targetSit != null && targetSit.isBot)
{
targetSit.isBot = false;
@@ -1034,7 +1035,8 @@ namespace Assets.MyGame.Scripts.Net
}
if (panel == null) return;
var targetSit = panel.list_Sits.Find(s => s.posID == position);
// ⭐ 座位镜像:服务端座位转显示座位再找场景座位
var targetSit = panel.list_Sits.Find(s => s.posID == SeatMirror.ToDisplay(position));
if (targetSit == null) return;
if (showWaiting)
@@ -1112,7 +1114,8 @@ namespace Assets.MyGame.Scripts.Net
if (panel == null) continue; // 不在游戏中只补数据,进房流程自己会建座位
bool isBot = p.isBot || p.userId.StartsWith("bot_");
var targetSit = panel.list_Sits.Find(s => s.posID == p.position);
// ⭐ 座位镜像:服务端座位转显示座位再找场景座位(JoinGame 内部也做同样转换)
var targetSit = panel.list_Sits.Find(s => s.posID == SeatMirror.ToDisplay(p.position));
// 真人顶掉 bot 座位时先替换(与 RoomJoinNotice 分支同逻辑)
if (targetSit != null && targetSit.isBot && !isBot)
{
@@ -653,7 +653,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 810
depthOffset: 890
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -704,7 +704,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 1
ischild0: 1
depthOffset: 820
depthOffset: 880
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -710,7 +710,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 840
depthOffset: 860
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -698,7 +698,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: -850
depthOffset: 850
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -394,7 +394,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 1
ischild0: 1
depthOffset: 860
depthOffset: 840
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -426,7 +426,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 870
depthOffset: 770
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -908,7 +908,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 890
depthOffset: 810
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: 1461200f483c12642a09ef755696d8c4,
type: 3}
propertyPath: m_SortingOrder
value: -99
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: 1461200f483c12642a09ef755696d8c4, type: 3}
m_RemovedGameObjects:
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 0
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: 6f9ba87c0c298204597a3786e912d4f3,
type: 3}
propertyPath: m_SortingOrder
value: -100
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: 6f9ba87c0c298204597a3786e912d4f3, type: 3}
m_RemovedGameObjects:
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 0
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: b153e0a4abc0c664cb98ddf7afb53870,
type: 3}
propertyPath: m_SortingOrder
value: -98
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: b153e0a4abc0c664cb98ddf7afb53870, type: 3}
m_RemovedGameObjects:
@@ -283,6 +283,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 0
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: d0d8725bed789824bb1b0462fde12f45,
type: 3}
propertyPath: m_SortingOrder
value: -97
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: d0d8725bed789824bb1b0462fde12f45, type: 3}
m_RemovedGameObjects:
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 0
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: 51618a9fa649dc84bbbb0f2b2ae34548,
type: 3}
propertyPath: m_SortingOrder
value: -96
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: 51618a9fa649dc84bbbb0f2b2ae34548, type: 3}
m_RemovedGameObjects:
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 0
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: ee1e72c133ca6124485715190eaa1e3a,
type: 3}
propertyPath: m_SortingOrder
value: -95
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: ee1e72c133ca6124485715190eaa1e3a, type: 3}
m_RemovedGameObjects:
@@ -283,6 +283,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 0
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: 4760e9d2d74a347458314fcb191d6d8b,
type: 3}
propertyPath: m_SortingOrder
value: -94
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: 4760e9d2d74a347458314fcb191d6d8b, type: 3}
m_RemovedGameObjects:
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: 8aca3c1d421ff8e4c87619bd4511a615,
type: 3}
propertyPath: m_SortingOrder
value: -92
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: 8aca3c1d421ff8e4c87619bd4511a615, type: 3}
m_RemovedGameObjects:
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: 574ac90f80e643441b595de13ffc99fe,
type: 3}
propertyPath: m_SortingOrder
value: -90
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: 574ac90f80e643441b595de13ffc99fe, type: 3}
m_RemovedGameObjects:
@@ -289,7 +289,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 1000
depthOffset: 900
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: 10dcab870ce73d24abf17f9ea84712ad,
type: 3}
propertyPath: m_SortingOrder
value: -91
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: 10dcab870ce73d24abf17f9ea84712ad, type: 3}
m_RemovedGameObjects:
@@ -123,6 +123,11 @@ PrefabInstance:
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: 210099118695303065, guid: d441aa2f2c0f8524e9a494199140b726,
type: 3}
propertyPath: m_SortingOrder
value: -93
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 114884547391421755, guid: d441aa2f2c0f8524e9a494199140b726, type: 3}
m_RemovedGameObjects:
@@ -350,7 +350,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 830
depthOffset: 870
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -506,7 +506,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 880
depthOffset: 820
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -760,7 +760,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 900
depthOffset: 800
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -1099,7 +1099,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 910
depthOffset: 790
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -464,7 +464,7 @@ MonoBehaviour:
isRotate: 1
isNot1: 0
ischild0: 1
depthOffset: 920
depthOffset: 780
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
@@ -316,6 +316,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
RoomJoinNotice roomJoinNotice = v.arguments[0];
// ⭐ 座位镜像:进房第一时间确定本地服务端座位(2/3=上方 → 全桌上下镜像,本地永远看自己在下方)
SeatMirror.Setup(roomJoinNotice.Position);
// ⭐ paoBeiList 每次进入房间都重新根据当前场次+房间等级获取,
// 避免再次进入同一场次的不同等级房间时沿用旧 paoBeiList(例如先高级场后低级场)
{
@@ -356,7 +358,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
foreach (var item in list_Sits)
{
if (item.posID == roomJoinNotice.Position)
// ⭐ 座位镜像:本地玩家的显示座位 = 服务端座位转显示空间(上方座位翻转后落回下方)
if (item.posID == SeatMirror.ToDisplay(roomJoinNotice.Position))
{
currSitData = item;
// ⭐ 长按本地座位的 img_OpenPT 开关炮台面板(与 btn_ChangeTurret 同逻辑)
@@ -389,7 +392,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
}
else
{
var targetSit = list_Sits.Find(s => s.posID == roomJoinNotice.Position);
var targetSit = list_Sits.Find(s => s.posID == SeatMirror.ToDisplay(roomJoinNotice.Position));
// ⭐ 优先用后端 isBot 字段判断,兼容 userId.StartsWith("bot_")
bool isBotJoin = roomJoinNotice.IsBot ||
(!string.IsNullOrEmpty(roomJoinNotice.UserId) && roomJoinNotice.UserId.StartsWith("bot_"));
@@ -523,6 +526,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
// 清除场景加载标记,下次进入可重新加载
Assets.MyGame.Scripts.Net.WebSocketReponseList.ClearLoadedGameScene(sceneName);
WebSocketManager.Instance.Close(SocketType.game);
// ⭐ 座位镜像状态清空,下次进房重新按新房间座位 Setup
SeatMirror.Clear();
// ⭐ 重置本地道具计数,下次进入房间重新从服务端读取
_localBaoZouCount = -1;
_localBingFengCount = -1;
@@ -962,7 +967,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
// 同步给服务器:取消锁定
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.changeAutoTarget,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeAutoTarget { targetGuid = null }
@@ -1659,7 +1664,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
// 广播给其他客户端
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.biaoQing,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_BiaoQing
@@ -1696,11 +1701,13 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
/// <summary>收到其他玩家发送表情的广播(biaoQing):在对应座位显示</summary>
public void SyncGameData_BiaoQing(SyncGameData sync)
{
if (currSitData == null || sync.sitPos == currSitData.posID) return; // 本地座位已在发送时显示
// ⭐ 座位镜像:服务端座位转显示座位
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (currSitData == null || dispPos == currSitData.posID) return; // 本地座位已在发送时显示
var data = FrameDataParser.Parse<FrameData_BiaoQing>(sync.frameData);
if (data == null) return;
ShowBiaoQing(sync.sitPos, data.biaoQingType, data.spineIndex, data.content);
ShowBiaoQing(dispPos, data.biaoQingType, data.spineIndex, data.content);
}
#endregion
@@ -4026,7 +4033,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.rotateTurrect,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_Rotate { angle = angle }
@@ -4057,7 +4064,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.fireBullet,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = fireData
@@ -4093,7 +4100,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.changeAutoTarget,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeAutoTarget
@@ -4108,7 +4115,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.changeTurretState,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_TurretState { state = state }
@@ -4120,7 +4127,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.changeTurret,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeTurret { turretIndex = turretNumber }
@@ -4133,7 +4140,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.changeWing,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeTurret { turretIndex = wingNumber }
@@ -4147,7 +4154,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.changeZhanShen,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeTurret { turretIndex = zhanShenNumber }
@@ -4160,7 +4167,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.freezeFish,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_FreezeFish { isFreeze = isFreeze }
@@ -4174,9 +4181,11 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
#region ⑦ 服务器广播处理(NetReceiveModule)
public void SyncGameData_Rotate(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
// ⭐ 座位镜像:网络 sitPos 是服务端空间,先转显示座位再比较/索引
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos == currSitData.posID) return;
var sit = list_Sits[sync.sitPos];
var sit = list_Sits[dispPos];
// ⭐ 房主不处理机器人旋转广播:房主在 RotateBotToTarget 里已直接旋转本地炮台,
// 再走 SmoothRotate 会和本地直接旋转冲突
if (sit != null && sit.isBot && currSitData == SitPosData.roomOwnerSit) return;
@@ -4235,13 +4244,15 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
}
var data = FrameDataParser.Parse<FrameData_FireBullet>(sync.extra.frameData);
// ⭐ 座位镜像:extra.sitPos 是发送方的服务端座位,转显示座位再索引
// ⭐ 边界检查:防止 sitPos 越界导致 IndexOutOfRangeException
if (sync.extra.sitPos < 0 || sync.extra.sitPos >= list_Sits.Count)
int dispPos = SeatMirror.ToDisplay(sync.extra.sitPos);
if (dispPos < 0 || dispPos >= list_Sits.Count)
{
Debug.LogWarning($"[SyncGameData_FireBullit] sitPos 越界: {sync.extra.sitPos}");
return;
}
var targetSit = list_Sits[sync.extra.sitPos];
var targetSit = list_Sits[dispPos];
FireBullet(targetSit, data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing, sync.bulletIds, data.monsterIDs, bulletGolds);
}
@@ -4252,9 +4263,11 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_BotFireAngle(SyncGameData sync)
{
if (sync == null || sync.frameData == null) return;
if (sync.sitPos < 0 || sync.sitPos >= list_Sits.Count) return;
// ⭐ 座位镜像:bot 的服务端座位转显示座位
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos < 0 || dispPos >= list_Sits.Count) return;
var sit = list_Sits[sync.sitPos];
var sit = list_Sits[dispPos];
if (sit == null || !sit.isBot) return;
var fireData = FrameDataParser.Parse<FrameData_FireBullet>(sync.frameData);
@@ -4266,13 +4279,15 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_ChangeTargetMonster(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
// ⭐ 座位镜像:网络 sitPos 是服务端空间,先转显示座位再比较/索引
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeAutoTarget>(sync.frameData);
if (data.targetGuid == null)
{
list_Sits[sync.sitPos].ClearLock();
list_Sits[dispPos].ClearLock();
return;
}
@@ -4283,7 +4298,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
// colliderList 顺序可能不一致,统一用"第一个活跃"更稳定
Transform hitPos = ResolveLockTransform(monster);
list_Sits[sync.sitPos].SetLockTarget(monster, hitPos);
list_Sits[dispPos].SetLockTarget(monster, hitPos);
}
public void SyncGameData_RendromDieFish(SyncGameData sync)
@@ -4291,7 +4306,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
var data = JsonConvert.DeserializeObject<FrameData_RandomDieFish>(sync.frameData.ToString());
// 用 sync.sitPos 判断是否本地玩家发的(服务器不会丢这个字段)
bool isLocalSender = sync.sitPos == currSitData.posID;
// ⭐ 座位镜像:服务端座位转显示座位后再与本地显示座位比较
bool isLocalSender = SeatMirror.ToDisplay(sync.sitPos) == currSitData.posID;
CreateFishManager.Instance.Id_DieListFish_FX(
data.guids,
@@ -4305,52 +4321,62 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void SyncGameData_TurretState(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
// ⭐ 座位镜像:服务端座位转显示座位
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_TurretState>(sync.frameData);
var sit = list_Sits[sync.sitPos];
var sit = list_Sits[dispPos];
sit.ChangeState(data.state);
}
public void SyncGameData_ChangeTurret(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
// ⭐ 座位镜像:服务端座位转显示座位
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeTurret>(sync.frameData);
list_Sits[sync.sitPos].LoadTurret(data.turretIndex);
list_Sits[dispPos].LoadTurret(data.turretIndex);
}
public void SyncGameData_ChangeWing(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
// ⭐ 座位镜像:服务端座位转显示座位
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeTurret>(sync.frameData);
// turretIndex<=0 = 卸下翅膀;>0 = 加载对应编号翅膀(与战神 changeZhanShen 同一约定)
if (data.turretIndex > 0)
list_Sits[sync.sitPos].LoadWing(data.turretIndex);
list_Sits[dispPos].LoadWing(data.turretIndex);
else
list_Sits[sync.sitPos].ClearWing();
list_Sits[dispPos].ClearWing();
}
public void SyncGameData_ChangeZhanShen(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
// ⭐ 座位镜像:服务端座位转显示座位
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeTurret>(sync.frameData);
// turretIndex<=0 = 卸下战神;>0 = 加载对应编号战神(内部会隐藏该座位的普通炮台)
if (data.turretIndex > 0)
list_Sits[sync.sitPos].LoadZhanShen(data.turretIndex);
list_Sits[dispPos].LoadZhanShen(data.turretIndex);
else
list_Sits[sync.sitPos].ClearZhanShen();
list_Sits[dispPos].ClearZhanShen();
}
public void SyncGameData_ChangeCannonMultiple(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
// ⭐ 座位镜像:服务端座位转显示座位
int dispPos = SeatMirror.ToDisplay(sync.sitPos);
if (dispPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeCannonMultiple>(sync.frameData);
var sit = list_Sits[sync.sitPos];
var sit = list_Sits[dispPos];
sit.consumeGoldNum = data.cannonMultiple;
sit.tMP_TurretConsumeNum.text = data.cannonMultiple.ToString();
}
@@ -4397,7 +4423,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
if (currSitData == null) return;
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
sitPos = SeatMirror.ToServer(currSitData.posID),
frameType = FrameDataType.changeCannonMultiple,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeCannonMultiple { cannonMultiple = consumeGold }
@@ -4551,9 +4577,13 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public void JoinGame(int sitNum, long goldNum, long diamondNum, long consumeGold, int currentTurretIndex, string user = null, string cannonId = null, string wingId = null, string warGodId = null)
{
// ⭐ 座位镜像:调用方传的是服务端座位号(RoomJoinNotice.Position / players[].position),
// 转成本地视角的显示座位再匹配场景座位(本地在上方时 0↔2、1↔3 对调)
int displayPos = SeatMirror.ToDisplay(sitNum);
foreach (var sit in list_Sits)
{
if (sit.posID != sitNum) continue;
if (sit.posID != displayPos) continue;
// ⭐ 确保座位 GameObject 本身是激活的(可能被 HandleBotExit 隐藏或初始为 inactive)
if (!sit.gameObject.activeSelf)
@@ -4964,7 +4994,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
// 接收端 SyncGameData_ChangeCannonMultiple 会根据 sitPos 更新对应座位
var sgd = new SyncGameData
{
sitPos = sit.posID,
sitPos = SeatMirror.ToServer(sit.posID),
frameType = FrameDataType.changeCannonMultiple,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeCannonMultiple { cannonMultiple = (int)sit.consumeGoldNum }
@@ -5345,7 +5375,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
SyncGameData sgd = new SyncGameData
{
sitPos = botSit.posID,
sitPos = SeatMirror.ToServer(botSit.posID),
frameType = FrameDataType.rotateTurrect,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_Rotate { angle = angle }
@@ -5374,7 +5404,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
SyncGameData sgd = new SyncGameData
{
sitPos = botSit.posID,
sitPos = SeatMirror.ToServer(botSit.posID),
frameType = FrameDataType.changeAutoTarget,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeAutoTarget
@@ -5403,7 +5433,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
SyncGameData sgd = new SyncGameData
{
sitPos = botSit.posID,
sitPos = SeatMirror.ToServer(botSit.posID),
frameType = FrameDataType.changeTurretState,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_TurretState { state = state }
@@ -5429,7 +5459,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
SyncGameData sgd = new SyncGameData
{
sitPos = botSit.posID,
sitPos = SeatMirror.ToServer(botSit.posID),
frameType = FrameDataType.fireBullet,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = fireData
+70
View File
@@ -0,0 +1,70 @@
// ============================================================
// 文件: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);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c45f7e3e9e6ed784eb872e39cbbc62e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: