Survivalcraft API 1.8.2.3 v1.8.2.3
Survivalcraft 2.4
载入中...
搜索中...
未找到
GamePad.cs
浏览该文件的文档.
1#if ANDROID
2#pragma warning disable CA1416
3using System.Collections.Concurrent;
4using Android.Views;
5using Axis = Android.Views.Axis;
6#elif BROWSER
8#else
9using Silk.NET.Input;
10#endif
11namespace Engine.Input {
12 public static class GamePad {
13 internal class State {
14 // ReSharper disable MemberHidesStaticFromOuterClass
15#pragma warning disable CS0649
16 public bool IsConnected;
17#pragma warning restore CS0649
18 // ReSharper restore MemberHidesStaticFromOuterClass
19
20 public Vector2[] Sticks = new Vector2[2];
21
22 public float[] Triggers = new float[2];
23
24 public float[] LastTriggers = new float[2];
25
26 public bool[] Buttons = new bool[14];
27
28 public bool[] LastButtons = new bool[14];
29
30 public double[] ButtonsRepeat = new double[14];
31
32 public object ModifierKeyOfCurrentCombo; //记录按下的组合键中的修饰键,避免弹起误触
33 }
34#if ANDROID
35 public struct KeyInfo {
36 public int GamepadIndex;
37 public GamePadButton Button;
38 public bool Press;
39
40 public KeyInfo(int gamepadIndex, GamePadButton button, bool press) {
41 GamepadIndex = gamepadIndex;
42 Button = button;
43 Press = press;
44 }
45 }
46
47 public struct TriggerInfo {
48 public int GamepadIndex;
49 public GamePadTrigger Trigger;
50 public float Value;
51
52 public TriggerInfo(int gamepadIndex, GamePadTrigger trigger, float value) {
53 GamepadIndex = gamepadIndex;
54 Trigger = trigger;
55 Value = value;
56 }
57 }
58
59 public static Dictionary<int, int> m_deviceToIndex = [];
60 public static List<int> m_deviceToRemove = [];
61 public static ConcurrentQueue<KeyInfo> m_cachedKeyEvents = [];
62 public static ConcurrentQueue<TriggerInfo> m_cachedTriggerEvents = [];
63 public static readonly bool[,] m_lastDpadStates = new bool[4, 4];
64 static readonly bool[,] m_dpadFromKey = new bool[4, 4];
65 static readonly bool[,] m_lastTriggerDown = new bool[4, 2];
66
67 const float TRIGGER_DOWN_THRESHOLD = 0.5f;
68 const float TRIGGER_UP_THRESHOLD = 0.4f;
69#elif !IOS && !BROWSER
70 public static IReadOnlyList<IGamepad> m_gamepads;
71#endif
72 public static double m_buttonFirstRepeatTime = 0.2;
73
74 public static double m_buttonNextRepeatTime = 0.04;
75
76 internal static State[] m_states = [new(), new(), new(), new()];
77
78 internal static void Initialize() {
79#if !MOBILE && !BROWSER
81#endif
82 }
83
84 internal static void Dispose() { }
85
86#if ANDROID
87 internal static void BeforeFrame() {
88 if (Time.PeriodicEvent(2.0, 0.0)) {
89 m_deviceToRemove.Clear();
90 foreach (int key in m_deviceToIndex.Keys) {
91 if (InputDevice.GetDevice(key) == null) {
92 m_deviceToRemove.Add(key);
93 }
94 }
95 foreach (int item in m_deviceToRemove) {
96 Disconnect(item);
97 }
98 }
99 while (m_cachedKeyEvents.TryDequeue(out KeyInfo keyInfo)) {
100 if (keyInfo.Press) {
101 HandleKeyDown(keyInfo.GamepadIndex, keyInfo.Button);
102 }
103 else {
104 HandleKeyUp(keyInfo.GamepadIndex, keyInfo.Button);
105 }
106 }
107 while (m_cachedTriggerEvents.TryDequeue(out TriggerInfo info)) {
108 int num = TranslateDeviceId(info.GamepadIndex);
109 if (num >= 0) {
110 // 在这里更新 Triggers,此时它是当前帧的最新值
111 m_states[num].Triggers[(int)info.Trigger] = info.Value;
112 }
113 }
114 }
115
116 public static void HandleKeyEvent(KeyEvent e) {
117 m_cachedKeyEvents.Enqueue(new KeyInfo(TranslateDeviceId(e.DeviceId), TranslateKey(e.KeyCode), e.Action == KeyEventActions.Down));
118 }
119
120 internal static void HandleKeyDown(int gamepadIndex, GamePadButton gamePadButton) {
121 if (gamepadIndex < 0) {
122 return;
123 }
124 if (gamePadButton >= GamePadButton.A) {
125 if (gamePadButton >= GamePadButton.DPadLeft
126 && gamePadButton <= GamePadButton.DPadDown) {
127 int idx = gamePadButton switch {
128 GamePadButton.DPadLeft => 0,
129 GamePadButton.DPadRight => 1,
130 GamePadButton.DPadUp => 2,
131 GamePadButton.DPadDown => 3,
132 _ => throw new ArgumentOutOfRangeException()
133 };
134 m_dpadFromKey[gamepadIndex, idx] = true;
135 }
136 m_states[gamepadIndex].Buttons[(int)gamePadButton] = true;
137 switch (gamePadButton) {
138 case GamePadButton.LeftShoulder: m_states[gamepadIndex].Triggers[0] = 1f; break;
139 case GamePadButton.RightShoulder: m_states[gamepadIndex].Triggers[1] = 1f; break;
140 }
141 }
142 }
143
144 internal static void HandleKeyUp(int gamepadIndex, GamePadButton gamePadButton) {
145 if (gamepadIndex < 0) {
146 return;
147 }
148 if (gamePadButton >= GamePadButton.A) {
149 if (gamePadButton >= GamePadButton.DPadLeft
150 && gamePadButton <= GamePadButton.DPadDown) {
151 int idx = gamePadButton switch {
152 GamePadButton.DPadLeft => 0,
153 GamePadButton.DPadRight => 1,
154 GamePadButton.DPadUp => 2,
155 GamePadButton.DPadDown => 3,
156 _ => throw new ArgumentOutOfRangeException()
157 };
158 m_dpadFromKey[gamepadIndex, idx] = false;
159 }
160 m_states[gamepadIndex].Buttons[(int)gamePadButton] = false;
161 switch (gamePadButton) {
162 case GamePadButton.LeftShoulder: m_states[gamepadIndex].Triggers[0] = 0f; break;
163 case GamePadButton.RightShoulder: m_states[gamepadIndex].Triggers[1] = 0f; break;
164 }
165 }
166 }
167
168 internal static void HandleMotionEvent(MotionEvent e) {
169 int gamepadIndex = TranslateDeviceId(e.DeviceId);
170 if (gamepadIndex >= 0) {
171 m_states[gamepadIndex].Sticks[0] = new Vector2(e.GetAxisValue(Axis.X), 0f - e.GetAxisValue(Axis.Y));
172 m_states[gamepadIndex].Sticks[1] = new Vector2(e.GetAxisValue(Axis.Z), 0f - e.GetAxisValue(Axis.Rz));
173 float l = MathF.Max(e.GetAxisValue(Axis.Ltrigger), e.GetAxisValue(Axis.Brake));
174 float r = MathF.Max(e.GetAxisValue(Axis.Rtrigger), e.GetAxisValue(Axis.Gas));
175 m_cachedTriggerEvents.Enqueue(new TriggerInfo(TranslateDeviceId(e.DeviceId), GamePadTrigger.Left, l));
176 m_cachedTriggerEvents.Enqueue(new TriggerInfo(TranslateDeviceId(e.DeviceId), GamePadTrigger.Right, r));
177 float axisX = e.GetAxisValue(Axis.HatX);
178 float axisY = e.GetAxisValue(Axis.HatY);
179 ProcessDpad(gamepadIndex, gamepadIndex, 0, axisX < -0.5f, GamePadButton.DPadLeft);
180 ProcessDpad(gamepadIndex, gamepadIndex, 1, axisX > 0.5f, GamePadButton.DPadRight);
181 ProcessDpad(gamepadIndex, gamepadIndex, 2, axisY < -0.5f, GamePadButton.DPadUp);
182 ProcessDpad(gamepadIndex, gamepadIndex, 3, axisY > 0.5f, GamePadButton.DPadDown);
183 }
184 }
185
186 public static void ProcessDpad(int gamepadIndex, int padIndex, int dpadIndex, bool current, GamePadButton button) {
187 if (m_dpadFromKey[padIndex, dpadIndex]) {
188 return;
189 }
190 bool last = m_lastDpadStates[padIndex, dpadIndex];
191 if (current == last) {
192 return;
193 }
194 m_lastDpadStates[padIndex, dpadIndex] = current;
195 m_cachedKeyEvents.Enqueue(new KeyInfo(gamepadIndex, button, current));
196 }
197
198 public static int TranslateDeviceId(int deviceId) {
199 if (m_deviceToIndex.TryGetValue(deviceId, out int value)) {
200 return value;
201 }
202 for (int i = 0; i < 4; i++) {
203 if (!m_deviceToIndex.Values.Contains(i)) {
204 Connect(deviceId, i);
205 return i;
206 }
207 }
208 return -1;
209 }
210
211 public static GamePadButton TranslateKey(Keycode keyCode) => keyCode switch {
212 Keycode.ButtonA => GamePadButton.A,
213 Keycode.ButtonB => GamePadButton.B,
214 Keycode.ButtonX => GamePadButton.X,
215 Keycode.ButtonY => GamePadButton.Y,
216 Keycode.Back => GamePadButton.Back,
217 Keycode.ButtonL1 => GamePadButton.LeftShoulder,
218 Keycode.ButtonR1 => GamePadButton.RightShoulder,
219 Keycode.ButtonThumbl => GamePadButton.LeftThumb,
220 Keycode.ButtonThumbr => GamePadButton.RightThumb,
221 Keycode.DpadLeft => GamePadButton.DPadLeft,
222 Keycode.DpadRight => GamePadButton.DPadRight,
223 Keycode.DpadUp => GamePadButton.DPadUp,
224 Keycode.DpadDown => GamePadButton.DPadDown,
225 Keycode.ButtonSelect => GamePadButton.Back,
226 Keycode.ButtonStart => GamePadButton.Start,
227 _ => (GamePadButton)(-1)
228 };
229
230 public static void Connect(int deviceId, int index) {
231 m_deviceToIndex.Add(deviceId, index);
232 m_states[index].IsConnected = true;
233 }
234
235 public static void Disconnect(int deviceId) {
236 if (m_deviceToIndex.Remove(deviceId, out int value)) {
237 m_states[value].IsConnected = false;
238 }
239 }
240#elif IOS || BROWSER
241 internal static void BeforeFrame() {}
242#else
243 internal static void BeforeFrame() {
244 for (int padIndex = 0; padIndex < 4; padIndex++) {
245 if (padIndex >= m_gamepads.Count) {
246 break;
247 }
248 IGamepad gamepad = m_gamepads[padIndex];
249 if (gamepad == null) {
250 continue;
251 }
252 string name = gamepad.Name;
253 if (!name.Contains("Unmapped")) {
254 State state = m_states[padIndex];
255 if (gamepad.IsConnected) {
256 state.IsConnected = true;
257 if (Window.IsActive) {
258 IReadOnlyList<Thumbstick> thumbsticks = gamepad.Thumbsticks;
259 for (int i = 0; i < 2; i++) {
260 state.Sticks[i] = new Vector2(thumbsticks[i].X, -thumbsticks[i].Y);
261 }
262 IReadOnlyList<Trigger> triggers = gamepad.Triggers;
263 for (int i = 0; i < 2; i++) {
264 state.Triggers[i] = triggers[i].Position;
265 }
266 foreach (Button button in gamepad.Buttons) {
267 switch (button.Name) {
268 case ButtonName.A: state.Buttons[0] = button.Pressed; break;
269 case ButtonName.B: state.Buttons[1] = button.Pressed; break;
270 case ButtonName.X: state.Buttons[2] = button.Pressed; break;
271 case ButtonName.Y: state.Buttons[3] = button.Pressed; break;
272 case ButtonName.Back: state.Buttons[4] = button.Pressed; break;
273 case ButtonName.Start: state.Buttons[5] = button.Pressed; break;
274 case ButtonName.LeftStick: state.Buttons[6] = button.Pressed; break;
275 case ButtonName.RightStick: state.Buttons[7] = button.Pressed; break;
276 case ButtonName.LeftBumper: state.Buttons[8] = button.Pressed; break;
277 case ButtonName.RightBumper: state.Buttons[9] = button.Pressed; break;
278 case ButtonName.DPadLeft: state.Buttons[10] = button.Pressed; break;
279 case ButtonName.DPadRight: state.Buttons[12] = button.Pressed; break;
280 case ButtonName.DPadUp: state.Buttons[11] = button.Pressed; break;
281 case ButtonName.DPadDown: state.Buttons[13] = button.Pressed; break;
282 }
283 }
284 }
285 }
286 else {
287 state.IsConnected = false;
288 }
289 }
290 }
291 }
292#endif
293
294 public static bool IsConnected(int gamePadIndex) => gamePadIndex < 0 || gamePadIndex >= m_states.Length
295 ? throw new ArgumentOutOfRangeException(nameof(gamePadIndex))
296 : m_states[gamePadIndex].IsConnected;
297
298 public static Vector2 GetStickPosition(int gamePadIndex, GamePadStick stick, float deadZone = 0f) {
299 if (deadZone < 0f
300 || deadZone >= 1f) {
301 throw new ArgumentOutOfRangeException(nameof(deadZone));
302 }
303 if (IsConnected(gamePadIndex)) {
304 Vector2 result = m_states[gamePadIndex].Sticks[(int)stick];
305 if (deadZone > 0f) {
306 float num = result.Length();
307 if (num > 0f) {
308 float num2 = ApplyDeadZone(num, deadZone);
309 result *= num2 / num;
310 }
311 }
312 return result;
313 }
314 return Vector2.Zero;
315 }
316
317 public static float GetTriggerPosition(int gamePadIndex, GamePadTrigger trigger, float deadZone = 0f) => deadZone < 0f || deadZone >= 1f ?
318 throw new ArgumentOutOfRangeException(nameof(deadZone)) :
319 IsConnected(gamePadIndex) ? ApplyDeadZone(m_states[gamePadIndex].Triggers[(int)trigger], deadZone) : 0f;
320
321 public static bool IsTriggerDown(int gamePadIndex, GamePadTrigger trigger, float deadZone = 0f, float threshold = 0.5f) {
322 if (deadZone < 0f
323 || deadZone >= 1f) {
324 throw new ArgumentOutOfRangeException(nameof(deadZone));
325 }
326 if (!IsConnected(gamePadIndex)) {
327 return false;
328 }
329 float value = ApplyDeadZone(m_states[gamePadIndex].Triggers[(int)trigger], deadZone);
330 return value >= threshold;
331 }
332
333 public static bool IsTriggerDownOnce(int gamePadIndex, GamePadTrigger trigger, float deadZone = 0f, float threshold = 0.5f) {
334 if (deadZone < 0f
335 || deadZone >= 1f) {
336 throw new ArgumentOutOfRangeException(nameof(deadZone));
337 }
338 if (!IsConnected(gamePadIndex)) {
339 return false;
340 }
341 if (m_states[gamePadIndex].ModifierKeyOfCurrentCombo is GamePadTrigger trigger1
342 && trigger1 == trigger) {
343 return false; //若修饰键按下期间已触发组合键,禁止当前修饰键触发自己的点按行为,避免误触
344 }
345 bool current = ApplyDeadZone(m_states[gamePadIndex].Triggers[(int)trigger], deadZone) >= threshold;
346 bool last = ApplyDeadZone(m_states[gamePadIndex].LastTriggers[(int)trigger], deadZone) >= threshold;
347 return !current && last; //扳机必定是修饰键,松开那一刻才算按下一次,避免影响组合键
348 }
349
350 public static bool IsButtonDown(int gamePadIndex, GamePadButton button) =>
351 IsConnected(gamePadIndex) && m_states[gamePadIndex].Buttons[(int)button];
352
353 public static bool IsButtonDownOnce(int gamePadIndex, GamePadButton button) {
354 if (!IsConnected(gamePadIndex)) {
355 return false;
356 }
357 if (m_states[gamePadIndex].ModifierKeyOfCurrentCombo is GamePadButton button1
358 && button1 == button) {
359 return false; //若修饰键按下期间已触发组合键,禁止当前修饰键触发自己的点按行为,避免误触
360 }
361 if (IsModifierKey(button)) { //如果是修饰键,松开那一刻才算按下一次,避免影响组合键
362 return !m_states[gamePadIndex].Buttons[(int)button] && m_states[gamePadIndex].LastButtons[(int)button];
363 }
364 //正常按键依然是按下那一刻算按下一次
365 return m_states[gamePadIndex].Buttons[(int)button] && !m_states[gamePadIndex].LastButtons[(int)button];
366 }
367
368 public static bool IsButtonDownRepeat(int gamePadIndex, GamePadButton button) {
369 if (IsConnected(gamePadIndex)) {
370 if (m_states[gamePadIndex].Buttons[(int)button]
371 && !m_states[gamePadIndex].LastButtons[(int)button]) {
372 return true;
373 }
374 double num = m_states[gamePadIndex].ButtonsRepeat[(int)button];
375 return num != 0.0 && Time.FrameStartTime >= num;
376 }
377 return false;
378 }
379
380 public static bool IsAnyModifierKeyHolding(int gamePadIndex, float threshold = 0.5f) {
381 if (!IsConnected(gamePadIndex)) {
382 return false;
383 }
384 State state = m_states[gamePadIndex];
385 return state.Triggers[0] >= threshold
386 || state.Triggers[1] >= threshold
387 || state.Buttons[(int)GamePadButton.LeftShoulder]
388 || state.Buttons[(int)GamePadButton.RightShoulder];
389 }
390
391 public static void SetModifierKeyOfCurrentCombo(int gamePadIndex, object modifierKey) {
392 if (!IsConnected(gamePadIndex)) {
393 return;
394 }
395 if (IsModifierKey(modifierKey)) {
396 m_states[gamePadIndex].ModifierKeyOfCurrentCombo = modifierKey;
397 }
398 }
399
400 public static bool IsModifierKey(object obj) => obj is GamePadTrigger
401 || (obj is GamePadButton button && (button == GamePadButton.LeftShoulder || button == GamePadButton.RightShoulder));
402
403 // /// <summary>
404 // /// 使指定的手柄的马达震动
405 // /// </summary>
406 // /// <param name="gamePadIndex"></param>
407 // /// <param name="vibration">震动幅度(马达速度),在0到1之间</param>
408 // /// <param name="durationMs">震动持续时间(毫秒)</param>
409 // public static void MakeVibration(int gamePadIndex, float vibration, float durationMs)
410 // {//由于GLFW不支持手柄震动,所以暂时注释掉这段代码
411 //#if !ANDROID
412 // if (IsConnected(gamePadIndex))
413 // {
414 // var gamePad = m_gamepads[gamePadIndex];
415 // foreach(var motor in gamePad.VibrationMotors)
416 // {
417 // motor.Speed = vibration;
418 // Task.Delay((int)durationMs).ContinueWith(_ =>
419 // {
420 // motor.Speed = 0f;
421 // });
422 // }
423 // }
424 //#endif
425 // }
426 public static void Clear() {
427 for (int i = 0; i < m_states.Length; i++) {
428 for (int j = 0; j < m_states[i].Sticks.Length; j++) {
429 m_states[i].Sticks[j] = Vector2.Zero;
430 }
431 for (int k = 0; k < m_states[i].Triggers.Length; k++) {
432 m_states[i].Triggers[k] = 0f;
433 }
434 for (int l = 0; l < m_states[i].Buttons.Length; l++) {
435 m_states[i].Buttons[l] = false;
436 m_states[i].ButtonsRepeat[l] = 0.0;
437 }
438 }
439 }
440
441 internal static void AfterFrame() {
442 for (int i = 0; i < m_states.Length; i++) {
444 && IsButtonDownOnce(i, GamePadButton.Back)) {
445 Window.Close();
446 }
447 State state = m_states[i];
448 for (int j = 0; j < state.Buttons.Length; j++) {
449 if (state.Buttons[j]) {
450 if (!state.LastButtons[j]) {
452 }
453 else if (Time.FrameStartTime >= state.ButtonsRepeat[j]) {
455 }
456 }
457 else {
458 state.ButtonsRepeat[j] = 0.0;
459 }
460 state.LastButtons[j] = state.Buttons[j];
461 }
462 for (int k = 0; k < state.Triggers.Length; k++) {
463 state.LastTriggers[k] = state.Triggers[k];
464 }
465 if (!IsAnyModifierKeyHolding(i, 0.08f)) { //所有修饰键都松开时,重置组合键触发标记
466 state.ModifierKeyOfCurrentCombo = null;
467 }
468 }
469 }
470
471 public static float ApplyDeadZone(float value, float deadZone) =>
472 MathF.Sign(value) * MathF.Max(MathF.Abs(value) - deadZone, 0f) / (1f - deadZone);
473#if BROWSER
474 public static void GamepadConnectedHandler(int index, string name) {
475 if (index < 0 || index >= m_states.Length) {
476 return;
477 }
478 m_states[index].IsConnected = true;
479 }
480
481 public static void GamepadDisconnectedHandler(int index) {
482 if (index < 0 || index >= m_states.Length) {
483 return;
484 }
485 m_states[index].IsConnected = false;
486 }
487#endif
488 }
489}
static State[] m_states
static bool IsAnyModifierKeyHolding(int gamePadIndex, float threshold=0.5f)
static bool IsButtonDown(int gamePadIndex, GamePadButton button)
static bool IsTriggerDown(int gamePadIndex, GamePadTrigger trigger, float deadZone=0f, float threshold=0.5f)
static void BeforeFrame()
static bool IsModifierKey(object obj)
static bool IsButtonDownRepeat(int gamePadIndex, GamePadButton button)
static Vector2 GetStickPosition(int gamePadIndex, GamePadStick stick, float deadZone=0f)
static float GetTriggerPosition(int gamePadIndex, GamePadTrigger trigger, float deadZone=0f)
static bool IsButtonDownOnce(int gamePadIndex, GamePadButton button)
static void Dispose()
static void AfterFrame()
static void SetModifierKeyOfCurrentCombo(int gamePadIndex, object modifierKey)
static IReadOnlyList< IGamepad > m_gamepads
static double m_buttonFirstRepeatTime
static bool IsConnected(int gamePadIndex)
static void Initialize()
static double m_buttonNextRepeatTime
static float ApplyDeadZone(float value, float deadZone)
static bool IsTriggerDownOnce(int gamePadIndex, GamePadTrigger trigger, float deadZone=0f, float threshold=0.5f)
static bool BackButtonQuitsApp
static bool PeriodicEvent(double period, double offset)
定义 Time.cs:63
static double FrameStartTime
定义 Time.cs:42
static bool IsActive
static void Close()
static IInputContext m_inputContext
static readonly Vector2 Zero