I'm developing a kind of remote controller car.
I'm using a window CE device and Compact Framework 2.0 C#.
When Remote controller adjust the speed of car,
I'm using a sequential way like stairs.
However, It is sometimes going up and down 2 steps, 4 steps.
I would like to make only one step up and down from now step.
For example, now step is 4 then I just go to 3 or 5 not 8 or 1.
// Dealing with GPIO Input signal (detect push button)
private void smartGPIO1_EvtPortADatasChange(object sender, EventArgs e)
{
int iPortDatas;
SmartX.PORTDataEvtArgs PortDatas;
PortDatas = (SmartX.PORTDataEvtArgs)e;
iPortDatas = PortDatas.iPortDatas;
if (!test_start_Flag)
{
// User push the down button
if ((iPortDatas & 0x08) == 0x00)
{
motor_step = (motor_step <= -2) ? -2 : motor_step -= 1;
motor_handler(motor_step);
}
// User push the up button
if ((iPortDatas & 0x05) == 0x01)
{
motor_step = (motor_step >= 12) ? 12 : motor_step += 1;
motor_handler(motor_step);
}
}
}
// When button is pushed, send a command to car depends on the step
public int motor_handler(int step)
{
switch (step)
{
case 0:
// send command
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
case 9:
break;
case 10:
break;
case 11:
break;
case 12:
break;
break;
case -1:
break;
case -2:
break;
}
return step;
}
That code is for sure looped through on every update. Meaning that the single push of up or down button gets registered tens of times every second, so it seems like it goes more than one step at a time. You need to somehow register only new button presses.
//define something like this to the class level
Bool downIsPressed;
Bool upIsPressed;
//and then check and modify those values
if ((iPortDatas & 0x08) == 0x00 && downIsPressed == false)
{
motor_step = (motor_step <= -2) ? -2 : motor_step -= 1;
motor_handler(motor_step);
downIsPressed = true;
}
else
{
downIsPressed = false;
}
That should only register the button once and enable it once the button is released. There might be some cleaner and nicer looking way too, but hope that is of some help :)
Is it Recursive?
motor_handler(motor_step);
Calls private void smartGPIO1_EvtPortADatasChange(object sender, EventArgs e)
That's why each button push you get two steps.
The solution is a Private Boolean, typically I name IsRecursive:
class RacingCar {
private bool IsRecursive = false;
private void smartGPIO1_EvtPortADatasChange(object sender, EventArgs e)
{
if (IsRecursive) return;
IsRecursive = true;
....
IsRecursive = false;
}
}
Your code only ever increments or decrements the motor_step by 1 so if you're finding that the variable value jumps up by 2 etc then the code must have run twice in quick succession.
You have to find a way to "debounce" two events arriving at the same time. For example, when you step, record the current time. If the next event occurs less than one second since the previous event, ignore the event
//declare var and set to min value
private DateTime last_event_time = DateTime.MinValue;
private void smartGPIO1_EvtPortADatasChange(object sender, EventArgs e)
{
//debounce
if((DateTime.Now - last_event_time).TotalSeconds < 1)
return;
last_event_time = DateTime.Now;
int iPortDatas;
SmartX.PORTDataEvtArgs PortDatas;
Note: I've no idea if using DateTime will work on your platform, please take this code as pseudocode to demonstrate the concept
Debouncing like this will rely on the fact that the math on the time now vs time then needs to execute faster than events happen
If you want it to be so that step never changes as long as the button is held down you need to flip the logic around a bit so that as long as events keep arriving quickly, the code keeps returning - for that. You would update the time every time you return and only let the code proceed if more than X time has passed since the last event arrived
Related
I'm making a game. it has a login panel and rememmer me button. i keep the info on regedit. when I click rememmer me button it works and when I start game after click button it gets my info like id and pass but when I start again it deletes my id and pass on regedit. So it get just one time. I couldn't find the problem or fix way. Can you help me? Thank you.
Here is my codes:
void Start()
{
if(PlayerPrefs.GetInt("BeniHatirla")==1)
{
Debug.Log("start "+PlayerPrefs.GetInt("BeniHatirla"));
BeniHatirlaGetir();
}
}
Here is the method ı called in start:
public void BeniHatirlaGetir()
{
isim = PlayerPrefs.GetString("BeniHatirlaIsim");
sifre = PlayerPrefs.GetString("BeniHatirlaSifre");
Debug.Log("kullanici "+isim+sifre);
BeniHatirlaUniSlider.value = 1;
Debug.Log("Ogrenm Durumu"+PlayerPrefs.GetInt("OgrenimDurumuBelirleme"));
switch (PlayerPrefs.GetInt("OgrenimDurumuBelirleme"))
{
case 1:
OrtaOkulKadiTextBox.text = isim.ToString();
OrtaokulKsifreTextBox.text = sifre.ToString();
LoginPanelleri[0].SetActive(true);
break;
case 2:
LiseKadiTextBox.text = isim.ToString();
LiseKsifreTextBox.text = sifre.ToString();
LoginPanelleri[1].SetActive(true);
break;
case 3:
UniversiteKadiTextBox.text = isim.ToString();
UniversiteKsifreTextBox.text = sifre.ToString();
LoginPanelleri[2].SetActive(true);
break;
}
}
And here is the rememmer me button:
public void BeniHatırlaButon()
{
PlayerPrefs.SetInt("BeniHatirla", 1);
switch (PlayerPrefs.GetInt("OgrenimDurumuBelirleme"))
{
case 1:
PlayerPrefs.SetString("BeniHatirlaIsim", OrtaOkulKadiTextBox.text.ToString());
PlayerPrefs.SetString("BeniHatirlaSifre", OrtaokulKsifreTextBox.text.ToString());
break;
case 2:
PlayerPrefs.SetString("BeniHatirlaIsim", LiseKadiTextBox.text.ToString());
PlayerPrefs.SetString("BeniHatirlaSifre", LiseKsifreTextBox.text.ToString());
break;
case 3:
PlayerPrefs.SetString("BeniHatirlaIsim", UniversiteKadiTextBox.text.ToString());
PlayerPrefs.SetString("BeniHatirlaSifre", UniversiteKsifreTextBox.text.ToString());
break;
}
}
If you're running some debugger, it may be missing out on when it actually saves the values to disk. From the documentation for PlayerPrefs:
By default Unity writes preferences to disk during OnApplicationQuit(). In cases when the game crashes or otherwise prematuraly exits, you might want to write the PlayerPrefs at sensible 'checkpoints' in your game. This function will write to disk potentially causing a small hiccup, therefore it is not recommended to call during actual gameplay.
So if you are exiting the application prematurely through debugging it may be that you just need to call PlayerPrefs.Save() right after you try to write the values.
I'm a complete Newbie to the world of programming, yet I really wish to learn a lot as quick as possible and now came up to a problem that I can't find to solute just via researching and "learning- by doing" (Trying around).
Basically I'm trying to work on a small console- based TextAdventure in C Sharp (With VisualStudios) Now I came to a Case- Switch (Offering the User some Options to read and walk through), but I wish to add a Y/N Confirmation in case the User decides to take a different path. For now it's only for the starting point of the story:
Does the User want to go into "The Wilds", "The City", "The Farm". Something as simple as that just in addition: "Are you sure (Y/N)?" leading the No to return the given choices.
Thank you all in advance and stay healthy!
Menu mainMenu = new Menu(prompt, options);
int selectedIndex = mainMenu.Run();
switch (selectedIndex)
{
case 0:
EnterTheWorld();
break;
case 1:
VisitTheMemorial();
break;
case 2:
TakeYourLeave();
break;
default:
break;
}
}
private void TakeYourLeave()
{
WriteLine("\nYou are about to take your leave... Are you sure ? (Y/N)");
ReadKey();
Environment.Exit(0);
}
private void VisitTheMemorial()
{
Clear();
//FILLER//
WriteLine("You may proceed by the press of any button.");
ReadKey(true);
RunMainMenu();
}
private void EnterTheWorld()
{
string prompt = "Where would you like to start your journey?";
string[] options = { "The Slums", "The Wilds", "The City", "The Farm" };
Menu startMenu = new Menu(prompt, options);
int selectedIndex = startMenu.Run();
BackgroundColor = ConsoleColor.White;
switch (selectedIndex)
{
case 0:
ForegroundColor = ConsoleColor.Black;
WriteLine("\n ||Small Description||Are you sure to take this path? (Y/N)");
break;
case 1:
ForegroundColor = ConsoleColor.Green;
WriteLine("\n ||Small Description||Are you sure to take this path? (Y/N)");
break;
case 2:
ForegroundColor = ConsoleColor.Red;
WriteLine("\n ||Small Description||Are you sure to take this path? (Y/N)");
break;
case 3:
ForegroundColor = ConsoleColor.Yellow;
WriteLine("\n ||Small Description|| Are you sure to take this path? (Y/N)");
break;
}
In this case, you need to take care of a few things:
Make sure that the entire switch-statement logic is in a while-loop, as such:
while (True) {
int selectedIndex = mainMenu.Run();
switch (selectedIndex)
{
case 0:
EnterTheWorld();
break;
case 1:
VisitTheMemorial();
break;
case 2:
TakeYourLeave();
break;
default:
break;
}
}
Check with your function if the input given is "No", if such, then immeditely return so your function exits before any navigation
private void VisitTheMemorial()
{
userInput = readKeyFunction();
if (userInput == "no") {
return;
}}
Finally, if the user doesn't select no, just do nothing and let the function go on
It is best to handle the 'Are you sure?' inside the menu.Run(), and return a value to the selectedIndex after the confirmation. Also avoid using while loop at the main flow in this case.
Note: you have to think in a modular way. Consider your Menu class as a module which handles user choice. The main flow does not have to bother about the confirmation by the user, it just have to process the final result of the user.
Another suggestion: Use enums instead of integers wherever possible for multiple choices.
I'm creating a Space Invaders game with C# WinForms and when coding the movement of the player's cannon, I create this event handler:
private void Game_Screen_KeyDown(object sender, KeyEventArgs e)
{
for (int i = 0; i < 500; i++)
{
if (e.KeyCode == Keys.Left)
{
cannonBox.Location = new Point(cannonBox.Left - 2, cannonBox.Top); //Changes location of cannonBox to a new location to the left
Application.DoEvents();
System.Threading.Thread.Sleep(10); //Delays the movement by couple milliseconds to stop instant movement
}
if (e.KeyCode == Keys.Right)
{
cannonBox.Location = new Point(cannonBox.Left + 2, cannonBox.Top); //Changes location of cannonBox to a new location to the right
Application.DoEvents();
System.Threading.Thread.Sleep(10); //Delays the movement by couple milliseconds to stop instant movement
}
if (e.KeyCode == Keys.Up)
{
createLaser(); //Calls the method whenever Up arrow key is pressed
}
}
}
But going on different sites concerning how this is unreliable in C#, I am going to make sure to not use it from thereon. What other alternatives are there to use instead of Application.DoEvents within this instance?
I'd suggest to make that event handler async and use await Task.Delay() instead of Thread.Sleep():
private async void Game_Screen_KeyDown(object sender, KeyEventArgs e)
{
for (int i = 0; i < 500; i++)
{
if (e.KeyCode == Keys.Left)
{
cannonBox.Location = new Point(cannonBox.Left - 2, cannonBox.Top); //Changes location of cannonBox to a new location to the left
await Task.Delay(10); //Delays the movement by couple milliseconds to stop instant movement
}
if (e.KeyCode == Keys.Right)
{
cannonBox.Location = new Point(cannonBox.Left + 2, cannonBox.Top); //Changes location of cannonBox to a new location to the right
await Task.Delay(10); //Delays the movement by couple milliseconds to stop instant movement
}
if (e.KeyCode == Keys.Up)
{
createLaser(); //Calls the method whenever Up arrow key is pressed
}
}
}
This way, the control flow is returned to the caller and your UI thread has time to handle the other events (so no need for Application.DoEvents()). Then after (about) the 10ms, the control is returned and execution of that handler resumed.
There may more fine-tuning be necessary, because now of course you could manage to hit more keys while the method has not finished. How to handle that depends on the surroundings. You could declare a flag that signals current execution and refuses further method entries (no thread safety needed here as it's all happening sequentially on the UI thread).
Or instead of refusing re-entrance queue the keystrokes and handle them in another event, e.g. "idle" events (like Lasse suggested in the comments).
Note that an event handlers is one of the rare occasions where using async without returning a Task is ok.
Use a timer that will call the game processing each 20 milliseconds.
Within the KeyDown/KeyUp events just change the current state which is used by the game processing.
Sample code:
[Flags]
public enum ActionState
{
MoveLeft,
MeveRight,
FireLaser,
}
// stores the current state
private ActionState _actionState;
// set action state
private void Game_Screen_KeyDown(object sender, KeyEventArgs e)
{
switch ( e.KeyCode )
{
case Keys.Left:
_actionState |= ActionState.MoveLeft;
break;
case Keys.Right:
_actionState |= ActionState.MoveRight;
break;
case Keys.Up:
_actionState |= ActionState.FireLaser;
break;
default:
break;
}
}
// remove action state
private void Game_Screen_KeyUp(object sender, KeyEventArgs e)
{
switch ( e.KeyCode )
{
case Keys.Left:
_actionState &= ~ActionState.MoveLeft;
break;
case Keys.Right:
_actionState &= ~ActionState.MoveRight;
break;
case Keys.Up:
_actionState &= ~ActionState.FireLaser;
break;
default:
break;
}
}
// called from a timer every 20 milliseconds
private void Game_Screen_LoopTimer_Tick(object sender, EventArgs e)
{
if ( _actionState.HasFlag( ActionState.MoveLeft ) && !_actionState.HasFlag( ActionState.MoveRight ) )
{
cannonBox.Location = new Point(cannonBox.Left - 2, cannonBox.Top); //Changes location of cannonBox to a new location to the left
}
if ( _actionState.HasFlag( ActionState.MoveRight ) && !_actionState.HasFlag( ActionState.MoveLeft ) )
{
cannonBox.Location = new Point(cannonBox.Left + 2, cannonBox.Top); //Changes location of cannonBox to a new location to the right
}
if ( _actionState.HasFlag( ActionState.FireLaser ) )
{
createLaser(); //Calls the method whenever Up arrow key is pressed
}
}
Application.DoEvents() interupts the execution of your method and the UI thread will process its events (including redrawing of the UI). From my experience there is nothing wrong with using it in the right places...
Using the 'async' pattern (as René Vogt suggested) is best practice to make reponsive UI's.
However. You have to ask yourself if you need a loop that checks 500 times, if the key down is left, right or up. Especially as it seems this loop is triggerd by a key down event...
It would be maybe easier if you make a 'while(true)' loop in the main and call Application.DoEvents from there.
Or you react on the key_down event and do on action at a time. => press left -> move one left -> press left again -> move one left more... and so on.
https://msdn.microsoft.com/en-us//library/system.windows.forms.application.doevents(v=vs.110).aspx
I've started learning to code C# within the past week or so and I decided to try and make a small text based game. My issue is that I can't get the input from the textbox i've placed on the form. Or rather, from when I run it, it looks like it takes the nothing that's inside the textbox and uses that in the switch, making it always pick the default case.
namespace TextCat
{
public partial class form1 : Form
{
public string choiceSwitch;
public double choice = 0;
public form1()
{
InitializeComponent();
rtbDialogBox.Text += "";
rtbChoice1.Text = "1) ";
rtbChoice2.Text = "2) ";
rtbChoice3.Text = "3) ";
rtbChoice4.Text = "4) ";
switch (choiceSwitch = txtboxInput.Text)
{
case "1":
choice = 0.1;
break;
case "2":
choice = 0.2;
break;
case "3":
choice = 0.3;
break;
case "4":
choice = 0.4;
break;
default:
rtbDialogBox.Text += "Invalid selection, please input 1-4.";
break;
}
if (choice == 0.1)
{
rtbDialogBox.Text = "";
}
The way you are doing it, that will only get ran once when the form gets created. So it just uses the uninitialized value of txtboxInput.Text.
You need to do your work in an eventhandler, not in the constructor of the form, possibly in a TextChanged event, or if you want to detect keys (ex. Enter), KeyUp or KeyDown event.
See the documentation of Textbox events for more information:
https://msdn.microsoft.com/en-us/library/system.windows.forms.textbox_events(v=vs.110).aspx
I have a program meant to simulate some probability problem (A variation of the monty hall problem if your interested).
The code is expected to produce 50% after enough iterations but in java it always comes to 60% (even after 1000000 iterations) while in C# it comes out to the expected 50% is there some thing different I do not know about java's Random maybe?
Here is the code:
import java.util.Random;
public class main {
public static void main(String args[]) {
Random random = new Random();
int gamesPlayed = 0;
int gamesWon = 0;
for (long i = 0; i < 1000l; i++) {
int originalPick = random.nextInt(3);
switch (originalPick) {
case (0): {
// Prize is behind door 1 (0)
// switching will always be available and will always loose
gamesPlayed++;
}
case (1):
case (2): {
int hostPick = random.nextInt(2);
if (hostPick == 0) {
// the host picked the prize and the game is not played
} else {
// The host picked the goat we switch and we win
gamesWon++;
gamesPlayed++;
}
}
}
}
System.out.print("you win "+ ((double)gamesWon / (double)gamesPlayed )* 100d+"% of games");//, gamesWon / gamesPlayed);
}
}
At the very least, you have forgotten to end each case block with a break statement.
So for this:
switch (x)
{
case 0:
// Code here will execute for x==0 only
case 1:
// Code here will execute for x==1, *and* x==0, because there was no break statement
break;
case 2:
// Code here will execute for x==2 only, because the previous case block ended with a break
}
You forgot to put breaks at the end of the case statements, so the case (1) continues to case (3).