Ik zit al een middagje te worstelen met een probleem dat ik gewoon niet snap? Ik wil XInput gebruiken in mijn applicatie en de basis-dingen gaan goed: De buttons, de triggers en de analoge controls worden prima uitlezen. Ook heb ik een paar methodes gemaakt die buttons als analoge input kunnen verwerken en analoge inputs na een threshold ook als buttons/digitale knoppen. Dit gaat ook goed.
Dan probeer ik drie methodes te maken:
IsButtonJustPressed
IsButtonJustReleased
WasButtonHeldForMs
Echter werken deze niet goed wanneer ik ze achter elkaar gebruik. Hier de klasse met wat dingen eruit geknipt:
IsButtonPressed() en GetAnalogValue() werken beide:
Echter werken de methodes die veranderingen moeten herkennen niet goed:
Ik zie ook niet echt meer waar het fout gaat? Als ik één keer in de main-loop de knop check, gaat het goed, maar als ik de knop op meerdere dingen check, gaat het fout.
In mijn log, zie ik wanneer ik op X ram nooit "Nope" en "Button Pressed", enkel "Button Released". Als ik de twee aanroepingen omdraai (Dus Pressed voor Released) zie ik enkel "Button Pressed" in de log staan. Op het scherm verschijnt netjes A, X en/of LBD als ik op de knoppen druk en ze verdwijnen weer netjes wanneer ik de knoppen loslaat, idem met LBD als ik het pookje naar beneden duw en weer los laat. De veranderingen zijn echter half te zien in de log - namelijk dus de eerste die komt.
Ik snap het niet. De buttonState wordt enkel beschreven door controller->GetState().Gamepad.wButtons en wordt gewoon netjes meegestuurd als argument in plaats van dat die uit de klasse wordt gehaald, dus die zal geen onverwachte veranderingen krijgen. IsButtonPressed returnt gewoon een bool, dus zou per aanroep XboxButtonCurr[buttonType] gewoon keihard een waarde moeten geven. Elke wordt XboxButtonPrev[buttonType] = XboxButtonCurr[buttonType]; ook aangeroepen, dus de if (XboxButtonCurr[buttonType] && !XboxButtonPrev[buttonType]) { check zou meteen true moeten returnen bij een verandering, ook al -
Wacht even! Ik snap het nu. Tijdens het schrijven van de vorige zin begon er een lampje te branden.
Omdat beide methoden van dezelfde variabelen gebruik maken is natuurlijk de eerste functie goed, maar daarna krijgt meteen de tweede functie de geüpdatete "previous" en zijn curr en prev voor de tweede aangeroepene altijd hetzelfde
Nu ik dit snap, hoe pas ik dit aan? Elke functie zijn eigen previous/current (voor elke knop) bij laten houden lijkt me niet echt een goede oplossing, deze state variabelen in de hoofdklasse updaten lijkt me ook niet gewenst?
Edit - Ik heb even een snelle methode in elkaar geflanst om toch extern de dingen te updaten -
Deze wordt dus elke update() aangeroepen. Hoe vreselijk slecht is deze oplossing? Het werkt op zich goed, maar het ligt me toch niet lekker.
Dan probeer ik drie methodes te maken:
IsButtonJustPressed
IsButtonJustReleased
WasButtonHeldForMs
Echter werken deze niet goed wanneer ik ze achter elkaar gebruik. Hier de klasse met wat dingen eruit geknipt:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| /* XboxController.hpp */ class XboxController { public: enum XboxButtons { DpadUp, etc..., SIZEOF_XboxButtons }; int XboxButtonMasks[SIZEOF_XboxButtons] = { XINPUT_GAMEPAD_DPAD_UP, etc... }; private: XINPUT_STATE controllerState; int controllerNum; __int64 pressTime[SIZEOF_XboxButtons]; __int64 releaseTime[SIZEOF_XboxButtons]; public: XboxController(int playerNumber); XINPUT_STATE GetState(); bool IsConnected(); void Vibrate(int leftval = 0, int rightval = 0); bool IsButtonPressed(XboxButtons buttonType, WORD buttonState); bool IsButtonJustPressed(XboxButtons buttonType, WORD buttonState); bool IsButtonJustReleased(XboxButtons buttonType, WORD buttonState); bool WasButtonHeldForMs(XboxButtons buttonType, WORD buttonState, int milliseconds); bool XboxButtonCurr[SIZEOF_XboxButtons]; bool XboxButtonPrev[SIZEOF_XboxButtons]; // Helper function to convert setting readouts to enums. XboxButtons StringToButton(std::string buttonString); // Returns a 0.0 to 1.0 value for any button float GetAnalogValue(XboxButtons buttonType, WORD buttonState); }; |
IsButtonPressed() en GetAnalogValue() werken beide:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| bool XboxController::IsButtonPressed(XboxButtons buttonType, WORD buttonState) { if (buttonType == LeftTrigger || buttonType == RightTrigger || buttonType == LeftThumbLeft || buttonType == LeftThumbRight || buttonType == RightThumbLeft || buttonType == RightThumbRight || buttonType == LeftThumbUp || buttonType == LeftThumbDown || buttonType == RightThumbUp || buttonType == RightThumbDown) { return(GetAnalogValue(buttonType, buttonState) > 0.75f); } else { return (buttonState & XboxButtonMasks[buttonType]) != 0; } } float XboxController::GetAnalogValue(XboxButtons buttonType, WORD buttonState) { switch (buttonType) { case LeftTrigger: return (float)controllerState.Gamepad.bLeftTrigger / 255; case RightTrigger: return (float)controllerState.Gamepad.bRightTrigger / 255; case LeftThumbLeft: return fmaxf(0, -(float)controllerState.Gamepad.sThumbLX / 32767); case LeftThumbRight: return fmaxf(0, (float)controllerState.Gamepad.sThumbLX / 32767); case RightThumbLeft: return fmaxf(0, -(float)controllerState.Gamepad.sThumbRX / 32767); case RightThumbRight: return fmaxf(0, (float)controllerState.Gamepad.sThumbRX / 32767); case LeftThumbUp: return fmaxf(0, (float)controllerState.Gamepad.sThumbLY / 32767); case LeftThumbDown: return fmaxf(0, -(float)controllerState.Gamepad.sThumbLY / 32767); case RightThumbUp: return fmaxf(0, (float)controllerState.Gamepad.sThumbRY / 32767); case RightThumbDown: return fmaxf(0, -(float)controllerState.Gamepad.sThumbRY / 32767); default: return IsButtonPressed(buttonType, buttonState) ? 1.0f : 0.0f; } } |
Echter werken de methodes die veranderingen moeten herkennen niet goed:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| bool XboxController::IsButtonJustPressed(XboxButtons buttonType, WORD buttonState) { XboxButtonCurr[buttonType] = IsButtonPressed(buttonType, buttonState); // raising edge if (XboxButtonCurr[buttonType] && !XboxButtonPrev[buttonType]) { XboxButtonPrev[buttonType] = XboxButtonCurr[buttonType]; return true; } XboxButtonPrev[buttonType] = XboxButtonCurr[buttonType]; return false; } bool XboxController::IsButtonJustReleased(XboxButtons buttonType, WORD buttonState) { XboxButtonCurr[buttonType] = IsButtonPressed(buttonType, buttonState); // falling edge if (!XboxButtonCurr[buttonType] && XboxButtonPrev[buttonType]) { XboxButtonPrev[buttonType] = XboxButtonCurr[buttonType]; return true; } XboxButtonPrev[buttonType] = XboxButtonCurr[buttonType]; return false; } |
Ik zie ook niet echt meer waar het fout gaat? Als ik één keer in de main-loop de knop check, gaat het goed, maar als ik de knop op meerdere dingen check, gaat het fout.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| XboxController* controller = new XboxController(1); WORD buttonState; void update() { if (controller && controller->IsConnected()) { buttonState = controller->GetState().Gamepad.wButtons; } else { logger.Write("Nope"); } if (controller->IsButtonJustReleased(XboxController::X, buttonState)) { logger.Write("Button Released"); } if (controller->IsButtonJustPressed(XboxController::X, buttonState)) { logger.Write("Button Pressed"); } if (controller->IsButtonPressed(XboxController::A, buttonState)) { showText(0.4, 0.46, 5.0, "A"); } if (controller->IsButtonPressed(XboxController::X, buttonState)) { showText(0.4, 0.46, 5.0, "X"); } if (controller->IsButtonPressed(XboxController::LeftThumbDown, buttonState)) { showText(0.4, 0.46, 5.0, "LBD"); } } |
In mijn log, zie ik wanneer ik op X ram nooit "Nope" en "Button Pressed", enkel "Button Released". Als ik de twee aanroepingen omdraai (Dus Pressed voor Released) zie ik enkel "Button Pressed" in de log staan. Op het scherm verschijnt netjes A, X en/of LBD als ik op de knoppen druk en ze verdwijnen weer netjes wanneer ik de knoppen loslaat, idem met LBD als ik het pookje naar beneden duw en weer los laat. De veranderingen zijn echter half te zien in de log - namelijk dus de eerste die komt.
Ik snap het niet. De buttonState wordt enkel beschreven door controller->GetState().Gamepad.wButtons en wordt gewoon netjes meegestuurd als argument in plaats van dat die uit de klasse wordt gehaald, dus die zal geen onverwachte veranderingen krijgen. IsButtonPressed returnt gewoon een bool, dus zou per aanroep XboxButtonCurr[buttonType] gewoon keihard een waarde moeten geven. Elke wordt XboxButtonPrev[buttonType] = XboxButtonCurr[buttonType]; ook aangeroepen, dus de if (XboxButtonCurr[buttonType] && !XboxButtonPrev[buttonType]) { check zou meteen true moeten returnen bij een verandering, ook al -
Wacht even! Ik snap het nu. Tijdens het schrijven van de vorige zin begon er een lampje te branden.
Omdat beide methoden van dezelfde variabelen gebruik maken is natuurlijk de eerste functie goed, maar daarna krijgt meteen de tweede functie de geüpdatete "previous" en zijn curr en prev voor de tweede aangeroepene altijd hetzelfde
Nu ik dit snap, hoe pas ik dit aan? Elke functie zijn eigen previous/current (voor elke knop) bij laten houden lijkt me niet echt een goede oplossing, deze state variabelen in de hoofdklasse updaten lijkt me ook niet gewenst?
Edit - Ik heb even een snelle methode in elkaar geflanst om toch extern de dingen te updaten -
code:
1
2
3
4
5
6
| void XboxController::UpdateButtonChangeStates() { for (int i = 0; i < SIZEOF_XboxButtons; i++) { XboxButtonPrev[i] = XboxButtonCurr[i]; } } |
Deze wordt dus elke update() aangeroepen. Hoe vreselijk slecht is deze oplossing? Het werkt op zich goed, maar het ligt me toch niet lekker.