0

I am making a small console application game in C#. The thing is, you have to run around and collect gold. You have to do it under 1 minute otherwise you die. I made it work the way that I have made an enum including: Runs, Won, Lost, Ends statements. The main loop runs while the game state of the game is: Runs. Something like:

switch (...) {
    case PlayGame:
        Console.Clear();
        GameWorld gameWorld = new GameWorld();

        gameWorld.Update();
        while (gameWorld.GameState == GameState.Runs) {
          gameWorld.PlayerMovement();
          gameWorld.Update();
        }
        break;
    case ...
}

Now everything works fine.. The last thing is, that I have to make this "timer", that would count from 1 to 60 seconds and also print it on the screen at the given coordinates. I tried doing it many different ways. For example I made a method inside the GameWorld class (that is where I have the sort of core of the game) and called it Time. I made a variable Time and assigned the value of 60 to it. Then I made a for loop which set the cursor at the given coordinates, wrote the value which was raising with every loop and set Thread.Sleep(1000); as the last thing .. That basically added 1 to the value every second untill it got to 59. Now the thing is, that when I ran this method at the main loop (while), it counted to 60 seconds first and then it ran anything below it. The same thing was happening when I ran it below all the commands in the while loop. Do you guys have any idea how could I make that "countdown" run side by side with running main loop? Thank you for your answers and time!

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
Root
  • 3
  • 3
  • Welcome to Stack Overflow! Please, do not include information about a language used in a question title unless it wouldn't make sense without it. Tags serve this purpose. – Ondrej Janacek Mar 18 '14 at 19:51
  • You can either put your timer on a separate thread (since you're using the `Thread.Sleep` command, you can't put your main thread to sleep) add / use a timer object and just have it update the clock on each tick event – John Bustos Mar 18 '14 at 19:57

4 Answers4

2

While it is possible to start a separate thread to continuously run a timer to have the countdown run side by side, I would just keep track of the start time and print the updated seconds as part of the Update() method (or wherever it makes most sense, possibly a separate method call in the main game loop).

Either the first time Update() is called, when the GameWorld is created or even a new Start() method, store the current time using DateTime.UtcNow and print the 60 where it needs to go. On the next/subsequent updates, get the new current time, find the difference and subtract that from 60 seconds to get the time remaining.

Edit, a little more explanation:

Here is some code to get you started, you can then plug in the calls to Start() and PrintCountdown() where appropriate, I want to let you do as much of the work as I can :).

class GameWorld {
    ...
    DateTime startTime;

    void Start() {
        startTime = DateTime.UtcNow;
    }

    void PrintCountdown() {
        DateTime currentTime = DateTime.UtcNow;
        TimeSpan gameRunTime = currentTime - startTime;
        int timeRemaining = 60 - gameRunTime.TotalSeconds();
        // Whatever code you have to print the countdown time
    }
    ...
}

Edit 2:

As hatchet pointed out, DateTime.UtcNow is much faster thant DateTime.Now. It probably doesn't matter too much for your game, but using DateTime.UtcNow would shave off a few cycle in your game loop.

Cemafor
  • 1,633
  • 12
  • 27
  • DateTime.UtcNow() is much faster, and will not cause problems if someone is using the app at the moment time changes between standard time and daylight savings. – hatchet - done with SOverflow Mar 18 '14 at 20:06
  • @hatchet It doesn't matter whether you're using local time or not. When you get the difference between the two times as a `TimeSpan` it is timezone agnostic. – Servy Mar 18 '14 at 20:08
  • Thanks, as I am reading through all these comments, I am thinking: Is there any directive for this time measuring ? Or could you guys be a little more specific please? I sort of understand what am I supposed to do. I need to start measuring the time when the game starts and then keep adding that 1 sec. utill it reaches 60 seconds. When it does, it shows a message or whatever. I just need a little more help with this, since I consider myself a beginner. Thank you all. – Root Mar 18 '14 at 20:18
  • @hatchet The whole point of a `DateTime` object is that is manages those abstractions away from you. When you say 4/9/2014 3:01 - 4/9/2014 2:00 you end up with 1 minute, not 1 hour and one minute. That's how much time passed between those two times (due to DST). If you grab `DateTime.Now`, and then grab it again a minute later, even if that time spans across DST jumps, you still get a timespan representing one minute. – Servy Mar 18 '14 at 20:22
  • @Cemafor Thank you Cemafor! It seems to be working. But there is a problem with it. It counts it down only when I press a key. Otherwise it just stays on it's value. – Root Mar 18 '14 at 20:40
  • @Root, You must be waiting for input in the `PlayerMovement()` method. This may require you to post another question, but I would test if there is any input available and just continue if not. This will allow the clock to run and allow you to add other moving pieces to the game (such as enemies) that move even while the player is standing still. – Cemafor Mar 18 '14 at 20:48
  • @Servy - that is not the impression I get when reading this http://msdn.microsoft.com/en-us/library/bb546099(v=vs.110).aspx . It says arithmetic returns difference in clock time. Also http://stackoverflow.com/questions/9308476/daylight-saving-issue-while-calculating-elapsed-time-in-c-sharp where the person describes having this exact problem. – hatchet - done with SOverflow Mar 18 '14 at 21:11
  • @Cemafor Thanks! What I did, was that I checked if any key is available. If it is, then run normal draw/update loop. If there is some key available however, it prints the countdown. And it actually worked! So thanks again! – Root Mar 18 '14 at 21:17
  • @hatchet, one important distinction with the SO question is they are parsing a date/time string where there is no indication whether it was the first or second 1:00am. Using DateTime.Now, I could see the structure knowing exactly when the DateTime was taken, although I do not know for sure if that is the case. – Cemafor Mar 18 '14 at 21:24
  • @Cemafor - that could be. But for uses such as your answer here, there is no doubt that UtcNow will work perfectly over the time transition, and it is significantly faster than Now. So I see advantages, but no drawbacks to using it when you're just wanting elapsed times. – hatchet - done with SOverflow Mar 18 '14 at 21:36
  • @hatchet: Wow, it is a lot faster (about 66 times faster based on my simple test), and it is true that it will definitely work. – Cemafor Mar 18 '14 at 21:47
  • @hatchet As was stated earlier, the issue is that the datetime is being incorrectly parsed, due to an ambiguity. That will never be the case when using `DateTime.Now`. It will always know which of the two times it represents. The storage of the time is always in an unambigous medium; it stored the number of ticks since a certain time in the past, along with some timezone information that can be used when displaying the time as a string. When you cross a DST border the underlying tick values never jump around; the display values of those ticks simply change. – Servy Mar 19 '14 at 13:52
  • @hatchet This means that subtracting two dates is as simple as subtracting two `long` values (so long as the timezones are the same, which they will be here). This will be the case even across DST borders, because that only really affects the string representation of the date, not the numeric representation of the date. – Servy Mar 19 '14 at 13:53
  • @Servy - You're misunderstanding how DateTime works. I suggest you run a test, as I have where you set the local time on your machine to a few minutes before Mar 9 2am. Run a little test program that shows elapsed time using Now, and another using UtcNow. They will be the same until 2am hits, and the clock jumps to 3am. At that point, the elapsed time using Now will instantly increase by 1 hour. I have tested this, and seen it happen. Jon Skeet concurs: http://stackoverflow.com/questions/5178595/difference-between-2-datetimes-in-hours as does MSDN in earlier link that says diff is clock time. – hatchet - done with SOverflow Mar 19 '14 at 14:39
