So, I as trying to create an "Open world exploration" game in C# WinForms, And while coding the mining, (which works just fine), I encountered a problem with saving the number of broken blocks to the inventory (a label). Basically, for every block player breaks, it gets added to the inventory as inventoryWrite.Text = $"Grass: {grassHolder}, Rock: {rockHolder}";.
Now, the thing is, sometimes, even though I use the ++ operator, it adds up to 4 to the inventory. I'm citing the code below.
private void Remove(object sender, EventArgs e, PictureBox itm)
{
if (itm.BorderStyle == BorderStyle.FixedSingle)
{
if (itm.Tag.Equals("grass") && items.Contains(itm))
{
grassHolder++;
itm.Tag = "";
}
if (itm.Tag.Equals("rock") && items.Contains(itm))
{
rockHolder++;
itm.Tag = "";
}
if (itm.Tag.Equals("dio") && items.Contains(itm))
{
dioHolder++;
itm.Tag = "";
}
this.Controls.Remove(itm);
items.Remove(itm);
}
}
I update the inventory in a public loop, don't worry about that (interval is 1ms). But I don't think that's the problem, since I tried putting it in the Remove() function, and nothing seemed to change.
I've even double locked the if statement, but nothing! It still adds more than 1. Can anybody tell me how to solve this? Thank you a lot.
EDIT:
As a reply to Ronald's comment, the if statement is called ONLY when the block is selected. ONLY once when the method is called.
There are too many points to cover in a comment and so I've had to enter an answer.
In itself the ++ operator is not the issue and will always behave as it should, but as someone reviewing a small piece of code the following points crop up.
grassHolder, rockHolder, dioHolder appear to have
accessibility beyond this function and so could be altered
elsewhere.
Function void Remove(object sender, EventArgs e, PictureBox itm) appears to be an event handler and yet there is no locking mechanism to ensure that the externally accessible parameters are not changed or used elsewhere whilst the function code is executed. Specifically items which is appears to be a collection of sorts and is used both in logic to determine whether parameters in (1) are incremented, but also has its contents changed within the function.
From comments made it would appear that this logic is run in
response to user interaction, maybe by use of a mouse button or key
event. Is this base event de-bounced to ensure that multiple
triggers aren't handled?
Your statement "saving the number of broken blocks to the inventory (a label)." Implies that you are storing game data within the UI. This should be avoided as it ties game data directly to the UI implementation and therefore makes it difficult to alter the game, but also ties any handling of game data directly to the UI thread.
Recommended actions:
Ensure that the parameters in question are not accessed and altered
elsewhere causing the issue seen.
Utilize a lock(x) statement to ensure that items is not changed
whilst this function is being executed. More information here
De-bounce the mouse button or key click that triggers this function
to ensure that multiple events aren't triggered. This is performed
by placing a minimum time between event triggers. A minimum time
period of say 150ms would be a good starting point. This would equate to a reasonably quick, conscious user action, but be slower than multiple events triggered by partial/poor switch contact. Incidentally this is especially true on touch screen interfaces.
Consider controlling access to global parameters through use of
access functions. For example
int IncrementRockHolder(){ rockHolder++;} Although implementation may appear onerous, they can greatly help with debugging as call stack information is then available showing what code is calling the function and thus making the change.
Implement a game engine class to control access to game data and implement game logic. This
would allow you to unit test game functionality whilst also freeing
it from UI implementation and restrictions.
Related
I have been learning C# using the book "Programming in the Key of C#...", this book has been very good in helping me understand the language but only deals with Console programs. I am ready to move on to developing versions of my past coding projects as Windows form applications but one program in particular is causing me a lot of frustration. I developed a simple movie trivia program utilizing arrays to hold the questions, answer choices, and the correct answer. It worked by displaying on the console the questions, the possible answers and waited for the user to provide a response (basically A,B,C or D) by using Console.Readline() to assign the response.
Now I want to be able to have the user enter the answer by selecting 1 of 4 buttons (A through D). Based on my old code, I am unsure how I get the program to wait for the user to click one of the buttons. I assume i need to change the nature of the loops but I just cant figure out how. Any help would be much appreciated.
Here is a snippet of my Console code:
while (iAsked < 5)
{
iLocation = rand.Next(0, astrQuestions.GetLength(0));
if (list.Contains(iLocation))
rand.Next(0, astrQuestions.GetLength(0));
else
{
iAsked++;
list.Add(iLocation);
Console.WriteLine("Question {0}", iAsked);
Console.WriteLine("------------");
Console.WriteLine(astrQuestions[(iLocation)]);
Console.WriteLine(astrChoices[(iLocation)]);
Console.Write("Answer:");
iResponse = Console.ReadLine();
if (iResponse == astrAnswers[(iLocation)])
{
Console.WriteLine("Correct\n");
iPoints += 5;
iCorrect++;
}
else
{
Console.WriteLine("Incorrect\n");
}
}
Moving from a prompting-centric environment like a console program to an event-driven environment like Winforms, yes…that definitely will require at least some change in "the nature of the loops". :)
That said, the latest version of C# offers an async/await-based approach that can minimize some of the culture-shock that might come from moving from console to GUI. Writing and using async method is itself non-trivial, but IMHO the simpler scenarios are not too hard to understand. More importantly, because it allows you to structure the code in a more directly-imperative way, similar to that which would be used in a console program, it's very much worth learning this along with Winforms generally.
In your particular scenario, you have two separate things you'll need to deal with: prompting the user, and receiving the user's input.
Because of the way an event-driven system works, you need to separate these tasks. But .NET has a class, TaskCompletionSource, which we can use to keep the two glued together, even though they wind up in different places.
First, what happens when the user starts the process? Presumably, you'll have a form, where on that form is a button (or possible a menu item) which when clicked/selected, starts the whole thing. That might look something like this:
private TaskCompletionSource<bool> _completionSource;
private async void button1_Click(object sender, EventArgs e)
{
int[] questionIndexes = ShuffleQuestions();
for (int iAsked = 0; iAsked < 5; iAsked++)
{
textBoxQuestionNumber.Text = string.Format("Question {0}", iAsked);
textBoxQuestion.Text = astrQuestions[questionIndexes[iAsked]];
textBoxChoices.Text = astrChoices[questionIndexes[iAsked]];
_completionSource =
new TaskCompletionSource<bool>(astrAnswers[questionIndexes[iAsked]]);
button2.Enabled = true;
bool result = await _completionSource.Task;
MessageBox.Show(result ? "Correct" : "Incorrect");
if (result)
{
iPoints += 5;
iCorrect++;
}
button2.Enabled = false;
_completionSource = null;
}
}
private void button2_Click(object sender, EventArgs e)
{
if (_completionSource != null)
{
_completionSource.SetResult(
textBoxUserAnswer.Text == (string)_completionsSource.Task.AsyncState);
}
}
(I have changed your question-selection logic above to something more efficient, by assuming that you have a ShuffleQuestions() method. See Is using Random and OrderBy a good shuffle algorithm? for details on how to implement that).
What the above code does is, in response to the user clicking the button1 button (which presumably has text like "Start" or something), executes a loop that is very similar to what you had in your console program. The two main differences are:
Where in your console program, you use Console.WriteLine() to display text to the user, here I have shown the use of TextBox controls in your form which are used to display the same text.
What in your console program, you use Console.ReadLine() to receive input from the user, this loop creates a TaskCompletionSource object for a completely different method to use. That method, which is executed with your button2 button (which presumably has text like "Check Answer" or something) will read the text entered in a text box by the user (here, I've given it the name textBoxUserAnswer), compare it to the correct answer for the question (which has been provided to this method by the other method via the AsyncState property of the Task created by the TaskCompletionSource object I created), and set the Task's result to true or false, depending on whether the user got the answer correct or not.
The tricky part above is that "under the hood", that first method actually returns as soon as it is done filling in the text for the first question and reaches the await statement in the loop. The compiler rewrites the entire method to facilitate this.
When button2 is pushed, and sets the result of the Task, the framework then knows to resume executing the first method where it left off at the await statement, continuing your loop.
This sequence continues until the user has answered all of the question.
Some final notes about the UI:
I have used TextBox's everywhere for user input and output. Of course, there are other ways to display text. Also, the default state for a TextBox is a single-line, read/write text. But for displaying to the user, you may find that setting the ReadOnly property of the TextBox to true is better (i.e. to prevent the user from accidentally changing the text), and/or that you prefer setting the Multiline property to true (i.e. so that more than one line of text is displayed).
The above also assumes that the initial state for the button2 button's Enabled property is false. I.e. that button can't be clicked until the first method above explicitly enables the button at the appropriate time.
UPDATE: turns out that this is a problem with the Mono compiler used by Unity3D. I'm not sure if the current version of Mono (3.10.0) fixes it, but the old version used in the engine (2.0.5) seems to not implement this feature for delegates, or simply work as it should.
At some point in a game I'm developing (in the Unity3D engine), the player unlocks several items at once. For each of them I must present a simple information dialog that the user must click to advance to the next, until all have been read.
I have a simple C# static method to show a dialog (just a colored overlay with some text in Unity, and NOT in any way related to C# UI frameworks):
ConfirmationDialog.Create("Item X Unlocked!", callback);
When the user finally presses the dialog, callback is called.
I wanted to chain all dialogs so that each one is only created when the previous is clicked on, so I tried this:
Action callback = delegate {};
foreach (string item in unlockedItems) {
var cb = callback; // I though this would create a closure for delegates too
callback = (() => ConfirmationDialog.Create(item + " Unlocked!", cb));
}
callback();
This made sense in my head, as the anonymous function would use a different "cb" delegate in each iteration of the cycle. It seems I'm mistaken, though, as this code seems to result in the same dialog being repeatedly called when clicked, as would happen in a recursive function (which I guess is what it became).
I know I can, for example, do int value = i; inside a for loop (where i is the loop iterator) to use the correct value of i inside the anonymous function (I found a lot of material on this). Delegates seem different, but what in particular makes them not work in the same way? Or am I doing something terribly wrong? Could I do this chaining in some similar way? I say "similar" because I can certainly think of doing this in other - more complicated - ways...
Note: please DO correct me if my use of the words "closure", "delegate", etc, is not right :)
Do you have control over the ConfirmationDialog.Create("Item X Unlocked!", callback); API? If so, instead of taking a callback, you should look at refactoring that to use Task. If you had that, you could simply chain a bunch of tasks as continuations and have them execute one after the other.
I know with game development, these things are often quite asynchronous in nature, the way UI gets shown and subsequently reacted to ... so you could use TaskCompletionSource so you have a handle to something that you can signal that your task is done.
I have a class, Plotter, which has a static event (declared public static event MouseEventHandler MultiCursorMouseMove for those wondering how I have a static event).
It emulates a graphical plot and our current application displays about 30-40 of them. The purpose of the event is to draw a cursor across all displayed graphs to indicate the value for the graph at the time the mouse cursor is pointing at.
The problem I'm running into is that, because the Plotter instances have this static event, they refuse to die. As best I can tell looking at them in a memory profiler, every instance of a Plotter (as well as the Form which contains them) is being held in memory by an EventHandler tied to this MultiCursorMouseMove event. I have coded set up which runs the Dispose method on each of the Plotter instances, which includes removing the delegate from MultiCursorMouseMove, but it doesn't seem to be doing any good. According to what I can see via the debugger, Plotter.MultiCursorMouseMove registers as null, but these instances still aren't getting collected and, best as I can tell via the profiler, all of the instances continue to exist due to MultiCursorMouseMove.
So, the question I have is, is there any way to explicitly state "remove all delegates associated with this event"?
I figure that way I'll be sure I got rid of all of them, not to mention it will simplify things compared to my current code which, during the Dispose method, cycles through all of the controls on the tab and runs Dispose on all of the Plotter objects it finds.
To be perfectly open, I submitted this question last week but after saying it was solved, I found another issue. I tried editing the question, but it seems to have fallen out of public view.
So, the question I have is, is there any way to explicitly state
"remove all delegates associated with this event"?
Have you tried explicitly setting "MultiCursorMouseMove" to null?
You could expand your event syntax to track add/remove calls to find out those objects which didn't not unsubscribe:
public static event MouseEventHandler MultiCursorMouseMove
{
add
{
}
remove
{
}
}
You could also follow Weak event pattern, or the one described by J. Richter. Weak handlers are based on weak references, which don't block GC.
And, at last, you could use hard-core Reflection approach to clean array of delegates in static MouseEventHandler object.
I'm cutting my teeth on events and delegates today and to do so, I have been toying with the idea of experience bars, those progress bars from games. But I have a question about the better way to solve my problem - it could be as simple as bad design. Let me provide you some details.
I have modelled my idea with an ExperienceBar class.
It contains properties:
int StartValue
int CurrentValue
int EndValue
and a method
void UpdateBar(int)
UpdateBar adds the parameter to CurrentValue and then tests to see if it has reached EndValue. If it exceeds the amount, the EndValue increases and the amount continues on. Note that initially in my thinking, it is not concerned with the effects of reaching the maximum amount possible, just that the end value increases and the StartValue is reset to zero.
Another class called Player has a property of class ExperienceBar.
In my little demo, when Player.ExperienceBar.UpdateBar(int) reaches the EndValue it fires an event which is handled by the Player class. It updates the Player.Level property by one.
I've just realised that I could achieve the same thing by just changing UpdateBar(int) to return type "true". This method could be tested by the Player class and when true, Player.Level increases by one.
So my question - which is the best practice way to handle this rather specific circumstance? As a general rule of thumb for these kind of situations, is it better to handle events, or is it better just to keep it simple with the testing of return statements?
PS: I hope I've made this clear as possible, but I can try to clarify if anyone is having trouble. I believe there may be some redundancies already with my idea, but try not to deviate from the question please. I'm kind of aware of them! Thank you :)
Well... To me, events is the good way to do it.
However, if I was to design the application it would be down to one question: Will the ExperienceBars's event when it reaches EndValue ever be used by anyone else than the class calling UpdateBar.
If you are designing a component to be used in many places (which seems to be the goal), the answer to me seems to be an almost certain yes, therefore my answer is use events!
/Victor
In my opinion, there's no best way to do this. There are various ways to implement the class that, depending on how it is going to be used, are a better or worse fit.
Use events when you want to implement the observer pattern for many "clients" or "observers" who need to know the state of an object and need to be alerted when that state changes. this works for the degenerate case where there is only one client, but the caller of the the method that changes the object's state is not the one that needs to know about the change.
Use return values when the state only needs to be known by the caller, there are no other observers of the class. This is simple, and limits the scope of the knowledge of the state of the class to the item that immediately needs to know it.
And finally, do not over-design this. If it only needs to notify the caller, do not implement events. If at some later date the class needs to be "observed" then implement events at that point.
It all depends on the coupling of your components and the flow of your program. The downside to events is that you will increase the complexity of your program, because it is harder to trace exactly what the flow of execution will be when any piece of code can subscribe to your event. The upside is it allows for a more flexible and scalable design, since any piece of code can subscribe to your event.
So here is the thing, if Player is going to be in charge of handling all things related to leveling up, then having a tight coupling between Player and ExperienceBar is ok. Let's say you want to expose an AddIn framework, in that case you probably want to expose leveling up to external plugins, in which case an event makes a lot more sense.
Personally, I would have XP be a part of Player, and have Player expose a LevelUp event, but I don't know if that would be a good idea for you and your framework/domain modeling without seeing your existing code.
I would use events rather than a return value. Why? Two reasons:
What does returning true mean when returning from UpdateBar? That it was updated? That xyz happened? Someone else looking at this (or you, two months down the road) will wonder as well.
What if more than one thing should occur when the limit is reached? Then you have to tie all of the code related to those things (levelling, getting a new item, whatever) into the method that you used to update the bar in the first place.
I would have an event associated with reaching a certain level and then "listeners" for that event that can respond accordingly.
I don't think it makes sense to have Experience bar fire an event - in that case a return value would be fine. It could then call the Player's LevelUp function, which could fire an OnLevelUp event from the Player class, if needed.
I'll try to explain my scenario as best i can;
At each application tick I query the current state of the keyboard and mouse and wrap them in individual classes and data structures. For the keyboard it's an array of my Keys enum (one item for each of the keys that are currently pressed) and for the mouse it's a class containing coordinate delta's and bools for each buttons pressed.
I also have a rudimentary state machine managed via a state manager class which maintains the stack and marshalls the states.
All I want to know is, how best to pass the input (snapshots) to the individual states my app can be in at any one time?
I would like to process as much of the input as possible away from the individual states, so as to reduce repeating logic within the states.
Or would it be better to keep the input snapshots as pure as possible and pass them to the states so they can keep they're input-specific-logic hidden?
Note
This structure is similiar to how I imagine a game would work, and although this application is not a game it does need to be processed as quickly as possible.
Why are you querying the state of the keyboard and mouse with each tick? A much better and traditional solution would be to capture events fired from the keyboard and mouse. That way you only need to update the state when you HAVE to.
If you simply query your keyboard and mouse every tick, I can guarantee you'll run into problems. When you query every tick, you'll find that you miss inputs that occur quickly (within the time domain of a single tick). Imagine a situation where the user presses and releases a key/button between two ticks (it will happen more than you think) - you'll completely miss the input.
Particularly in C#, which is completely geared for input events like this, you should be using events.
In XNA, they do this by making the keyboard class static.
KeyboardState state = Keyboard.GetState();
you can then access the current key state with the above line of code.
You should fire events and capture the arguments. That would be the easiest, and most efficient way to handle this.
class YourClass
{
//data members ...
public void OnKeyboardPress(object sender, EventArgs args)
{
//handle your logic capturing the state here
}
}
//elsewhere
someControl.KeyPress += new KeyPressDelegate(yourClassInstance.OnKeyboardPress);