How to create a pause like effect while counting down in XNA - c#

I'm creating a simple game, where whenever I leave the menu I want a 3 second timer to cover up the screen, while the game is paused. I had imagined I would do something like this
protected void Draw(GameTime gameTime)
{
//..//
for(int i = 3; i > 0; i--)
{
spriteBatch(gameFont, i.ToString(), new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2), Color.White);
Thread.Sleep(1000);
}
//..//
}
However, this just pauses at the menu for three seconds, then immediatly puts you in game. You can for a split second see a random number, but not at all a real countdown. What can I do to pause the games running and still draw the game?

Thread.Sleep(1000);
At the best of times you want to avoid using Thread.Sleep() and I can't think of any-time you should use it in a game. Games should be running flat-out and you want to minimise situations where the CPU is blocking or just sleeping.
What can I do to pause the games running and still draw the game?
Generally in games (and XNA is no exception), the draw code is separate from state update mechanics. This means your Draw() should be focused on drawing and nothing else.
Pausing can be considered a game state and is better served being processed in your game's Update().
Consider adding a state to your game, perhaps:
enum States
{
Initialising,
InMenu,
LeavingMenu,
InGame,
}
States _state;
Then in your Update() you could work on your state machine, switching from in-menu; to leaving menu; to finally in game:
DateTime timeOfEscPressed;
protected virtual void Update(GameTime gameTime)
{
switch (_state)
{
case States.Initialising:
break;
case States.InMenu:
// *** pseudo-code here ***
// if ESC pressed then
// _state = States.LeavingMenu
// timeOfEscPressed = DateTime.Now;
break;
case States.LeavingMenu:
// stay in this state for 3 seconds
if ((DateTime.Now - timeOfEscPressed).TotalSeconds >= 3)
{
_state = States.InGame;
}
break;
case States.InGame:
if (menuKeyPressed) // pseudo code
{
_state = States.InMenu;
timeOfEscPressed = DateTime.Now;
}
break;
}
}
Your draw code becomes just that, draw code:
protected void Draw(GameTime gameTime)
{
switch (_state)
{
case States.InMenu:
DrawMenu();
break;
case States.LeavingMenu:
ShowTimer();
break;
}
}
In this way, you gain the illusion of your game pausing but without freezing the update-draw loop.

Related

How to make a timer that ticks down in c# monogame

So I'm trying to make a timer that will ticks down from 30 seconds to 0 in monogame but I don't understand how I can do it. I'm very new to programing. I'm trying to put it into my enum so when the timer hit 0 it will change state from play to gameover.
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
switch (currentGameState)
{
case Gamestate.Start:
timeSinceLastFrame += gameTime.ElapsedGameTime.TotalSeconds;
if (timeSinceLastFrame >= timeBetweenFrame)
{
timeSinceLastFrame -= timeBetweenFrame;
currentFrame.X++;
if(currentFrame.X >= sheetSize.X)
{
currentFrame.X = 0;
currentFrame.Y++;
if (currentFrame.Y>=sheetSize.Y)
{
currentFrame.Y = 0;
}
}
}
posStone.Y = posStone.Y + 1;
if (Keyboard.GetState().IsKeyDown(Keys.Enter))
{
currentGameState = Gamestate.Play;
}
break;
case Gamestate.Play:
break;
case Gamestate.GameOver:
break;
}
}
Method 1
This approach I used in a game where I was storing a future DateTime representing the world game time with a future starting point and updated on every Unity physics frame in my Monobehaviour script. However the general idea can be used in your scenario. If you don't need to store the game time between saves then this might be overkill. Read Method 2 in that case.
You store an end time and a reference time (game time) for the game's current time. You'd want to store both as a class property.
DateTime referenceTime;
DateTime endTime;
Right before you set currentGameState to Gamestate.Play you would want to set these values
referenceTime = DateTime.Now; //Would be assigned from a global GameTime class object or your own custom game time
endTime = referenceTime.AddSeconds(30);
Then you would update your reference time on every physics frame. I used Millisecond instead of Seconds for accuracy (GameTime Documentation) since in most game engines I've worked with frames updates tend to happen in a fraction of a second. This would be placed under Gamestate.Play case branch
referenceTime.AddMilliseconds(gameTime.ElapsedGameTime.TotalMilliseconds);
Then you also would need to do the check in the same case branch (probably after the last code snippet).
if (referenceTime > endTime) {
currentGameState = Gamestate.GameOver;
}
Method 2
GameTime class has an alternative member totalGameTime which is also a System.TimeSpan struct value (GameTime Documentation, TimeSpan Documentation). So this means another option is you can used the total number of seconds from the beginning as the game time. The reference value would be again stored as a class member it is now an int this time since we will store the number of seconds since the game started.
int referenceTime;
You would set the reference time right before you switch currentGameState to Gamestate.Play . There are other ways of doing this but this is probably the simplest.
referenceTime = gameTime.totalGameTime.TotalSeconds;
Then you would add a check in your Gamestate.Play case statement as such. If you plan on repeatedly using gameTime.totalGameTime.TotalSeconds then you might want to store it in another variable for legibility.
if (gameTime.totalGameTime.TotalSeconds > referenceTime + 30) {
currentGameState = Gamestate.GameOver;
}
Have you looked into System.Timer class?
An example of the class is below so you can copy and paste into LinqPad to run and experiment with.
void Main()
{
/* What this does is raise an event every 5 seconds, according to value set in the interval
*/
var timer = new System.Timers.Timer(5000);
timer.Elapsed += OnTimedEvent;
timer.AutoReset = true; // Whether or not the event should be repeated
timer.Enabled = true; // Starts the timer
}
void OnTimedEvent(object sender, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0:HH:mm:ss.fff}",
e.SignalTime);
}

