How to perform an action after key pressed? - c#

I tried to hide a text in Unity after caps was pressed, but it doesn't work, it stops before "while".
I'm quite a not up to par programmer, so anyone more experienced?
private float TurnOffInfoText()
{
bool IsCapsPressed = Input.GetKeyDown(KeyCode.CapsLock);
while (IsCapsPressed == true)
{
EndOfGameText.enabled = false;
}
return 0;
}

Why does this even return a value?
Also your while loop would completely freeze the entire App and even the Unity Editor application! Within the loop the IsCapsPressed value is never ever changed!
I don't see where your method is called from but if you never experienced a freeze so far then "luckily" the key never went down in the same frame so far.
Usually you would rather poll the input every frame. By a simple look into the API for Input.GetKeyDown:
private void Update ()
{
if(Input.GetKeyDown(KeyCode.CapsLock))
{
EndOfGameText.enabled = false;
}
}

Related

Is there a way to await a flag change in a function?

I've attempted to make a simple step mode for an algorithm I'm running, and here is how it looks like:
public async Task<bool> AStarAlgorithmAsync(PFSquare curr = null)
{
// some algorithm code here
foreach(var square in Sorroundings)
{
if (SteppedMode)
{
await Task.Run(Pause);
}
if (await AStarAlgorithmAsync(square))
{
return true;
}
}
}
In my application, I have a Boolean called SteppedMode that decides if the algorithm should run one iteration per click event.
Pause() looks like this:
private void Pause()
{
while (!ContinueStep) { }
ContinueStep = false;
return;
}
And in another part of my (GUI) application I have an event which sets the boolean ContinueStep to true which in theory should end the while loop and continue the algorithm function. Currently this bit of code locks my GUI thread up and I'm almost certain there is a better way to do this.
I'm trying to get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm. Is there an easier and cleaner way to do this?
(This is a GUI application, not a console application.)
Your property is moonlighting as a method.
It makes no sense to set a property, to then have that property revert back to its original state immediately. As a consumer, I would be majorly confused by that behavior. Think about this code:
var myObj = new MyObject();
myObj.MyBoolean = true;
Console.WriteLine(myObj.MyBoolean); // FALSE!?
It just doesn't make sense.
The only effect you want to trigger by setting this property is to execute some code. That's exactly what methods are supposed to be used for:
public void ContinueStep()
{
Console.WriteLine("I did some work");
}
So instead of this:
myObj.ContinueStep = true;
you should be doing this:
myObject.ContinueStep();
This doesn't lock up your UI thread, while also being a lot more sensical to your consumer. The method suggests that some action will be taken (which may or may not lead to state changes in the object - that's a contextual expectation).
Infinite recursion
As an aside; based on your code, AStarAlgorithmAsync is a recursive function, and seemingly infinitely so. There doesn't seem to be an ending condition.
Every recursive level will interate over the first surrounding and then trigger the next level, which again will interate over the first surrounding and then trigger the next level, which again ...
That can't be right, but it's unclear to me how to fix it as the bigger picture is not explained in your question
A simple implementation
What I'm trying to do is get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm, is there an easier and cleaner way to do this?
A simple example of such a thing:
private int _index = 0;
private List<object> _myList = ...; // assume this list contains some elements
public void ProcessNextObject()
{
if(_index < _myList.Length)
{
Process(_myList[_index]);
_index++;
}
}
private void Process(object o)
{
Console.WriteLine("Processing this object!");
}
You can then hook up your click event to call ProcessNextObject().
Note that in this example, the list is processed once and cannot be processed again. By manipulating the index value, you can change that behavior as you like.

Having issues with State changes in a Text Adventure in Unity 4.6

