I need to interface with Xbox 360 controllers and my limited knowledge leaves me stuck with C#. I don't want to use XNA (and require users to have it installed) for this simple thing, so I'm using SlimDX to allow me to interface with XInput. I have been able to detect all button presses and the little program is coming along...but I need to know how to poll / know when a button is pressed, to then trigger events.
Right now I have a timer running at 10ms and it checks which buttons are pressed and behaves based on that. But let's say you press X for one second = it detects X was "pressed" 100 times because well, every 10ms, X was pressed. I want to fire up my events only when the button is pressed, and not have to depend on a timer.
Is it possible with SlimDX on C#? If so, how? If not, is there a better/lower latency XInput wrapper for C# that will do what I need?
Thanks in advance!
Create two variables to store the button's state between polls.
bool curretButtonState;//pressed == true
bool previousButtonState;
//poll each 10ms
void CheckInput()
{
currentButtonState = pollResult;
if(currentButtonState == true && previousButtonState == false)
{
//button just pressed, call code
}
if(currentButtonState== false && previousButtonState == true)//additional functionality if desired
{
//button just released, call other code
}
previousButtonState = currentButtonState;
}
Let's say it detects a button press and runs your appropriate code, then the next time it polls (10ms), since the previous state was toggled to true, and the button is still pressed, then both states will be true and it wont run the code the 2nd time. The 1st if statement will not be true again until the button is release and re-pressed.
Related
I'm using the new inptu system, and i want a way to see if an input action has been performed (only once, like GetButtonDown).
If I use ".ReadValue()", the if statement returns true not only once, but for as long as the "inventoryAction button" is held down.
private void Awake()
{
playerInput = GetComponent<PlayerInput>();
inventoryAction = playerInput.actions["Inventory"];
}
private void Update()
{
if (inventoryAction.ReadValue<float>() == 1)
{
Debug.Log("inventory key was pressed");
}
}
How do I only get the "performed" part in an if statement (with the new input system)? Is there something like GetButtonDown in the new input system?
Starting with version 1.1.1, actions have a WasPressedThisFrame method which basically does this.
The documentation(linked above) includes the following code snippet:
var fire = playerInput.actions["fire"];
if (fire.WasPressedThisFrame() && fire.IsPressed())
StartFiring();
else if (fire.WasReleasedThisFrame())
StopFiring();
As the code example shows, there are also WasReleasedThisFrame and IsPressed which are essentially GetButtonUp and GetButton, respectively.
Note that there are special considerations to make that make them behave differently than GetButtonX calls, especially where buttons release and press thresholds are substantially different, or where buttons are pressed and released multiple times in a frame.
1.1.1 IsPressed docs:
This method is different from simply reading the action's current float value and comparing it to the press threshold and is also different from comparing the current actuation of activeControl to it. This is because the current level of actuation might have already fallen below the press threshold but might not yet have reached the release threshold.
This method works with any type of action, not just buttons.
Also note that because this operates on the results of EvaluateMagnitude(), it works with many kind of controls, not just buttons. For example, if an action is bound to a StickControl, the control will be considered "pressed" once the magnitude of the Vector2 of the control has crossed the press threshold.
Finally, note that custom button press points of controls (see pressPoint) are respected and will take precedence over defaultButtonPressPoint.
1.1.1 WasPressedThisFrame docs:
This method is different from WasPerformedThisFrame() in that it is
not bound to phase. Instead, if the action's level of actuation (that
is, the level of magnitude -- see EvaluateMagnitude() -- of the
control(s) bound to the action) crossed the press threshold (see
defaultButtonPressPoint) at any point in the frame, this method will
return true. It will do so even if there is an interaction on the
action that has not yet performed the action in response to the press.
This method works with any type of action, not just buttons.
Also note that because this operates on the results of
EvaluateMagnitude(), it works with many kind of controls, not just
buttons. For example, if an action is bound to a StickControl, the
control will be considered "pressed" once the magnitude of the Vector2
of the control has crossed the press threshold.
Finally, note that custom button press points of controls (see
pressPoint) are respected and will take precedence over
defaultButtonPressPoint.
Be sure to read the documentation for your version for more information.
I've developed a UWP app that could potentially run on XBOX.
I want to detect whether the buttons on the gamepad controller have been pressed (either A B X or Y).
I think I need to be using the click event? If it is on the click event, what do I need to check in the click event?
Looking at this post which determines whether a trigger has been pressed..
Controller support for Xbox one in Windows UWP
/*
* Note: I have not tested this code.
* If it is incorrect, please do edit with the fix.
*/
using Windows.Gaming.Input;
// bla bla bla boring stuff...
// Get the first controller
var controller = Gamepad.Gamepads.First();
// Get the current state
var reading = controller.GetCurrentReading();
// Check to see if the left trigger was depressed about half way down
if (reading.LeftTrigger == 0.5;)
{
// Do whatever
}
I presume there is an equivalent way of checking whether one of the ABXY buttons have been pressed?. I shall check next time I get chance.
On another note, this blog post looks really useful for people starting out with developing UWP for Xbox One http://grogansoft.com/blog/?p=1278
Update :
Looks like I can call GetCurrentReading() to get a GamepadReading structure. and from that get the state of the GamepadButtons.
The KeyDown event from CoreWindow or any other UWP control will be fired even when user clicks the gamepad buttons. You can find values such as GamepadA and GamepadB in the VirtualKey enumeration so basic method for checking their pressing could look like this:
private void CoreWindow_KeyDown(CoreWindow sender, KeyEventArgs args)
{
if (args.Handled)
{
return;
}
switch (args.VirtualKey)
{
case VirtualKey.GamepadA:
// Gamepad A button was pressed
break;
case VirtualKey.GamepadB:
// Gamepad B button was pressed
break;
case VirtualKey.GamepadX:
// Gamepad X button was pressed
break;
case VirtualKey.GamepadY:
// Gamepad Y button was pressed
break;
}
}
You have to subscribe the event (for example in constructor):
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;
I am a beginner to the OOP and the C#.
I am working on a quiz game using the Windows Forms.
My problem is related to two classes, the form and the game logic.
I have a basic UI with classic Froms controls. Take a look.
The thing I want to achieve is, when a player presses any answer button, it will higlight that pressed button by red or green color, depending on if it is right or wrong answer. After changing the color I want the program to wait for a while and then go to the next question.
Probelm is, that I don´t know how to achieve this correctly. I don´t know how to work with threads and how exactly the Form app works related to threads. Should I use a thread sleep or a timer or a async?
I will show you the method in game logic class which should handle this.
public static void Play(char answer) //Method gets a char representing a palyer answer
{
if (_rightAnswer == answer) //If the answer is true, the button should become green
{
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.Color.LightGreen);
_score++;
}
else //Otherwise the button becomes Red
{
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.Color.Red);
}
//SLEEP HERE
if (!(_currentIndex < _maxIndex)) //If it is the last question, show game over
{
Program.MainWindow.DisplayGameOver(_score);
}
else //If it is not the last question, load next question and dispaly it and finally change the button color to default
{
_currentIndex++;
_currentQuestion = Database.ListOfQuestions.ElementAt(_currentIndex);
_rightAnswer = _currentQuestion.RightAnswer;
Program.MainWindow.DisplayStats(_score, _currentIndex + 1, _maxIndex + 1);
Program.MainWindow.DisplayQuestion(_currentQuestion.Text);
Program.MainWindow.DisplayChoices(_currentQuestion.Choices);
}
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.SystemColors.ControlLight);
}
I don´t want to completely block the UI but also I don´t want users to make other events by pressing other buttons during the pause. Because it will result in improper run of app.
If the program is really simple and you do not want to implement Threads I would suggest using Timer. Just start your Timer when clicking answer button. Your timer should contain function which would stop itself after some time and do other actions needed (e.g. pick another question).
Once the user has selected an answer you can disable all the buttons so they can't press anything else.
Then start a timer so you don't block the UI. The timer is basically a thread but handles all the threading for you so you don't have to worry about that aspect.
When the timer reaches the desired delay stop it and fire an event to select the next question.
At //SLEEP HERE add this line of code
Timer timer = new Timer(new TimerCallback(timerCb), null, 2000, 0);
The 2000 is milliseconds and is the wait time, timerCb is a call back method.
Also under that disable all your buttons so new events wont be generated.
private void timerCb(object state)
{
Dispatcher.Invoke(() =>
{
label1.Content = "Foo!";
});
}
You can do whatever you want in the callback, however if you do something that would change anything in the UI, you need to use the Dispatcher like I have changing the label content.
Suspending execution in a GUI scenario is very easy thanks to await:
await Task.Delay(2000);
This does not block the UI.
You should research what await does and how to use it. If you have never heard about it and are programming WinForms you are doing something wrong.
No timers or threads are needed. No callbacks, no Invoke.
I have a published app in the windows phone marketplace, which I'm trying to port to Win 8. I'm using Windows 8 Release Preview and Visual Studio Express RC 2012 for Win 8 and the code is C#-XAML.
I have created a custom 6x7 calendar. The first 7 buttons are put into the first StackPanel , the next into another panel and so forth. So there are 6 StackPanels holding 42 buttons. All these StackPanels are put into a Grid for the easy positioning.
Every button has is associated with a Holding EventHandler named OnLongPress. So the problem I'm facing is that when a button is pressed, the OnLongPress function is being called twice. On debugging I found that first time, the Holding state is Started and the next time it is called, the Holding state id Completed. I cannot figure out why it is being called twice.
Is it because the event is bubbled up?? :(
private void OnLongPress(object sender, HoldingRoutedEventArgs e)
{
Button butClicked = (Button)sender;
int iNumClicked = Convert.ToInt32(butClicked.Content.ToString());
CycleManager pCycMan = CycleManager.Instance;
string iVal, jVal;
int iRow, jCol;
string butName = butClicked.Name;
iVal = butName.Substring(1, 1);
jVal = butName.Substring(2, 1);
iRow = Convert.ToInt32(iVal);
jCol = Convert.ToInt32(jVal);
DateTime dtSelDate = new DateTime(m_yearBuffer[iRow, jCol], m_monthBuffer[iRow, jCol], iNumClicked);
int trackingStatus = pCycMan.IsDateOkForHistory(dtSelDate);
// setting or resetting few colors based on few checks
}
It would be helpful if someone can shed some light since I'm new to Win 8 dev.
I have solved the issue holding event being called twice, once on handling state is started and once on completed by including the following check. I'm still not sure if it is the right method.
if (e.HoldingState == Windows.UI.Input.HoldingState.Started)
If you just want the event to fire only once when holding state is complete or cancel, try to use RightTapped.
Holding is intended for informational UI, but for interactions like displaying a context menu you should use RightTapped instead. You might handle Holding first to display a hint that a menu will appear, but to display the menu itself, use a RightTapped handler. See Touch interaction design or Guidelines for common user interactions for more info on how to use a Hold interaction in your app design.
http://msdn.microsoft.com/en-us/library/windows.ui.xaml.uielement.holding.aspx
RightTapped for a touch action results from processing an action that remains in one place for a certain amount of time. If it's a touch action, a Holding event from the same element always precedes this, but RightTapped won't fire until the touch point is released. If the time the pointer is pressed is too short and Tapped fires instead of Holding, or if the Hold action ends with HoldingState as Canceled, RightTapped won't fire.
I have a block of code that repeats using a "for" loop, and each loop constructs a form to display some text. Some thing like the shorthand code below.
Main()
{
For (int x: x<=20; x++)
{
createform(string[x]);
}
}
So for each loop a different string is passed to a method that will construct a form as below.
createform void (string input_)
{
...
code to build form and add a button "cancelbutton"
form.text = intput_
....
form.cancelbutton.Click += // and I want this to cause the original loop to end....
}
No I know I could use the button to make int x greater than 20 and that would end the loop, but I don't actually know what the max value will be as this is dynamic. Again I could work this out and do he same thing but it seems a bit "messy".
Is there a neater way to cause the button click to exit the loop. How about if the Createform method is in a separate class to main, does that make any difference?
If your loop runs from 0 to 20 (or even over 9000) the user won't be able to click the cancel button in time. In fact, since it is all on one thread, the loop will finish before the UI responds to the click, but maybe I have misunderstood. Could you just have a boolean flag which you check each time you enter the loop and set it to false once the user clicks the button?
Just add a variable and code the click event:
static bool clicked;
Main()
{
clicked = false;
For (int x: (x<=20) && !clicked; x++)
{
createform(string[x]);
}
}
public static void Click_Detector(object sender, EventArgs e) {
clicked = true;
}
Your routine would need to do something like this:
createform void (string input_)
{
...
code to build form and add a button "cancelbutton"
form.text = intput_
....
form.cancelbutton.Click += MainClass.Click_Detector;
}
You shouldn't be using a loop to create each form. Assuming you have a submit button. Every time the user clicks submit, you should explicitly show the next form. That way, if a user clicks cancel, you don't have to worry about the rest of the forms.
The first solution that comes to my mind is: return value of the method True/False and in foreach check for its return value, if False=> break.
Your psuedo code isn't indicating how you are showing the form in CreateForm(). Is this going to be a modal or non modal? It would have to be modal, otherwise, you'd just keep creating forms until the cows come home. Remember, adding a handler to the click button doesn't actually execute the handler method until the click button is clicked.
Presumably your click event handler could set the value of x to be greater than 20, but since you said you're not sure what the maximum is, you could have a do loop governed by a boolean flag, and your createform() could set the state of the flag.
I'd recommend rethinking your problem space, as this overall approach seems really convoluted. I don't understand your UI, but it seems like your intent is to have the user control the exit of an infinite loop, like those old RPGs that ask you "Do you want to go on an adventure Yes/No", and it would continue asking you until you hit "Yes". These are pointless and confusing.
Add a boolean to the class and have the click event set the boolean value. Then you can break from the loop. However, if your real world use is the question above I would agree with the answer given by mikerobi.
One good solution would be to use the CancelAsync() method of the BackgroundWorker class: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
If you don't want to work with a separate thread to improve the performance, that I don't recommend, is to set a class level boolean value when the button clicked, then the for loop checks that boolean value in each next process.