Monogame C# Timer (do something for 15 seconds every 3 seconds)

I am trying to create a timer, which, for example, every 3 seconds during eg 15 seconds will perform an action.
I tried to use gameTime.ElapsedGameTime.TotalSeconds and loop, but unfortunately it doesn't work.
I have an Attack () function that reduces player statistics when an enemy attacks it. I would like that in case of one particular enemy, this function for a specified period of time would subtract player's HP, eg for every 3 seconds. I guess it should be done in the Update function to access gameTime, unfortunately, I have no idea how to do it.
public override Stats Attack()
{
attack = true;
return new Stats(0, -stats.Damage, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
public override void Update(GameTime gameTime)
{
spriteDirection = Vector2.Zero; // reset input
Move(Direction); // gets the state of my keyborad
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; // make movement framerate independant
spriteDirection *= Speed; // add hero's speed to movement
position += (spriteDirection * deltaTime); // adding deltaTime to stabilize movement
totalPosition = new Vector2((int)((BottomBoundingBox.Center.X) / 32.0f), (int)((BottomBoundingBox.Center.Y) / 32.0f));
base.Update(gameTime);
}
I will make it simple, so you need to modify my code to achieve your desire result.
My best guess is that you want to have a special effect when your monsters hit your player.
First, you need to check if the monster actually hits the player (if collision is detected):
if (collision)//if it's true
{
// Apply your special effect if it is better than
// the one currently affecting the target :
if (player.PoisonModifier <= poisonModifier) {
player.PoisonModifier = poisonModifier;
player.ModifierDuration = modifierDuration;
}
//player.setColor(Color.Blue);//change color to blue
player.hitPoints -= Poision.Damage;//or enemy.PoisonDamage or whatever you define here
hit.Expire();//this can be for the arrow or bullet from your enemy or simply just a normal hit
}
In your Player class, you need:
public float ModifierDuration {
get {
return modifierDuration;
}
set {
modifierDuration = value;
modiferCurrentTime = 0;
}
}
Then in Update method of Player class:
// If the modifier has finished,
if (modiferCurrentTime > modifierDuration) {
// reset the modifier.
//stop losing HP code is here
modiferCurrentTime = 0;//set the time to zero
setColor(Color.White);//set back the color of your player
}
count += gameTime.ElapsedGameTime.TotalSeconds;//timer for actions every 3s
if (posionModifier != 0 && modiferCurrentTime <= modifierDuration) {
// Modify the hp of the enemy.
player.setHP(player.getCurrentHP() - posionDamage);
//Or change it to every 3s
//if (count > 3) {
// count = 0;
//DoSubtractHP(player);
//}
// Update the modifier timer.
modiferCurrentTime += (float) gameTime.ElapsedGameTime.TotalSeconds;
setColor(Color.Blue);//change the color to match the special effect
}
Hope this helps!
You need to store the start time, or the last time that the action was carried out. Then during each update compare the elapsed time to the stored time. If 3 seconds have passed then perform the action, store the current time and repeat the process.
I do not know monogame, but if I were doing this in one of my C# applications, I would use a timer, and pass in anything that the timer would need to modify.
There is good info here https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=netframework-4.8 and I stole a bit of code from here and modified it as an example to demonstrate my idea. I extended the System.Timer to allow it to run for a duration and stop itself. You can set the frequency and duration and forget about it. Assuming that you are able to update this information from a timer.
class Program
{
private static FixedDurationTimer aTimer;
static void Main(string[] args)
{
// Create a timer and set a two second interval.
aTimer = new FixedDurationTimer();
aTimer.Interval = 2000;
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;
// Start the timer
aTimer.StartWithDuration(TimeSpan.FromSeconds(15));
Console.WriteLine("Press the Enter key to exit the program at any time... ");
Console.ReadLine();
}
private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
{
FixedDurationTimer timer = source as FixedDurationTimer;
if (timer.Enabled)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
}
public class FixedDurationTimer : System.Timers.Timer
{
public TimeSpan Duration { get; set; }
private Stopwatch _stopwatch;
public void StartWithDuration(TimeSpan duration)
{
Duration = duration;
_stopwatch = new Stopwatch();
Start();
_stopwatch.Start();
}
public FixedDurationTimer()
{
Elapsed += StopWhenDurationIsReached;
}
private void StopWhenDurationIsReached(object sender, ElapsedEventArgs e)
{
if (_stopwatch != null && Duration != null)
{
if (_stopwatch.Elapsed > Duration)
{
Console.WriteLine("Duration has been met, stopping");
Stop();
}
}
}
}
}
You could see examples of how to pass objects into the timer here (#JaredPar's example) How do I pass an object into a timer event?
string theString = ...;
timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, theString);
static void MyElapsedMethod(object sender, ElapsedEventArgs e, string theString) {
...
}
One way to do this would be to use coroutines. MonoGame does not have built-in support for them like other game engines, but they are not too complicated to implement yourself. You need some knowledge of the yield keyword and enumerators to understand them, but once abstracted away they make your game code way easier to write and understand.
Here's an example of what your gameplay logic would look using a Coroutine system like the one described below:
public void Attack(Enemy enemyAttacking)
{
if (enemyAttacking.Type == "OneParticularEnemy")
{
StartCoroutine(RunDamageOverTimeAttack());
}
}
// This coroutine starts a second coroutine that applies damage over time, it
// then waits 15 seconds before terminating the second coroutine.
public IEnumerator RunDamageOverTimeAttack()
{
var cr = StartCoroutine(ApplyDamageOverTime());
yield return 15000; // in milleseconds (ms), i.e. 15000 ms is 15 seconds
cr.IsFinished = true;
}
// This coroutine applies the damage every 3 seconds until the coroutine is finished
public IEnumerator ApplyDamageOverTime()
{
while (true)
{
ApplyDamageToPlayer();
yield return 3000;
}
}
The code reads very close to the way you described the actual problem you're trying to solve. Now for the coroutine system...
The StartCouroutine method creates a Coroutine class instance and stores it. During the Update step of the game loop you iterate through the coroutines and update them, providing gameTime to calculate when the next step of the method should run. Each step executes the code in the routine until a yield is found OR until the method ends naturally. Once the coroutine is finished you clear them out. This logic looks something like this:
private List<Coroutine> coroutines = new List<Coroutine>();
public Coroutine StartCoroutine(IEnumerator routine)
{
var cr = new Coroutine(routine);
couroutines.Add(cr);
return cr;
}
public void UpdateCoroutines(GameTime gameTime)
{
// copied in case list is modified during coroutine updates
var coroutinesToUpdate = coroutines.ToArray();
foreach (coroutine in coroutinesToUpdate)
coroutine.Update(gameTime);
coroutines.RemoveAll(c => c.IsFinished);
}
public void Update(GameTime gameTime)
{
// normal update logic that would invoke Attack(), then...
UpdateCoroutines(gameTime);
}
A Coroutine class is responsible for tracking the time remaining between steps of the routine, and tracking when the routine is finished. It looks something like this:
public class Coroutine
{
private IEnumerator routine;
private double? wait;
public Coroutine(IEnumerator routine)
{
this.routine = routine;
}
public bool IsFinished { get; set; }
public void Update(GameTime gameTime)
{
if (IsFinished) return;
if (wait.HasValue)
{
var timeRemaining = wait.Value - gameTime.ElapsedGameTime.TotalMilliseconds;
wait = timeRemaining < 0 ? null : timeRemaining;
// If wait has a value we still have time to burn before the
// the next increment, so we return here.
if (wait.HasValue) return;
}
if (!routine.MoveNext())
{
IsFinished= true;
}
else
{
wait = routine.Current as double?;
}
}
}
This may seem considerably more complex than other solutions provided here, and it may be overkill, but Coroutines allow you to forgo tracking a bunch of state in tracking variables, making complex scenarios easier to follow and cleaner to read. For example, here's a arrow spawning strategy I used Coroutines for in Ludum Dare 37. It spawns 3 arrows 600 milleseconds apart with a 3 second wait between them: https://github.com/srakowski/LD37/blob/477cf515d599eba7c4b55c3f57952865d894f741/src/LD37/GameObjects/BurstArrowSpawnBehavior.cs
If you'd like more social proof of the value of Coroutines take a look at Unity. Unity is one of the more popular game engines, and it has Coroutine support. They describe a scenario where it is useful in their documentation: https://docs.unity3d.com/Manual/Coroutines.html.
I use this for my game :
Public Async Function DelayTask(Time As Double) As Threading.Tasks.Task
Await Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time))
End Function
Converted to C# :
public async System.Threading.Tasks.Task DelayTask(double Time)
{
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time));
}
You would use it like this in an Async Function :
Await DelayTask(1.5);
The number is in seconds, you can change this by changing the TimeSpan.whateverformat.
Considering that you'll have various things that affect your stats maybe you're better off at having an update subroutine in your Stats class that will check a list of effects that are scheduled to update after one point in time.
This would be better for performance than having each effect relying on its own thread.

