diff --git a/.gitignore b/.gitignore
index 7cc5f27..f3ddef2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@
/AxibugEmuOnline.Server/config.cfg
/AxibugEmuOnline.Server/bin/
/AxibugEmuOnline.Client/.editorconfig
+/AxibugEmuOnline.Client/*.user
+/AxibugEmuOnline.Client/.idea
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Event/EEvent.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Event/EEvent.cs
index 6f8fdf1..a16f100 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Event/EEvent.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Event/EEvent.cs
@@ -63,5 +63,10 @@
/// 服务器等待Step更新
///
OnRoomWaitStepChange,
+
+ ///
+ /// 当房间中手柄位信息发生任何变化时触发,进入房间后也应该触发
+ ///
+ OnRoomSlotDataChanged, //todo : 实现这个事件
}
}
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppEmu.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppEmu.cs
index 4896f54..f995068 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppEmu.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppEmu.cs
@@ -1,6 +1,7 @@
using AxibugEmuOnline.Client.ClientCore;
using AxibugEmuOnline.Client.Event;
using UnityEngine;
+using VirtualNes.Core;
namespace AxibugEmuOnline.Client.Manager
{
@@ -11,6 +12,9 @@ namespace AxibugEmuOnline.Client.Manager
/// 但是Equals方法可以,所以,这个接口判断为空请使用Equals
///
private IEmuCore m_emuCore;
+
+ private IControllerSetuper m_controllerSetuper;
+
///
/// unity的c#实现有bug,以接口类型保存的monobehaviour引用,!=和==运算符没有调用到monobehaviour重写过的运算符
/// 但是Equals方法可以,所以,这个接口判断为空请使用Equals
@@ -28,7 +32,7 @@ namespace AxibugEmuOnline.Client.Manager
if (!m_emuCore.IsNull()) StopGame();
var roomInfo = App.roomMgr.mineRoomMiniInfo;
- roomInfo.FetchRomFileInRoomInfo(EnumPlatform.NES, (room, romFile) =>
+ roomInfo.FetchRomFileInRoomInfo(EnumPlatform.NES, (_, romFile) =>
{
if (!romFile.RomReady) //这个rom并没有下载,所以取消进入房间
{
@@ -58,6 +62,35 @@ namespace AxibugEmuOnline.Client.Manager
InGameUI.Instance.Show(romFile, m_emuCore);
m_emuCore.SetupScheme();
+
+ m_controllerSetuper = Supporter.GetControllerSetuper();
+
+ SetupController();
+
+ Eventer.Instance.RegisterEvent(EEvent.OnRoomSlotDataChanged,OnSlotDataChanged);
+ }
+
+ private void OnSlotDataChanged()
+ {
+ SetupController();
+ }
+
+ private void SetupController()
+ {
+ if (!App.roomMgr.InRoom) //不在房间中,自动分配0号手柄到0号手柄位
+ {
+ m_controllerSetuper.SetConnect(con0ToSlot: 0);
+ }
+ else //在房间中则使用服务器下发的手柄槽位信息分配本地手柄
+ {
+ long selfUID = App.user.userdata.UID;
+ App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 0, out var con0Slot);
+ App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 1, out var con1Slot);
+ App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 2, out var con2Slot);
+ App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 3, out var con3Slot);
+
+ m_controllerSetuper.SetConnect(con0Slot, con1Slot, con2Slot, con3Slot);
+ }
}
public void StopGame()
@@ -68,6 +101,8 @@ namespace AxibugEmuOnline.Client.Manager
InGameUI.Instance.Hide();
LaunchUI.Instance.ShowMainMenu();
+ m_controllerSetuper = null;
+ Eventer.Instance.UnregisterEvent(EEvent.OnRoomSlotDataChanged,OnSlotDataChanged);
}
public void ResetGame()
@@ -77,4 +112,4 @@ namespace AxibugEmuOnline.Client.Manager
m_emuCore.DoReset();
}
}
-}
+}
\ No newline at end of file
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppRoom.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppRoom.cs
index 5f14db9..3085daa 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppRoom.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/AppRoom.cs
@@ -492,6 +492,26 @@ namespace AxibugEmuOnline.Client.Manager
return freeSlots.Length > 0;
}
+ ///
+ /// 指定uid和该uid的本地手柄序号,获取占用的手柄位
+ ///
+ public static bool GetPlayerSlotIdxByUid(this Protobuf_Room_MiniInfo roomMiniInfo, long uid ,int controllerIndex, out uint? slotID)
+ {
+ slotID = null;
+
+ //controllerIndex取值返回[0,3],这个序号代表玩家本地的手柄编号
+ //todo : 根据uid和controllerIndex 返回占用的位置
+
+ //目前未实现,所有非0号位置的手柄,都返回false
+ if (controllerIndex != 0) return false;
+
+ if (roomMiniInfo.Player1UID == uid) slotID = 0;
+ if (roomMiniInfo.Player2UID == uid) slotID = 1;
+ if (roomMiniInfo.Player3UID == uid) slotID = 2;
+ if (roomMiniInfo.Player4UID == uid) slotID = 3;
+ return true;
+ }
+
///
/// 按照房间玩家下标获取昵称
///
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/CoreSupporter.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/CoreSupporter.cs
index 437b14f..19c381d 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/CoreSupporter.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/CoreSupporter.cs
@@ -129,6 +129,11 @@ namespace AxibugEmuOnline.Client
}
}
+ public IControllerSetuper GetControllerSetuper()
+ {
+ return ControllerMapper;
+ }
+
public ControllerState FromNet(AxiReplay.ReplayStep step)
{
var temp = new ServerInputSnapShot();
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesControllerMapper.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesControllerMapper.cs
index 5a42fe7..742ac01 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesControllerMapper.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesControllerMapper.cs
@@ -3,77 +3,69 @@ using VirtualNes.Core;
namespace AxibugEmuOnline.Client
{
- public class NesControllerMapper
+ public class NesControllerMapper : IControllerSetuper
{
- public MapperSetter Player1 = new MapperSetter(1);
- public MapperSetter Player2 = new MapperSetter(2);
- public MapperSetter Player3 = new MapperSetter(3);
- public MapperSetter Player4 = new MapperSetter(4);
+ public Controller Controller0 { get; } = new(0);
+ public Controller Controller1 { get; } = new(1);
+ public Controller Controller2 { get; } = new(2);
+ public Controller Controller3 { get; } = new(3);
+
+ private readonly EnumButtonType[] m_states = new EnumButtonType[4];
public ControllerState CreateState()
{
- var state1 = Player1.GetButtons();
- var state2 = Player2.GetButtons();
- var state3 = Player3.GetButtons();
- var state4 = Player4.GetButtons();
+ m_states[0] = m_states[1] = m_states[2] = m_states[3] = 0;
- var result = new ControllerState(state1, state2, state3, state4);
+ if (Controller0.ConnectSlot.HasValue) m_states[Controller0.ConnectSlot.Value] = Controller0.GetButtons();
+ if (Controller1.ConnectSlot.HasValue) m_states[Controller1.ConnectSlot.Value] = Controller1.GetButtons();
+ if (Controller2.ConnectSlot.HasValue) m_states[Controller2.ConnectSlot.Value] = Controller2.GetButtons();
+ if (Controller3.ConnectSlot.HasValue) m_states[Controller3.ConnectSlot.Value] = Controller3.GetButtons();
+
+ var result = new ControllerState(m_states);
return result;
}
- public class Mapper
+ ///
+ /// Nes控制器
+ ///
+ public class Controller
{
- MapperSetter m_setter;
- EnumButtonType m_buttonType;
- IKeyListener m_keyListener;
- int m_controllerIndex => m_setter.ControllerIndex;
-
- public Mapper(MapperSetter setter, EnumButtonType buttonType)
- {
- m_setter = setter;
- m_buttonType = buttonType;
-
- loadConfig();
- }
-
- private void loadConfig()
- {
- m_keyListener = MapperSetter.GetKey(m_controllerIndex, m_buttonType);
-
- }
-
- public EnumButtonType SampleKey()
- {
- return m_keyListener.IsPressing() ? m_buttonType : 0;
- }
- }
-
- public class MapperSetter
- {
- /// 控制器序号(手柄1,2,3,4)
+ ///
+ /// 控制器编号
+ /// 此编号并非对应游戏中的player1,player2,player3,player4,仅仅作为本地4个手柄的实例
+ /// [0,3]
+ ///
public int ControllerIndex { get; }
- public Mapper UP { get; private set; }
- public Mapper DOWN { get; private set; }
- public Mapper LEFT { get; private set; }
- public Mapper RIGHT { get; private set; }
- public Mapper A { get; private set; }
- public Mapper B { get; private set; }
- public Mapper SELECT { get; private set; }
- public Mapper START { get; private set; }
- public Mapper MIC { get; private set; }
- public MapperSetter(int controllerIndex)
+ ///
+ /// 指示该手柄连接的手柄插槽
+ /// 这个值代表了该手柄在实际游戏中控制的Player
+ /// [0,3] 例外:为空代表未连接
+ ///
+ public uint? ConnectSlot { get; set; }
+
+ public Button UP { get; }
+ public Button DOWN { get; }
+ public Button LEFT { get; }
+ public Button RIGHT { get; }
+ public Button A { get; }
+ public Button B { get; }
+ public Button SELECT { get; }
+ public Button START { get; }
+ public Button MIC { get; }
+
+ public Controller(int controllerIndex)
{
ControllerIndex = controllerIndex;
- UP = new Mapper(this, EnumButtonType.UP);
- DOWN = new Mapper(this, EnumButtonType.DOWN);
- LEFT = new Mapper(this, EnumButtonType.LEFT);
- RIGHT = new Mapper(this, EnumButtonType.RIGHT);
- A = new Mapper(this, EnumButtonType.A);
- B = new Mapper(this, EnumButtonType.B);
- SELECT = new Mapper(this, EnumButtonType.SELECT);
- START = new Mapper(this, EnumButtonType.START);
- MIC = new Mapper(this, EnumButtonType.MIC);
+ UP = new Button(this, EnumButtonType.UP);
+ DOWN = new Button(this, EnumButtonType.DOWN);
+ LEFT = new Button(this, EnumButtonType.LEFT);
+ RIGHT = new Button(this, EnumButtonType.RIGHT);
+ A = new Button(this, EnumButtonType.A);
+ B = new Button(this, EnumButtonType.B);
+ SELECT = new Button(this, EnumButtonType.SELECT);
+ START = new Button(this, EnumButtonType.START);
+ MIC = new Button(this, EnumButtonType.MIC);
}
public EnumButtonType GetButtons()
@@ -93,7 +85,7 @@ namespace AxibugEmuOnline.Client
return res;
}
- public static IKeyListener GetKey(int controllerInput, EnumButtonType nesConBtnType)
+ public static KeyListener GetKey(int controllerInput, EnumButtonType nesConBtnType)
{
string configKey = $"NES_{controllerInput}_{nesConBtnType}";
if (PlayerPrefs.HasKey(configKey))
@@ -102,62 +94,60 @@ namespace AxibugEmuOnline.Client
}
else
{
- var defaultKeyCode = GetDefaultKey();
+ var defaultKeyCode = KeyListener.GetDefaultKey(controllerInput, nesConBtnType);
PlayerPrefs.SetString(configKey, defaultKeyCode.ToString());
return defaultKeyCode;
}
-
- KeyListener GetDefaultKey()
- {
- switch (controllerInput)
- {
- case 1:
- if (nesConBtnType == EnumButtonType.LEFT) return new KeyListener(KeyCode.A);
- if (nesConBtnType == EnumButtonType.RIGHT) return new KeyListener(KeyCode.D);
- if (nesConBtnType == EnumButtonType.UP) return new KeyListener(KeyCode.W);
- if (nesConBtnType == EnumButtonType.DOWN) return new KeyListener(KeyCode.S);
- if (nesConBtnType == EnumButtonType.START) return new KeyListener(KeyCode.B);
- if (nesConBtnType == EnumButtonType.SELECT) return new KeyListener(KeyCode.V);
- if (nesConBtnType == EnumButtonType.A) return new KeyListener(KeyCode.K);
- if (nesConBtnType == EnumButtonType.B) return new KeyListener(KeyCode.J);
- if (nesConBtnType == EnumButtonType.MIC) return new KeyListener(KeyCode.M);
- break;
- case 2:
- if (nesConBtnType == EnumButtonType.LEFT) return new KeyListener(KeyCode.Delete);
- if (nesConBtnType == EnumButtonType.RIGHT) return new KeyListener(KeyCode.PageDown);
- if (nesConBtnType == EnumButtonType.UP) return new KeyListener(KeyCode.Home);
- if (nesConBtnType == EnumButtonType.DOWN) return new KeyListener(KeyCode.End);
- if (nesConBtnType == EnumButtonType.START) return new KeyListener(KeyCode.PageUp);
- if (nesConBtnType == EnumButtonType.SELECT) return new KeyListener(KeyCode.Insert);
- if (nesConBtnType == EnumButtonType.A) return new KeyListener(KeyCode.Keypad5);
- if (nesConBtnType == EnumButtonType.B) return new KeyListener(KeyCode.Keypad4);
- if (nesConBtnType == EnumButtonType.MIC) return new KeyListener(KeyCode.KeypadPeriod);
- break;
- }
-
- return default;
- }
-
}
}
- public interface IKeyListener
+ ///
+ /// NES控制器按键类
+ ///
+ public class Button
{
- bool IsPressing();
+ /// 所属控制器
+ readonly Controller m_hostController;
+
+ /// 按键
+ readonly EnumButtonType m_buttonType;
+
+ /// 按键监听器
+ KeyListener m_keyListener;
+
+ public Button(Controller controller, EnumButtonType buttonType)
+ {
+ m_hostController = controller;
+ m_buttonType = buttonType;
+
+ CreateListener();
+ }
+
+ ///
+ /// 采集按钮按下状态
+ ///
+ ///
+ public EnumButtonType SampleKey()
+ {
+ return m_keyListener.IsPressing() ? m_buttonType : 0;
+ }
+
+ private void CreateListener()
+ {
+ m_keyListener = Controller.GetKey(m_hostController.ControllerIndex, m_buttonType);
+ }
}
- public struct KeyListener : IKeyListener
+ public readonly struct KeyListener
{
- private KeyCode m_key;
+ private readonly KeyCode m_key;
public KeyListener(KeyCode key)
{
m_key = key;
}
- ///
- /// 从配置表字符串构建
- ///
+ /// 从配置字符串构建
public KeyListener(string confStr)
{
m_key = KeyCode.None;
@@ -166,19 +156,83 @@ namespace AxibugEmuOnline.Client
m_key = (KeyCode)result;
}
- public readonly bool IsPressing()
+ public bool IsPressing()
{
- if (Input.GetKey(m_key)) return true;
-
- return false;
+ return Input.GetKey(m_key);
}
public override string ToString()
{
return ((int)(m_key)).ToString();
}
+
+ public static KeyListener GetDefaultKey(int controllerIndex, EnumButtonType nesConBtnType)
+ {
+ switch (controllerIndex)
+ {
+ case 1:
+ switch (nesConBtnType)
+ {
+ case EnumButtonType.LEFT:
+ return new KeyListener(KeyCode.A);
+ case EnumButtonType.RIGHT:
+ return new KeyListener(KeyCode.D);
+ case EnumButtonType.UP:
+ return new KeyListener(KeyCode.W);
+ case EnumButtonType.DOWN:
+ return new KeyListener(KeyCode.S);
+ case EnumButtonType.START:
+ return new KeyListener(KeyCode.B);
+ case EnumButtonType.SELECT:
+ return new KeyListener(KeyCode.V);
+ case EnumButtonType.A:
+ return new KeyListener(KeyCode.K);
+ case EnumButtonType.B:
+ return new KeyListener(KeyCode.J);
+ case EnumButtonType.MIC:
+ return new KeyListener(KeyCode.M);
+ }
+
+ break;
+ case 2:
+ switch (nesConBtnType)
+ {
+ case EnumButtonType.LEFT:
+ return new KeyListener(KeyCode.Delete);
+ case EnumButtonType.RIGHT:
+ return new KeyListener(KeyCode.PageDown);
+ case EnumButtonType.UP:
+ return new KeyListener(KeyCode.Home);
+ case EnumButtonType.DOWN:
+ return new KeyListener(KeyCode.End);
+ case EnumButtonType.START:
+ return new KeyListener(KeyCode.PageUp);
+ case EnumButtonType.SELECT:
+ return new KeyListener(KeyCode.Insert);
+ case EnumButtonType.A:
+ return new KeyListener(KeyCode.Keypad5);
+ case EnumButtonType.B:
+ return new KeyListener(KeyCode.Keypad4);
+ case EnumButtonType.MIC:
+ return new KeyListener(KeyCode.KeypadPeriod);
+ }
+
+ break;
+ }
+
+ return default;
+ }
+ }
+
+ public void SetConnect(uint? con0ToSlot = null,
+ uint? con1ToSlot = null,
+ uint? con2ToSlot = null,
+ uint? con3ToSlot = null)
+ {
+ Controller0.ConnectSlot = con0ToSlot;
+ Controller1.ConnectSlot = con1ToSlot;
+ Controller2.ConnectSlot = con2ToSlot;
+ Controller3.ConnectSlot = con3ToSlot;
}
}
-
-
-}
+}
\ No newline at end of file
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesEmulator.cs
index ace0654..085be65 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesEmulator.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesEmulator.cs
@@ -1,44 +1,61 @@
-using AxibugEmuOnline.Client.ClientCore;
-using System;
+using System;
using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Xml.Linq;
+using AxibugEmuOnline.Client.ClientCore;
+using UnityEditor;
using UnityEngine;
using VirtualNes.Core;
using VirtualNes.Core.Debug;
+using Debug = System.Diagnostics.Debug;
namespace AxibugEmuOnline.Client
{
public class NesEmulator : MonoBehaviour, IEmuCore
{
- public EnumPlatform Platform => EnumPlatform.NES;
-
- //ģʵ
+ public VideoProvider VideoProvider;
+ public AudioProvider AudioProvider;
+
+ //模拟器核心实例化对象
public NES NesCore { get; private set; }
- //ƵUnityģݵIJȾʵ֣
- public VideoProvider VideoProvider;
- //ƵUnityģƵݵIJųʵ֣
- public AudioProvider AudioProvider;
- //Ƿͣ
- private bool m_bPause;
- /// Ƿͣ
- public bool IsPause => m_bPause;
+ /// 是否暂停
+ public bool IsPause { get; private set; }
private void Start()
{
- //رմֱͬ
+ //关闭垂直同步
QualitySettings.vSyncCount = 0;
- //Ϊ60֡
+ //设为60帧
Application.targetFrameRate = 60;
VideoProvider.NesEmu = this;
AudioProvider.NesEmu = this;
}
///
- /// ָROMʼϷ
+ /// Unity的逐帧驱动
+ ///
+ private unsafe void Update()
+ {
+ if (IsPause) return;
+
+ if (NesCore != null)
+ {
+ PushEmulatorFrame();
+ if (InGameUI.Instance.IsNetPlay)
+ FixEmulatorFrame();
+
+ var screenBuffer = NesCore.ppu.GetScreenPtr();
+ VideoProvider.SetDrawData(screenBuffer);
+ }
+ }
+
+ public EnumPlatform Platform => EnumPlatform.NES;
+
+ ///
+ /// 指定ROM开始游戏
///
- ///
public void StartGame(RomFile rom)
{
StopGame();
@@ -59,78 +76,14 @@ namespace AxibugEmuOnline.Client
}
}
- ///
- /// ֹͣϷ
- ///
- public void StopGame()
- {
- NesCore?.Dispose();
- NesCore = null;
- }
-
- ///
- /// Unity֡
- ///
- private unsafe void Update()
- {
- if (m_bPause) return;
-
- if (NesCore != null)
- {
- PushEmulatorFrame();
- if (InGameUI.Instance.IsNetPlay)
- FixEmulatorFrame();
-
- var screenBuffer = NesCore.ppu.GetScreenPtr();
- VideoProvider.SetDrawData(screenBuffer);
- }
- }
-
- //Ƿ֡Ч
- private void FixEmulatorFrame()
- {
- var skipFrameCount = App.roomMgr.netReplay.GetSkipFrameCount();
-
- if (skipFrameCount > 0) App.log.Debug($"SKIP FRAME : {skipFrameCount}");
- for (int i = 0; i < skipFrameCount; i++)
- {
- if (!PushEmulatorFrame()) break;
- }
- }
-
- ControllerState lastState;
- //ƽ֡
- private bool PushEmulatorFrame()
- {
- Supporter.SampleInput(NesCore.FrameCount);
- var controlState = Supporter.GetControllerState();
-
- //δյInput,֡ƽ
- if (!controlState.valid) return false;
-
-#if UNITY_EDITOR
- if (controlState != lastState)
- {
- App.log.Info($"[LOCALDEBUG]{NesCore.FrameCount}-->{controlState}");
- }
-#endif
-
- NesCore.pad.Sync(controlState);
- NesCore.EmulateFrame(true);
-
- lastState = controlState;
-
- return true;
- }
-
public void Pause()
{
- m_bPause = true;
+ IsPause = true;
}
public void Resume()
{
- m_bPause = false;
+ IsPause = false;
}
@@ -155,7 +108,7 @@ namespace AxibugEmuOnline.Client
}
///
- /// ȡʱ浵
+ /// 获取即时存档
///
///
public byte[] GetStateBytes()
@@ -164,21 +117,68 @@ namespace AxibugEmuOnline.Client
}
///
- /// ؼʱ浵
+ /// 加载即时存档
///
- ///
+ ///
+ ///
public void LoadStateFromBytes(byte[] data)
{
- State st = new State();
+ var st = new State();
st.FromByte(data);
NesCore.LoadState(st);
}
public uint Frame => NesCore.FrameCount;
+ ///
+ /// 停止游戏
+ ///
+ public void StopGame()
+ {
+ NesCore?.Dispose();
+ NesCore = null;
+ }
+
+
+#if UNITY_EDITOR
+ private ControllerState m_lastState;
+#endif
+ //是否跳帧,单机无效
+ private void FixEmulatorFrame()
+ {
+ var skipFrameCount = App.roomMgr.netReplay.GetSkipFrameCount();
+
+ if (skipFrameCount > 0) App.log.Debug($"SKIP FRAME : {skipFrameCount}");
+ for (var i = 0; i < skipFrameCount; i++)
+ if (!PushEmulatorFrame())
+ break;
+ }
+
+ //推进帧
+ private bool PushEmulatorFrame()
+ {
+ Supporter.SampleInput(NesCore.FrameCount);
+ var controlState = Supporter.GetControllerState();
+
+ //如果未收到Input数据,核心帧不推进
+ if (!controlState.valid) return false;
+
+#if UNITY_EDITOR
+ if (controlState != m_lastState) App.log.Info($"[LOCALDEBUG]{NesCore.FrameCount}-->{controlState}");
+ m_lastState = controlState;
+#endif
+
+ NesCore.pad.Sync(controlState);
+ NesCore.EmulateFrame(true);
+
+
+ return true;
+ }
+
#if UNITY_EDITOR
///
- /// ༭
+ /// 编辑器用
///
[Conditional("UNITY_EDITOR")]
[ContextMenu("ImportNesDB")]
@@ -189,22 +189,23 @@ namespace AxibugEmuOnline.Client
var xmlStr = File.ReadAllText("nes20db.xml");
var xml = XDocument.Parse(xmlStr);
- var games = xml.Element("nes20db").Elements("game");
+ var games = xml.Element("nes20db")?.Elements("game");
+ Debug.Assert(games != null, nameof(games) + " != null");
foreach (var game in games)
{
- var crcStr = game.Element("rom").Attribute("crc32").Value;
- var crc = uint.Parse($"{crcStr}", System.Globalization.NumberStyles.HexNumber);
+ var crcStr = game.Element("rom")?.Attribute("crc32")?.Value;
+ var crc = uint.Parse($"{crcStr}", NumberStyles.HexNumber);
- var mapper = int.Parse($"{game.Element("pcb").Attribute("mapper").Value}");
+ var mapper = int.Parse($"{game.Element("pcb")?.Attribute("mapper")?.Value}");
if (mapper > 255) continue;
db.AddInfo(new RomDB.RomInfo { CRC = crc, Mapper = mapper });
}
- UnityEditor.EditorUtility.SetDirty(db);
- UnityEditor.AssetDatabase.SaveAssets();
+ EditorUtility.SetDirty(db);
+ AssetDatabase.SaveAssets();
}
#endif
}
-}
+}
\ No newline at end of file
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/GamesUI/RomItem.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/GamesUI/RomItem.cs
index 65a339d..e047221 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/GamesUI/RomItem.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/GamesUI/RomItem.cs
@@ -1,4 +1,4 @@
-using AxibugEmuOnline.Client.ClientCore;
+using AxibugEmuOnline.Client.ClientCore;
using AxibugEmuOnline.Client.UI;
using UnityEngine;
using UnityEngine.UI;
@@ -59,7 +59,7 @@ namespace AxibugEmuOnline.Client
{
if (!m_romfile.InfoReady)
{
- SetBaseInfo("ȡ", "---", "---");
+ SetBaseInfo("正在拉取", "---", "---");
}
else
{
@@ -82,8 +82,6 @@ namespace AxibugEmuOnline.Client
}
else
{
- //һûԼṩRomʱ,ʹ
- //App.emu.BeginGame(App.nesRomLib.GetExistRom("bad_apple_2_5.nes"));
App.emu.BeginGame(m_romfile);
return false;
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/InGameUI.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/InGameUI.cs
index 743ee71..697f4c3 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/InGameUI.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/InGameUI.cs
@@ -1,15 +1,21 @@
-using AxibugEmuOnline.Client.ClientCore;
+using System.Collections.Generic;
+using AxibugEmuOnline.Client.ClientCore;
using AxibugEmuOnline.Client.Event;
-using System;
-using System.Collections.Generic;
+using AxibugProtobuf;
namespace AxibugEmuOnline.Client
{
public class InGameUI : CommandExecuter
{
+ private bool m_delayCreateRoom;
+ private object m_state;
+ private StepPerformer m_stepPerformer;
+
+ private readonly List menus = new();
public static InGameUI Instance { get; private set; }
- public RomFile RomFile => m_rom;
+ public RomFile RomFile { get; private set; }
+
public override bool Enable => gameObject.activeInHierarchy;
/// 指示该游戏实例是否处于联机模式
@@ -19,18 +25,13 @@ namespace AxibugEmuOnline.Client
{
if (!App.user.IsLoggedIn) return false;
if (App.roomMgr.mineRoomMiniInfo == null) return false;
- if (App.roomMgr.RoomState <= AxibugProtobuf.RoomGameState.OnlyHost) return false;
+ if (App.roomMgr.RoomState <= RoomGameState.OnlyHost) return false;
return true;
}
}
- private RomFile m_rom;
public IEmuCore Core { get; private set; }
- private object m_state;
-
- private List menus = new List();
- private StepPerformer m_stepPerformer;
protected override void Awake()
{
@@ -59,36 +60,36 @@ namespace AxibugEmuOnline.Client
{
m_state = state;
}
+
///
- /// 读取快速快照
+ /// 读取快速快照
///
- ///
- ///
public object GetQuickState()
{
return m_state;
}
- private bool m_delayCreateRoom;
public void Show(RomFile currentRom, IEmuCore core)
{
m_delayCreateRoom = false;
- m_state = null;//清空游戏快照
+ m_state = null; //清空游戏快照
CommandDispatcher.Instance.RegistController(this);
- m_rom = currentRom;
+ RomFile = currentRom;
Core = core;
m_stepPerformer.Reset();
- if (!App.roomMgr.InRoom)
- {
- if (App.user.IsLoggedIn)
- App.roomMgr.SendCreateRoom(m_rom.ID, 0, m_rom.Hash);
- else
- {
- m_delayCreateRoom = true;
- OverlayManager.PopTip("稍后将会建立房间");
- }
+ if (!App.roomMgr.InRoom)
+ {
+ if (App.user.IsLoggedIn)
+ {
+ App.roomMgr.SendCreateRoom(RomFile.ID, 0, RomFile.Hash);
+ }
+ else
+ {
+ m_delayCreateRoom = true;
+ OverlayManager.PopTip("稍后将会建立房间");
+ }
}
Eventer.Instance.RegisterEvent(EEvent.OnLoginSucceed, OnLoggedIn);
@@ -106,21 +107,18 @@ namespace AxibugEmuOnline.Client
filter.ApplyPreset(preset);
App.filter.EnableFilter(filter);
}
- }
-
- private void OnRoomCreated()
- {
- m_delayCreateRoom = false;
- }
-
- private void OnLoggedIn()
- {
- if (m_delayCreateRoom)
- {
- App.roomMgr.SendCreateRoom(m_rom.ID, 0, m_rom.Hash);
- }
- }
-
+ }
+
+ private void OnRoomCreated()
+ {
+ m_delayCreateRoom = false;
+ }
+
+ private void OnLoggedIn()
+ {
+ if (m_delayCreateRoom) App.roomMgr.SendCreateRoom(RomFile.ID, 0, RomFile.Hash);
+ }
+
private void OnServerStepUpdate(int step)
{
m_stepPerformer.Perform(step);
@@ -138,16 +136,17 @@ namespace AxibugEmuOnline.Client
{
OverlayManager.PopSideBar(menus, 0, PopMenu_OnHide);
- if (!IsNetPlay)//单人模式暂停模拟器
+ if (!IsNetPlay) //单人模式暂停模拟器
Core.Pause();
}
//菜单关闭时候
private void PopMenu_OnHide()
{
- if (!IsNetPlay)//单人模式恢复模拟器的暂停
+ if (!IsNetPlay) //单人模式恢复模拟器的暂停
Core.Resume();
}
+
public void QuitGame()
{
Eventer.Instance.UnregisterEvent(EEvent.OnRoomWaitStepChange, OnServerStepUpdate);
@@ -157,4 +156,4 @@ namespace AxibugEmuOnline.Client
App.emu.StopGame();
}
}
-}
+}
\ No newline at end of file
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/StepPerformer.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/StepPerformer.cs
index 5a5372e..039790b 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/StepPerformer.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/InGameUI/StepPerformer.cs
@@ -6,7 +6,7 @@ namespace AxibugEmuOnline.Client
{
public class StepPerformer
{
- private InGameUI m_inGameUI;
+ private readonly InGameUI m_inGameUI;
private int m_step = -1;
public StepPerformer(InGameUI inGameUI)
diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/RoomUI/RoomItem.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/RoomUI/RoomItem.cs
index 63e312d..2766162 100644
--- a/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/RoomUI/RoomItem.cs
+++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/UI/RoomUI/RoomItem.cs
@@ -1,28 +1,25 @@
-using AxibugEmuOnline.Client.ClientCore;
+using AxibugEmuOnline.Client.ClientCore;
using AxibugEmuOnline.Client.Event;
using AxibugEmuOnline.Client.Manager;
using AxibugEmuOnline.Client.UI;
using AxibugProtobuf;
using UnityEngine;
using UnityEngine.UI;
+using Debug = System.Diagnostics.Debug;
namespace AxibugEmuOnline.Client
{
public class RoomItem : MenuItem, IVirtualItem
{
- [SerializeField]
- Image m_roomPreview;
- [SerializeField]
- Slider m_downloadProgress;
- [SerializeField]
- GameObject m_downloadingFlag;
- [SerializeField]
- GameObject m_romReadyFlag;
+ [SerializeField] Image m_roomPreview;
+ [SerializeField] Slider m_downloadProgress;
+ [SerializeField] GameObject m_downloadingFlag;
+ [SerializeField] GameObject m_romReadyFlag;
private RomFile m_romFile;
public int Index { get; set; }
- public int roomID { get; private set; }
+ public int RoomID { get; private set; }
protected override void Awake()
{
@@ -33,7 +30,7 @@ namespace AxibugEmuOnline.Client
private void OnRoomSignelUpdate(int roomID)
{
- if (this.roomID != roomID) return;
+ if (this.RoomID != roomID) return;
if (App.roomMgr.GetRoomListMiniInfo(roomID, out var roomInfo))
UpdateUI(roomInfo);
@@ -41,8 +38,10 @@ namespace AxibugEmuOnline.Client
public void SetData(object data)
{
- var roomInfo = data as Protobuf_Room_MiniInfo;
- roomID = roomInfo.RoomID;
+ Debug.Assert(data is Protobuf_Room_MiniInfo);
+
+ var roomInfo = (Protobuf_Room_MiniInfo)data;
+ RoomID = roomInfo.RoomID;
UpdateUI(roomInfo);
}
@@ -58,20 +57,19 @@ namespace AxibugEmuOnline.Client
}
else
{
- if (!App.roomMgr.GetRoomListMiniInfo(roomID, out Protobuf_Room_MiniInfo MiniInfo))
+ if (!App.roomMgr.GetRoomListMiniInfo(RoomID, out Protobuf_Room_MiniInfo MiniInfo))
{
- OverlayManager.PopTip("䲻");
+ OverlayManager.PopTip("房间不存在");
return false;
}
- int[] freeSlots = null;
- if (!MiniInfo.GetFreeSlot(out freeSlots))
+ if (!MiniInfo.GetFreeSlot(out var freeSlots))
{
- OverlayManager.PopTip("λ");
+ OverlayManager.PopTip("无空闲位置");
return false;
}
- App.roomMgr.SendJoinRoom(roomID, freeSlots[0]);
+ App.roomMgr.SendJoinRoom(RoomID, freeSlots[0]);
return true;
}
}
@@ -80,12 +78,12 @@ namespace AxibugEmuOnline.Client
{
var hostNick = roomInfo.GetHostNickName();
roomInfo.GetRoomPlayers(out var cur, out var max);
- SetBaseInfo("--", $"{hostNick}ķ", $"{cur}/{max}");
+ SetBaseInfo("--", $"{hostNick}的房间", $"{cur}/{max}");
SetIcon(null);
roomInfo.FetchRomFileInRoomInfo(EnumPlatform.NES, (room, romFile) =>
{
- if (room.RoomID != roomID) return;
+ if (room.RoomID != RoomID) return;
m_romFile = romFile;
Txt.text = romFile.Alias;
@@ -139,4 +137,4 @@ namespace AxibugEmuOnline.Client
Reset();
}
}
-}
+}
\ No newline at end of file
diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs
index d5e04fa..22b7bcc 100644
--- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs
+++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs
@@ -2,7 +2,7 @@
namespace VirtualNes.Core
{
- public struct ControllerState
+ public struct ControllerState : IEquatable
{
public uint raw0;
public uint raw1;
@@ -11,19 +11,49 @@ namespace VirtualNes.Core
public bool valid;
- public ControllerState(
- EnumButtonType player0_buttons,
- EnumButtonType player1_buttons,
- EnumButtonType player2_buttons,
- EnumButtonType player3_buttons)
+ public ControllerState(EnumButtonType[] states)
{
- raw0 = (uint)player0_buttons;
- raw1 = (uint)player1_buttons;
- raw2 = (uint)player2_buttons;
- raw3 = (uint)player3_buttons;
+ raw0 = (uint)states[0];
+ raw1 = (uint)states[1];
+ raw2 = (uint)states[2];
+ raw3 = (uint)states[3];
valid = true;
}
+
+ public bool HasButton(int player, EnumButtonType button)
+ {
+ uint raw = player switch
+ {
+ 0 => raw0,
+ 1 => raw1,
+ 2 => raw2,
+ 3 => raw3,
+ _ => 0
+ };
+ return (raw & (uint)button) == (uint)button;
+ }
+ public override string ToString()
+ {
+ return $"{raw0}|{raw1}|{raw2}|{raw3}";
+ }
+
+ #region Impl_Equals
+ public bool Equals(ControllerState other)
+ {
+ return raw0 == other.raw0 && raw1 == other.raw1 && raw2 == other.raw2 && raw3 == other.raw3 && valid == other.valid;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is ControllerState other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(raw0, raw1, raw2, raw3, valid);
+ }
+
public static bool operator ==(ControllerState left, ControllerState right)
{
return
@@ -37,24 +67,7 @@ namespace VirtualNes.Core
{
return !(left == right);
}
-
- public override string ToString()
- {
- return $"{raw0}|{raw1}|{raw2}|{raw3}";
- }
-
- public bool HasButton(int player, EnumButtonType button)
- {
- uint raw = 0;
- switch (player)
- {
- case 0: raw = raw0; break;
- case 1: raw = raw1; break;
- case 2: raw = raw2; break;
- case 3: raw = raw3; break;
- }
- return (raw & (uint)button) == (uint)button;
- }
+ #endregion
}
[Flags]
diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs
index 393210d..3701159 100644
--- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs
+++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs
@@ -5,6 +5,7 @@ namespace VirtualNes.Core
public static class Supporter
{
private static ISupporterImpl s_support;
+
public static void Setup(ISupporterImpl supporter)
{
s_support = supporter;
@@ -44,6 +45,7 @@ namespace VirtualNes.Core
{
s_support.SaveFile(fileData, directPath, fileName);
}
+
public static Stream OpenFile(string directPath, string fileName)
{
return s_support.OpenFile(directPath, fileName);
@@ -64,6 +66,11 @@ namespace VirtualNes.Core
s_support.SampleInput(frameCount);
}
+ public static IControllerSetuper GetControllerSetuper()
+ {
+ return s_support.GetControllerSetuper();
+ }
+
public static EmulatorConfig Config => s_support.Config;
}
@@ -82,5 +89,21 @@ namespace VirtualNes.Core
bool TryGetMapperNo(ROM rom, out int mapperNo);
ControllerState GetControllerState();
void SampleInput(uint frameCount);
+ IControllerSetuper GetControllerSetuper();
}
-}
+
+ ///
+ /// 负责管理本地控制器与具体游戏之间的槽位分配
+ ///
+ public interface IControllerSetuper
+ {
+ ///
+ /// 设置本地手柄与游戏手柄槽位的映射
+ ///
+ void SetConnect(
+ uint? con0ToSlot = null,
+ uint? con1ToSlot = null,
+ uint? con2ToSlot = null,
+ uint? con3ToSlot = null);
+ }
+}
\ No newline at end of file