Basically, I'm trying to get Unity to change the game's state when the player clicks a button. For whatever reason though, it isn't reading the second key press that actually changes the state. The code looks like this:
void state_Cell () {
if (CelP == 0) {
text.text = "Some text";
if (Input.GetKeyDown (KeyCode.C)) { //This works
++CelP;
text.text = "Some more text.";
if (Input.GetKeyDown (KeyCode.B)) { //This doesn't work
++CelP;
MyState = States.Bed;
}
}
}
}
If it will help, this is the code for the Bed state that I can't access in the game:
void state_Bed () {
if (BedP == 0) {
text.text = "Still more text.";
if (Input.GetKeyDown (KeyCode.D)) {
MyState = States.Delivery;
BedP++
}
}
}
The most confusing part about this for me is that Unity's compiler isn't giving me any errors. I encountered this problem with a different state before, and what I did was move the "++Variable" to a different location, but that didn't work for this statement.
Aside from that, I've also tried to removing the "if statement" in the Bed state, but that didn't work either.
Another thing that I attempted was changing "B" in the "KeyCode" to different keys, but that didn't work at all.
I also tried to include a "print();" statement at the very beginning of the Bed state, but it didn't trigger either.
The last thing that I've tried was putting a "print();" statement after the "(KeyCode.B))", but that didn't work either. So from that I know that the problem is that Unity isn't reading or responding to the key press.
Any help would be appreciated. And you need anymore information than what I've supplied, tell me and I'll give it to you.
Thanks in advance.
You should create two states for Cell, first one listens to C and changes the state to second, second one listens to B and changes the state to Bed.
The reason is that state machines do not follow after where they left in the middle of the code inside one state. You need to be more specific when you are defining your states.
I assume that state_Cell is being called from an Update or a similar Coroutine which is being called every x frame. so local variables inside state_Cell is reset every time it is called. so machine does not know if user previously pressed C or did not press anything.
When user presses a key then technically the state has been changed. when user presses another key then again state changes.
Edit
In case you don't want to add extra states this is the simplified version of your state_Cell
You have to be careful when to set the extra Boolean to false.
bool c_pressed = false;
void state_Cell () {
if (c_pressed || Input.GetKeyDown (KeyCode.C)) {
c_pressed = true;
if (Input.GetKeyDown (KeyCode.B)) {
c_pressed = false;
MyState = States.Bed;
}
}
else
c_pressed = false;
}
}

Waiting while KeyPressed in XNA

I'm trying to learn XNA game programming, now i would like to wait while a Key is Pressed
I have Test it with:
while (IsKeyPressed = Keyboard.GetState().IsKeyDown(key));
But IsKeyPressed is also true when the key was released
That code is effectively a spin lock. Don't use spin-locks.
The bug you are seeing is likely because you are using a spin-lock, its not getting a chance to update properly.
Instead, you should read the key being pressed, and set state in whatever class is relevant to stop processing (likely a simple if check in the Update function). Then, when you detect the release, you change the state so the if check will pass.
Something like:
//Main Update
if (Keyboard.GetState().IsKeyDown(key))
myObject.Wait();
else
myObject.Continue();
//Other object
public void Wait()
{
waiting = true;
}
public void Continue()
{
waiting = false;
}
public void Update()
{
if (!waiting)
{
//Update state
}
}
You could always check previous state to avoid calling Wait and Continue repeatedly, but thats going to be something of a micro-optimization with the code provided.

CheckBox does not update

I am using a CheckBox as a Toggle Button (checkBox.Appearance = Appearance.Button) in a child form. If the users presses the button a measurement cycle will start if some criteria are satisfied (e.g. temperature within range). The CheckBox.Checked property remains true until the measurement is completed.
If e.g. the temperature is out of range a warning will appear and the button will be reset. The same happens if the cycle ended properly. In the end this self-explaining function is called:
/// <summary>
/// Resets the button states to false
/// </summary>
public void ResetButton()
{
checkBox_Start.Checked = false;
}
Now, when debugging, I see that the CheckBox_Start.Checked property is false and remains false. BUT the UI does not show the actual value. It seemingly remains checked. I have tried Refresh() and Update() on all levels.
Does anyone have an idea? What could possibly keep the UI from showing the actual value?
I found a similar problem, except with DataGridView. The solution I found was to invalidate the control and then request it to be redrawn, as follows:
public void ResetButton()
{
checkBox_Start.Checked = false;
checkBox_Start.Invalidate();
checkBox_Start.Update();
}
Try that, see if it fixes it.
this.MyCheckBox.Checked=false;
I had same problem, after spending some times on CheckBox properties and methods, finally i realize that where I am initializing this control is not a suitable one. I was using a semi framework which in some cases doesnt goes in this code. I use this simple property:
checkBox_Start.Checked = false;
Make sure you do not have InitializeComponent running twice.
That would cause the initial poster's control behaviour.
This usually comes with trying to update UI within un-main thread. So I recommend to create a generic function doing UI update. And let the main or un-main thread call it if need to update UI.
This is my sample code:
public void OnCheckBox(bool set_on) {
if (set_on == true) {
CBox.Invoke(new Action(() => {
CBox.Checked = true;
CBox.Invalidate();
CBox.Update();
}));
}
else {
CBox.Invoke(new Action(() => {
CBox.Checked = false;
CBox.Invalidate();
CBox.Update();
}));
}
}
And you can call it like:
void run() {
OnCheckBox(true);
Thread.Sleep(500);
OnCheckBox(false);
Thread.Sleep(500);
OnCheckBox(true);
Thread.Sleep(500);
OnCheckBox(false);
}
Thread t = new Thread(new ThreadStart(this.run));
t.Start();

