How to create a timer using C++ only and no windows API

I am writing a game similar to the Fallout shelter part where they explore the wasteland. I would like the game to do something every minute like say some dialogue or find an item etc, but I cant seem to get a decent clock to work, and I havent found anything reliable on the web, at least anything that doesnt use Sleep(), maybe something that uses chrono?. I would like the code to use C++ only. Here's what I have so far, please excuse the bad code, I havent gotten around to cleaning it up yet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <limits>
#include <chrono>
#include <thread>

using namespace std;

class Inventory
{
    public:
        int money = 0;
        int inventory[3] = {0, 0, 0};
};

void Explore(int &randNum)
{
    randNum = rand() % 200;

    int miliseconds = 60000;
    int counter = 0;

    while(true)
    {
        if(counter >= miliseconds)
        {
            if(randNum >= 0 && randNum <= 100)
            {
                cout << "Glad it's sunny out." << endl;
                counter = 0;
            }
            if(randNum > 100 && randNum <= 150)
            {
                cout << "My feet hurt." << endl;
                counter = 0;
            }
            if(randNum > 150 && randNum <= 200)
            {
                cout << "Is that a lake?" << endl;
                counter = 0;
            }
        }

        counter++;
    }
}

void PlayerInventory(Inventory &inv)
{
    cout << "INVENTORY" << endl;

    cout << "Money $" << inv.money << endl;
    cout << "Handguns " << inv.inventory[0] << endl;
    cout << "Shotguns " << inv.inventory[1] << endl;
    cout << "Medpacks " << inv.inventory[2] << endl;
}

void Shop(Inventory &inv)
{
    cout << "WELCOME TO THE SHOP" << endl;

    int choice = 0;
    int selectItem = 0;

    cout << "\n1) Buy" << endl;
    cout << "2) Sell" << endl;
    cin >> choice;

    if(choice == 1)
    {
        while(selectItem != -1)
        {

            cout << "1) Medkit = $30" << endl;
            cout << "2) Pistol Ammo (x10) = $15" << endl;
            cin >> selectItem;

            switch(selectItem)
            {
                case 1:
                    if(inv.money >= 30)
                    {
                        cout << "Thank you for your purchase!" << endl;
                        inv.money -= 30;
                        cout << "Current Money $" << inv.money << endl;

                    }
                    if(inv.money <= 30)
                    {
                        cout << "Sorry but you do not possess the necessary funds to purchase that item, you need $30 and you have $" << inv.money << "!\n" << endl;
                    }
                    break;

                case 2:
                    break;
            }
        }
    }
    if(choice == 2)
    {

        while(selectItem != -1)
        {
                cout << "What would you like to sell? Type -1 to exit.\n\n" << endl;
                cout << "1) Handguns, you have " << inv.inventory[0] << " Buy price = $15" << endl;
                cout << "2) Shotguns, you have " << inv.inventory[1] << " Buy price = $20" << endl;
                cout << "3) Medpacks, you have " << inv.inventory[2] << " Buy price = $10" << endl;
                cin >> selectItem;

                switch(selectItem)
                {
                    case 1:
                        if(inv.inventory[0] >= 1)
                        {
                            cout << "Ok 1 handgun, thanks!" << endl;
                            inv.inventory[0] -= 1;
                        }
                        else if(inv.inventory[0] < 1)
                        {
                            cout << "You do not have any Handguns to sell!" << endl;
                        }
                        break;

                    case 2:
                        if(inv.inventory[1] >= 1)
                        {
                            cout << "Ok 1 shotgun, thanks!" << endl;
                            inv.inventory[1] -= 1;
                        }
                        else if(inv.inventory[1] < 1)
                        {
                            cout << "You do not have any Shotguns to sell!" << endl;
                        }
                        break;

                    case 3:
                        if(inv.inventory[2] >= 1)
                        {
                            cout << "Ok 1 medpack, thanks!" << endl;
                            inv.inventory[2] -= 1;
                        }
                        else if(inv.inventory[2] < 1)
                        {
                            cout << "You do not have any medpacks to sell!" << endl;
                        }
                        break;
                }
        }
    }

}

void SaveFile()
{

}

void LoadFile()
{

}

int main()
{
    srand(time(0));

    string enemyName;
    int choice = 0;

    Inventory inv;

    while(choice != -1)
    {
        int num = rand() % 3;

        cout << "Do what?\n" << endl;
        cout << "1) Explore" << endl;
        cout << "2) Check Inventory" << endl;
        cout << "3) Shop" << endl;
        //cin >> choice;

        Explore(num);

        switch(choice)
        {
            case 1:
                Explore(num);
                break;
            case 2:
                PlayerInventory(inv);
                break;
            case 3:
                Shop(inv);
                break;
        }
    }

    return 0;
}
Last edited on
in your Explore() somewhere? This function still seems a bit rough because you still need conditions for stopping. Perhaps you want to explore for 10 seconds at most or so.