0

You could just check the system clock (using DateTime.Now, for example) somewhere and compare it against the starting time of the program, checking for when 60 seconds has elapsed.

SlimsGhost
  • 2,849
  • 1
  • 10
  • 16
-1

You can have a timer which pings you up on specified time intervals and you can handle that event to do your updates. Example

case PlayGame:
        Console.Clear();
        GameWorld gameWorld = new GameWorld();
        gameWorld.Update();

        //Timer to ping you every second
        Timer aTimer = new Timer
        {
            Interval = 1000,    //change this to fit your needs
            Enabled = true
        };
         aTimer.Elapsed+=this.OnTimedEvent;


        while (gameWorld.GameState == GameState.Runs)
        {
            gameWorld.PlayerMovement();
            gameWorld.Update();
        }

Here's the event handler

    private void OnTimedEvent(object sender, ElapsedEventArgs e)
    {
        //updates here?
    }
SirH
  • 535
  • 2
  • 7
  • This is not the proper design for a game. The whole purpose of a game loop is to avoid code like this. – Servy Mar 18 '14 at 20:04
-1

EDIT: if the scope is to run movement at every second for 60s then using Stopwatch should be the most accurate method. (it will also do a lot of loops...)

        var gameWorld = new GameWorld ();
        var stopwatch = Stopwatch.StartNew ();
        gameWorld.Update();
        for (var n = 0; n < 60;)
        {
            if ((int)(stopwatch.ElapsedMilliseconds / 1000) > n)
            {
                gameWorld.PlayerMovement ();
                gameWorld.Update ();
                ++n;
                Console.WriteLine ("{0}ms elapsed.", stopwatch.ElapsedMilliseconds);
            }
            Thread.Sleep(1);
        }
SKall
  • 5,234
  • 1
  • 16
  • 25
  • Using a second thread to manage this is not a proper design for this problem, either in general, but in particular for a game. It consumes a *lot* of system resources to do this, and for a game this is simply unacceptably high in its performance costs. It just doesn't scale based on the shear number of time-based actions are going on. You can't have thousands of threads (all of which are doing nothing most of the time) to manage all of the things that will happen in some period of time. It also lacks precision; the given thread could go for quite some time without being scheduled. – Servy Mar 18 '14 at 20:24
  • So a thread spawn every second is too much, ok, then just put in a stopwatch on the main thread and check when the total seconds go over 60s. Simple and done. For the precision I don't really think it is in the scope of the question (the above code seemed to run with +-1ms precision on my machine). – SKall Mar 18 '14 at 20:33
  • The precision can easily get as bad as ~16 ms or so, and that's with just a few threads. If the application scales and there are thousands of operations trying to all do this you'll crash the entire process, and before you crash it, you're precision will have ground to much, much longer than just a few milliseconds due to the thread starvation. – Servy Mar 18 '14 at 20:37
  • It's not a matter of a percentage. That's still enough for a perceivable lag, when playing a game. These lags can also become additive, as you compose various operations that each internally have their own lag the potentially lag of the aggregate operator is that much more. – Servy Mar 18 '14 at 20:42