Have MessageBox appear once (code inside timer)

UPDATE: I've managed to fix my problem. Using the code below, I moved my MessageBox AFTER my XML saving and changed the Timer from 100ms to 400ms. I now have 1 box appear, thank god. Although If anyone has a short cut to updating a single value (ActReminded) in the List array(ActListTask), that'd be great to know.
I'm having a little issue with displaying the MessageBox. Show inside a timer without it spamming me. Here's the part of the code I've been working with:
public class ActiveTasks
{
//Properties here
}
public List<ActiveTasks> ActTaskList = new List<ActiveTasks>();
for (int i = 0; i < ListActive.Items.Count; i++)
{
if (DTime.Date == newDateTime.Date)
{
if (newDateTimeLeft.CompareTo(TimeSpan.Zero) <= 0 && ActTaskList[i].ActReminded != "true")
{
MessageBox.Show("!!!!");
ActTaskList.Add(new ActiveTasks()
{
ActTitle = ActTaskList[i].ActTitle,
ActDesc = ActTaskList[i].ActDesc,
ActDate = ActTaskList[i].ActDate,
ActTime = ActTaskList[i].ActTime,
ActStatus = ActTaskList[i].ActStatus,
ActReminded = "true",
ActRepeat = ActTaskList[i].ActRepeat
});
ListActive.Items.RemoveAt(i);
ActTaskList.RemoveAt(i);
XDocument XmlActTasks = GenerateActiveListToXML(ActTaskList);
}
}
}
I actually decided I may want to hold onto the reminder status, whether it has been shown or not as I wouldn't want a repeated reminder every time the program is opened. Since I don't know of a way to update an individual part of ActTaskList I just re-added it, and then deleted the original. This code manages to recognise that if it happens, it will change the reminder status from false, to true; after I've Ok'ed all the spam. So it will stop the MessageBox once I've managed to closed all the Messageboxes. However, it doesn't stop the spam. Would it be anything to do with the fact I've set the timer to 100ms? Or could their be an alternative way to make the messagebox appear without it being inside the timer?
The odds of the current time lining up exactly to the second what is happening in your loop is small. Why not treat newDateTime as a cut off point and just set a flag?
//Declare this outside of the loop
bool hasDisplayed = false;
//Inside the timer event handler
if (!hasDisplayed && DateTime.Now >= newDateTime)
{
hasDisplayed = true;
MessageBox.Show("!!!!!!!!!!!!!");
}
Can you do something like this?
Action message = () => MessageBox.Show("!!!!!!!!!!!!!"));
object lockOb = new object();
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
lock(lockOb)
if(null != message)
{
message();
message = null;
}
}
You say you've already tried a boolean indicating the message has already been shown, I'm assuming because the code probably looked like it did below.
void TimerLoop()
{
bool msgAlreadyShown;
if(!msgAlreadyShown)
{
MessageBox.Show("!!!!!!!");
}
// Other work in your timer function
}
The problem with that code is that the bool will be set to false each time the function is called by the timer. You haven't posted much code, but you've at least stated what you're trying to accomplish, a timer that checks if a reminder should be presented to the user.
I'm about to make some wild guesses about how you've put together your software, there's a good chance it's way off, but I hope it might point you in the right direction. You could have some sort of reminder class like this:
public class Reminder
{
string Message { get; set;}
DateTime Alarm { get; set; }
bool IsDismissed { get; set; }
}
I'm assuming you might want to have multiple reminders that can be checked for in the timer loop, so your timer loop could look something like:
private List<Reminder> _activeReminders; // A list of reminders
void TimerLoop(object s, ElapsedEventArgs e)
{
lock(_activeReminders)
{
var now = DateTime.Now;
foreach(var reminder in _activeReminders)
{
// only run this code if the time has passed and it hasn't already
// been shown
if(now.CompareTo(reminder.Alarm) >= 0 && !reminder.IsDismissed)
{
MessageBox.Show(reminder.Message);
reminder.IsDismissed = true;
}
}
}
}
This is a pretty naive implementation, since you probably don't want to hold onto the reminders for forever and the reminders are never removed from the _activeReminders list, but you essentially just need to add some sort of state to determine if the reminder has already been shown.
Of course, this isn't a complete example either, since I never new up the _activeReminders field or add anything to it, but I think this might help get the idea of what you need to do across. Also, you might not care about multiple reminders, and your timer code could look nothing like this. The main idea was to show you how you can keep track of the state of a reminder, and tailor it to your own code. The above was just an example.
Also, I haven't actually tested it, so treat it more like pseudocode than anything else. However, the logic is sound, and should it should only cause the message box to appear once.

Categories