But generally to answer your q, start a thread, have it do <action> and have it sleep for the tickrate (don't sleep in main thread), before possibly doing another action.
An example of a timer...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <ctime>

using namespace std;

int main() {

	double timepassed = 0.0;
	time_t start = time(NULL); //gets the number of seconds since Jan. 1 2000.
	time_t end = time(NULL);

	while (timepassed < 5) { //counts 5 seconds
			
		cout << timepassed << endl;
		end = time(NULL);
		timepassed = difftime(end, start);
	}
	system("pause");
	return 0;
}
awesome thank you, but why use system()? I excluded it as i dont want to use platform specific code and it seems to work just fine.
time_t start = time(NULL); //gets the number of seconds since Jan. 1 2000

That's an implementation-dependent assumption. On linux the epoch is 00:00:00 UTC on 1 January 1970.

1
2
3
4
5
#include <iostream>
#include <ctime>
int main() {
    std::cout << time(0)/(60*60*24*365.25) << " years\n";   // Prints  48.5454 years
}

Last edited on
so that snippet is more reliable that just time_t start = time(NULL);?
Are you talking to me? I wasn't talking to you. :-)
yes :), I was just wondering, I didnt know that code was platform specific.
Only his comment was platform specific (or more technically, unspecified). In fact, technically a time_t value is pretty much opaque. It doesn't necessarily even indicate seconds, but I don't know of any implementations where it doesn't. It's a C function anyway, and C++ has better time functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <chrono>
 
void wait(double seconds) {
    auto start = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> diff;
    do {
        diff = std::chrono::high_resolution_clock::now()-start;
    } while (diff.count() < seconds);
}

int main() {
    std::cout << "Start\n";
    wait(5);   // wait for 5 seconds
    std::cout << "End\n";
}


Note however that this is a "busy wait" loop. icy1's thread idea is better:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <chrono>
#include <thread>
 
int main() {
    using namespace std::chrono_literals;
    std::cout << "Start" << std::endl; // endl to flush output
    auto start = std::chrono::high_resolution_clock::now();
    std::this_thread::sleep_for(5s);
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> elapsed = end-start;
    std::cout << "Done: " << elapsed.count() << " ms\n";
}

OK tpb.

Thank you for your correction. I don't know much about Linux os. But the timer still works.

@ch1156

You are correct. Do not include the system command. That is just a bad habit I get into when making short programs.
Thank you for your correction. I don't know much about Linux os. But the timer still works.

Did I say the timer doesn't work? I was just wondering where you got the Jan 1, 2000 date. Did you just pull it out of your ass or do you have a reference? Linux was just an example. You should run the code on your system and see what it says.
@tpb

Your eloquent manner of calling it like it is never fails to be refreshing. The example posted here came from some old tutorial I saved but can't remember from where. I refer back to old code but don't save the source for some reason.

When I run your time code it tells me that it is something like 48 and a half years. Which proves conclusively, that the comment about the year 2000, beyond any doubt, came from the depths of my ass.

Again I have been corrected...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
#include <atomic>
#include <random>

using namespace std::chrono_literals;

class Game
{
public:
    Game() :
        tick_rate_(2),
        quotes_{"\"Stay hungry. Stay foolish.\" --Steve Jobs",
                "\"Good artists copy; great artists steal.\" --Pablo Picasso",
                "\"Argue with idiots, and you become an idiot.\" --Paul Graham",
                "\"Be yourself; everyone else is already taken.\" --Oscar Wilde",
                "\"Simplicity is the ultimate sophistication.\" --Leonardo Da Vinci"}
    {        
    }
    ~Game()
    {
        ticking_.store(false);
    }

    void Tick()
    {
        while(ticking_.load())
        {
            TickAction();
            std::this_thread::sleep_for(tick_rate_);
        }
    }
    void TickAction()
    {
        std::cout << '\n' << quotes_[rd_() % quotes_.size()] << '\n';
    }

    void Explore(std::chrono::milliseconds millis)
    {     
        auto elapsed = 0ms;
        auto interval = 50ms;
        ticking_.store(true);
        std::thread t(&Game::Tick, this);
        t.detach();

        while (elapsed < millis)
        {
            std::cout << '.' << std::flush;
            elapsed += interval;
            std::this_thread::sleep_for(interval);
        }
        ticking_.store(false);
        
        std::cout << "\n\nDone exploring!\n";
    }
private:
    std::chrono::seconds tick_rate_;
    std::atomic<bool> ticking_;
    std::random_device rd_;
    std::vector<std::string> quotes_;    
};

int main()
{
    Game g;
    g.Explore(10'000ms);

    return 0;
} 


Was thinking something like this. Explore for 10 seconds, outputting some quote every 2 seconds. (I also chose to output right away, so you get a total of 6 quotes). Also outputs "." every interval check, even with a std::flush (expensive operation when done so often) just for demonstration purposes -- this is how often it checks whether it hits that 10 second mark

https://repl.it/@icy_1/PalatableSweetChapters

Perhaps can be made more robust with mutexes for writes to cout and stuff, especially if you plan on having multiple tickers. /shrug
Last edited on
Topic archived. No new replies allowed.