Unity coroutine does not work

I don't think I fully understand coroutines, it doesn't work the way I wanted, so I need help.
I've got a memory game (Simon like) that consist of 4 squares that randomly switches on and off. After square switches on/off it should take a little break, before switching next button, which my program does not seem to do so. For the switching process I use blink*Colorname coroutine which is following:
foreach (int color in pattern)
{
switch (color)
{
case 0:
StartCoroutine (blinkGreen (blinkSeconds));
break;
case 1:
StartCoroutine (blinkRed (blinkSeconds));
break;
default:
break;
}
}
//to do: pause function between button blinks
IEnumerator blinkGreen (float seconds)
{
greenImg.color = Color.white;
yield return new WaitForSeconds (seconds);
greenImg.color = Color.green;
}
I've tried using waitforseconds at 2 places to achieve my goal: First, at blink*Color as following:
IEnumerator blinkGreen (float seconds)
{
greenImg.color = Color.white;
yield return new WaitForSeconds (seconds);
greenImg.color = Color.green;
yield return new WaitForSeconds (seconds);
}
Second, after the the loop, under //to do: pause function between button blinks by calling another coroutine:
StartCoroutine(waitfornexttransition(5.0f));
IEnumerator waitfornexttransition (float second)
{
yield return new WaitForSeconds (second);
}
Am I missing something? All suggestions and helps are appreciated. Thanks!
Unity does not wait for the completion of the started coroutine to continue code execution. If you type (psuedocode):
print "1"
start coroutine that prints "2" after a second
print "3"
the output will be: "1", "3", "2".
If you put all your code in one coroutine, everything will run sequentially as expected.
Well, since it's all in a foreach loop anyway and you're starting a new couroutine for each iteration, then basically all the coroutines are starting at the same time within the same frame. They will then all run concurrently regardless of what the rest of your code is doing, blinking all lights simultaneously.
If you want your code to wait for a certain amount of time between executions, then make a timer and only blink the lights when the timer hits 0. A basic example would be:
private float timer;
void Start()
{
timer = 2f; // Waiting 2 seconds between blinks
}
void Update()
{
timer -= Time.deltaTime; // Consistently decrement timer independently of frame rate
if (timer <= 0)
{
blinkLight();
timer = 2f;
}
}
void blinkLight()
{
// Light blinking logic goes here.
}
Your foreach loop should also be inside a coroutine and you can put a yield return new WaitForSeconds (seconds) before each iteration.

Wait until a click event has been fired C#

I'm developing a card game but I need to have a function that stops the program until the player hasn't clicked in the PictureBox of his card to discard it.
The algorithm of my game is this:
int nextDrawer = 0; // the players which will discard a card are determinated in counterclockwise starting from the human player
for (int i = 0; i < players; i++) // untill all the players hasn't drawed a card
{
if (i == 0) .... // the human player has to click on a picture box to discard a card
else .... // an AI player will discard a card which is selected randomly from the 3 cards which AI has got in its hand
}
The problem is that when a mance ends, the first who will discard a card could change. If the players are numerated with 0 (human player), 1 (first AI player), 2 (second AI player) and 3 (third AI player), at the first mance the first to discard a card is the human player, but at the second mance the first to discard could be the 2 AI player and the human player has to wait until all the AI players before him discard a card (in this case, the round would be 2-3-0-1).
How can I cancel the click event if the AI players hasn't discarded a card yet?
UPDATE
I don't always need to wait that all AI players had drawed a card: if the winner of the mance is the number 2, the round would be 2-3-0-1: that means the player has to wait the AI players 2 and 3 drawed, then the player has to click one PictureBox, and the loop will return back to the AI players and then the AI player 1 is allowed to discard its card.
UPDATE 2
I've thought something like that:
int leader = 0; // who is going to discard first
int nextDiscarder = leader; // next player who's going to discard
for (int i = 0; i < nPlayers; i++) // until all the players hasn't discarded
{
if (nextDiscarder == 0) // the human has to discard
{
enablePictureBoxClickEvent;
// now before the loop continue the program has to wait the event click on a picture box
}
else
{
AI[nextDiscarder].discard(); // the ai player will discard
}
if (nextDiscarder == players - 1) // if nextDiscarder has reached the end of the table
nextDiscarder = 0; // return to the begin until all player has discarded a card
else
++nextDiscarder; // continue to discard with the next player
}
and in my event click I'd do something like this:
private myEventClick(object sender, EventArgs e)
{
.... // do the instructions needed to discard a card
disableMyEventClick;
returnToLoop;
}
but the main problem is that I don't know how to write in code my instruction returnToLoop.
I know most of the people will argue that you should use event-driven approach, but async/await feature can be used for easily implementing things like this w/o the need of implementing manually state machines.
I already posted similar approach in Force loop to wait for an event and A Better Way to Implement a WaitForMouseUp() Function?, so basically this is the same helper as in the former with Button replaced with Control:
public static class Utils
{
public static Task WhenClicked(this Control target)
{
var tcs = new TaskCompletionSource<object>();
EventHandler onClick = null;
onClick = (sender, e) =>
{
target.Click -= onClick;
tcs.TrySetResult(null);
};
target.Click += onClick;
return tcs.Task;
}
}
Now all you need is to mark your method as async and use await:
// ...
if (nextDiscarder == 0) // the human has to discard
{
// now before the loop continue the program has to wait the event click on a picture box
await pictureBox.WhenClicked();
// you get here after the picture box has been clicked
}
// ...
I like Ivan solution, because it looks good, and is reusable easily anywhere else you need to wait for a control.
However, I wanted to provide another solution, because I feel like the way are doing this is far more complicated that it could be.
So let's resume this :
At some point in the game, you need players to select a card they don't want to throw it away
There is one human player, which is number 0 in your array
The human player is not always the first to decide which card to throw away.
To decide which card to throw away, you display a picturebox to the player and you wait for him to click on it.
I believe a simple solution could be :
You start by removing the card for the AI players before the human (if human is first to discard, this will do nothing, if human is last, all AI will discard here)
You enable the PictureBox and you end your function
In the click event of the PictureBox, you remove the user card, then you remove the card for the remaining AI players that are after the human (if human is first, all AI will remove a card here, if human is last, you do nothing)
Done...
So this would look like this :
//We need an instance variable, to keep track of the first player
int _firstPlayerToDiscard = 0;
private void StartDiscardingProcess(int FirstToDiscard)
{
_firstPlayerToDiscard = FirstToDiscard;
if (FirstToDiscard != 0) //If the player is the first, we do nothing
{
//We discard for every other AI player after the human player
for (int i = FirstToDiscard; i < nPlayers; i++)
{
AI[i].Discard();
}
}
//Now we fill the PictureBox with the cards and we display it to the player
DiscardPictureBox.Enabled = true;
//or DiscardPictureBox.Visible = true;
//and we are done here, we know basically wait for the player to click on the PictureBox.
}
private void pictureBox_click(Object sender, EventArgs e)
{
//Now we remove the card selected by the player
// ...
//And we remove the cards from the other AI players
//Note that if the player was first to discard, we need to change the instance variable
if (_firstPlayerToDiscard == 0) { _firstPlayerToDiscard = nbPlayers; }
for (int i = 1; i < _firstPlayerToDiscard; i++)
{
AI[i].Discard();
}
}
And you're pretty much done...
NB: Sorry if syntax is bad or unusual, I usually code in VB .Net... Feel free to edit syntax issues...
The following code demonstrates a simple timer based state machine. In this case, the machine's state is the current Player's Turn. This example lets each Play decide when to let the next player have her turn by setting the state to the next player. Add additional states for other things the program should check for. This program architecture runs relatively smoothly because the program threads are not blocked in tight loops. The "faster" each player can complete and exit the turn, the better - even if the player's turn repeats 10000 times without doing anything before letting the next player play.
In the example below, the Click event handler advances the machine state from the Human's turn to the AI's turn. This effectively pauses the game until the Human Clicks. Since the Turn is not blocked in a tight loop, you can have other buttons for the Human to click on like "Pass", "Start Over", and "Quit".
using System;
using System.Windows.Forms;
using System.Timers;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private System.Timers.Timer machineTimer = new System.Timers.Timer();
// These are our Machine States
private const int BEGIN_PLAY = 0;
private const int HUMAN_PLAYER_TURN = 1;
private const int AI_PLAYER_TURN = 2;
// This is the Current Machine State
private int currentPlayer = BEGIN_PLAY;
// Flag that lets us know that the Click Event Handler is Enabled
private bool waitForClick = false;
// The AI members, for example 100 of them
private const int AIcount = 100;
private object[] AIplayer = new object[AIcount];
private int AIcurrentIndex = 0; // values will be 0 to 99
public Form1()
{
InitializeComponent();
this.Show();
// The Timer Interval sets the pace of the state machine.
// For example if you have a lot of AIs, then make it shorter
// 100 milliseconds * 100 AIs will take a minimum of 10 seconds of stepping time to process the AIs
machineTimer.Interval = 100;
machineTimer.Elapsed += MachineTimer_Elapsed;
MessageBox.Show("Start the Game!");
machineTimer.Start();
}
private void MachineTimer_Elapsed(object sender, ElapsedEventArgs e)
{
// Stop the Timer
machineTimer.Stop();
try
{
// Execute the State Machine
State_Machine();
// If no problems, then Restart the Timer
machineTimer.Start();
}
catch (Exception stateMachineException)
{
// There was an Error in the State Machine, display the message
// The Timer is Stopped, so the game will not continue
if (currentPlayer == HUMAN_PLAYER_TURN)
{
MessageBox.Show("Player Error: " + stateMachineException.Message, "HUMAN ERROR!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (currentPlayer == AI_PLAYER_TURN)
{
MessageBox.Show("Player Error: " + stateMachineException.Message, "AI ERROR!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
MessageBox.Show("Machine Error: " + stateMachineException.Message, "Machine ERROR!",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void State_Machine()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
switch (currentPlayer)
{
case HUMAN_PLAYER_TURN:
Play_Human();
break;
case AI_PLAYER_TURN:
Play_AI();
break;
default:
Play_Begin();
break;
}
}
private void Play_Human()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
// My Turn!
if (!waitForClick)
{
// Please Wait until I take a card...
// I am using this.Invoke here because I am not in the same thread as the main form GUI
// If we do not wrap the code that accesses the GUI, we may get threading errors.
this.Invoke((MethodInvoker)delegate
{
pictureBox1.Click += PictureBox1_Click;
});
// set this flag so we do not re-enable the click event until we are ready next time
waitForClick = true;
}
}
private void PictureBox1_Click(object sender, EventArgs e)
{
// This routine is executing in the Main Form's Thread, not the Timer's Thread
// Stop the game for a little bit so we can process the Human's turn
machineTimer.Stop();
// Disable the Click Event, we don't need it until next time
pictureBox1.Click -= PictureBox1_Click;
waitForClick = false;
// To Do: Human's Turn code...
// Let the AI Play now
currentPlayer = AI_PLAYER_TURN;
machineTimer.Start();
}
private void Play_AI()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
if (AIcurrentIndex < AIcount)
{
// If we do not wrap the code that accesses the GUI, we may get threading errors.
this.Invoke((MethodInvoker)delegate
{
// To Do: AI Player's Turn code...
});
// Advance to the next AI
AIcurrentIndex++;
}
else
{
// Reset to the beginning
AIcurrentIndex = 0;
currentPlayer = BEGIN_PLAY;
}
}
private void Play_Begin()
{
// This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
// If we do not wrap the code that accesses the GUI, we may get threading errors.
this.Invoke((MethodInvoker)delegate
{
// ... do stuff to setup the game ...
});
// Now let the Human Play on the next Timer.Elapsed event
currentPlayer = HUMAN_PLAYER_TURN;
// After the Human is done, start with the first AI index
AIcurrentIndex = 0;
}
}
}
i would have design the process in a different way based on events without loop, but following your way you should use an autoreset event to notify your loop myEvent have been fired.
AutoResetEvent clickEventFired = new AutoResetEvent(false); // instanciate event with nonsignaled state
AutoResetEvent clickEventFired = new AutoResetEvent(true); // instanciate event with signaled state
clickEventFired.Reset(); // set state to nonsignaled
clickEventFired.Set(); // set state to signaled
clickEventFirect.WaitOne(); // wait state to be signaled
https://msdn.microsoft.com/en-us/library/system.threading.autoresetevent(v=vs.110).aspx
public static void yourLoop()
{
int leader = 0; // who is going to discard first
int nextDiscarder = leader; // next player who's going to discard
// instanciate auto reset event with signaled state
AutoResetEvent clickEventFired = new AutoResetEvent(true);
for (int i = 0; i < nPlayers; i++) // until all the players hasn't discarded
{
if (nextDiscarder == 0) // the human has to discard
{
enablePictureBoxClickEvent;
clickEventFired.WaitOne(); // wait for event to be signaled
}
else
{
AI[nextDiscarder].discard(); // the ai player will discard
clickEventFired.Reset(); // set event state to unsignaled
}
if (nextDiscarder == players - 1) // if nextDiscarder has reached the end of the table
nextDiscarder = 0; // return to the begin until all player has discarded a card
else
++nextDiscarder; // continue to discard with the next player
}
}
private myEventClick(object sender, EventArgs e)
{
.... // do the instructions needed to discard a card
disableMyEventClick;
clickEventFired.Set(); // signal event
}

Gamestate not functioning properly

All three of my screen/states works fine, however, i implemented a fourth one to act as an information screen. Fine so far, but when i run the game, and press the 'H' key, it doesn't change the screen to another background (what I have done so far). Below is the code:
public void UpdateInformation(GameTime currentTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.H))
{
GameState = 4;
} // GAMESTATE 4 which is the instruction/Information screen.
}
This is the code for the game state in the update method:
protected override void Update(GameTime gameTime)
{
switch (GameState)
{
case 1: UpdateStarted(gameTime);
break;
case 2: UpdatePlaying(gameTime);
break;
case 3: UpdateEnded(gameTime);
break;
case 4: UpdateInformation(gameTime);
break;
}
base.Update(gameTime);
}
Here i am drawing the screen.
public void DrawInformation(GameTime currentTime)
{
spriteBatch.Begin();
spriteBatch.Draw(InfoBackground, Vector2.Zero, Color.White);
spriteBatch.End();
}
Below is the draw information code for the states:
protected override void Draw(GameTime gameTime)
{
switch (GameState)
{
case 1: DrawStarted(gameTime);
break;
case 2: DrawPlaying(gameTime);
break;
case 3: DrawEnded(gameTime);
break;
case 4: DrawInformation(gameTime);
break;
}
}
I hope this helps, it's just my H key is not responding, but my S key responds well and starts the game. Are four state/screens compatible with 'Gamestate'?
Thank you.
The H key will not work, because your Update code for the H key is in UpdateInformation...
What it actually does is: If you're in the Information screen, press H to go to the Information screen (which doesn't make sense)
You should move your H detection code somewhere more appropriate. Where is your S detection code?
Also, I would recommend using an enum instead of numbers for your game states.
enum gameStates
{
Started,
Playing,
Ended,
Information,
}
That way, it's much easier to maintain and understand. (See example below)
switch(GameState)
{
case gameStates.Started:
//Do something
break;
}

Categories