1

So I am making a game for a school project. You might be familiar with this one. Its Arkanoid, or the game in which a ball is used to destroy bricks and is deflected on by a platform.

Currently I am stuck at a point. I have got an idea of how to move my platform using _getch(), but when I put in a ball it is also moving on a key press. I cant figure out on how to run it simultaneously with or without any key presses. Or if there is way to skip a key press every time until it is registered.

Here is my code so far. The movement of the ball is not complete it is just a prototype right now. Any help would be appreciated.

I am using a graphics library provided by my school but you can use the C++ graphics library.

#include <iostream>
#include "myconsole.h"
#include "mygraphics.h"
#include <conio.h>
#include <string>

using namespace std;

void rect() {
    COLORREF White = RGB(255, 255, 255);
    COLORREF Blue = RGB(135, 206, 235);
    //     x1   y    x2   y 
    myRect(400, 475, 550, 480, Blue, Blue);
    _getch();
}

void circle() {
    COLORREF Red = RGB(255, 0, 0);
    myEllipse(0, 50, 300, 350, Red, Red);
    _getch();
}

void moverect() {
    COLORREF White = RGB(255, 255, 255);
    COLORREF Blue = RGB(135, 206, 235);
    char _x;
    const char _r = 'd';
    const char _l = 'a';
    int x1 = 400; 
    int x2 = 550;
    int by1 = 455;
    int by2 = 475;
    int m = 48;
    
    while (1) {
        _x = _getch();
        system("cls");
        if (_x == _r) {
            if (x2 < 970) {
                x1 += 10;
                x2 += 10;
                for (int i = 0; i < 10; i++) {
                    myRect(x1++, 475, x2++, 480, Blue, Blue);
                }
            }
            else
                myRect(x1, 475, x2, 480, Blue, Blue);
        }
        else if (_x == _l) {
            if (x1 > 0) {
                x1 -= 10;
                x2 -= 10;
                for (int i = 0; i < 10; i++) {
                    myRect(x1--, 475, x2--, 480, Blue, Blue);

                }
            }
            else
                myRect(x1, 475, x2, 480, Blue, Blue);
        }
        myEllipse(463, by1 -= 10, 487, by2 -= 10, White, White);
    }
}

int main() {
    {
        moverect();
        return 0;
    }
}
ocrdu
  • 2,172
  • 6
  • 15
  • 22
  • 2
    What you are asking about is threading, or event driven programming. It's an advanced topic. Since you are learning it might be a good idea to try something easier for your project. – john Dec 26 '20 at 09:50
  • How is this related to Java? Please don't spam unrelated tags. Also please read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Dec 26 '20 at 09:52
  • Maybe, this gives some inspiration: [SO: I/O in concurrent program](https://stackoverflow.com/a/48097134/7478597) – Scheff's Cat Dec 26 '20 at 09:53
  • Any help would be appreciated, and yes I did see some posts about multiple threading and it went over my head. @john – TheWhiteKnight Dec 26 '20 at 09:59
  • 1
    Don’t try to use threads to solve your problem. They’re an unnecessary complication. – Sneftel Dec 26 '20 at 10:03
  • If there is a way to solve this problem without threads, I am willing to go for it. @Sneftel – TheWhiteKnight Dec 26 '20 at 10:06
  • @user14891171 There's no way in standard C++ to do what you want (except threading) so you need to look to platform specific solutions. Even a simple function like `_getch` is not standard C++. So the answer depends on what platform you are on. It's not an area I know much about however. – john Dec 26 '20 at 10:27
  • I tried to implement threading but it did not work, If there is a way I can do it with threading Ill go with it then I guess. @john – TheWhiteKnight Dec 26 '20 at 11:00

1 Answers1

2

Since you seem to be using Windows and Microsoft Visual C++ you could use _kbhit(), which will tell you if a key is pressed. Note that this is not standard C++.

This might look like:

if (_kbhit()) {
    _x = _getch();
}
else {
    _x = -1;
}

Alternatively, others mentioned threads, although John also noted that _getch() is also not standard C++, but I thought introducing some of the concepts of threads might be helpful.There are multiple ways to do it with threads but I will show what I consider to be the easiest way.

First we will create a class, this is to hide a variable that contains the latest character. We want it hidden so that when it is read from the main thread, the latest character is consumed (set to -1) so that when we next call the function, if another character is yet to be input the function will return -1.

Note that if two threads read and write from a variable at the same time, undefined behaviour occurs, for this reason we are going to use an atomic variable as it is defined when two threads try to access it at the same time. Alternatively mutexes exist, they are slower but allow for more complex types to be locked and unlocked so that only one thread may access them at a time.

class LatestChar {
public:
    /// initialize latest character to -1
    LatestChar() :
        latest_char{ -1 }
    {}
    /// returns latest character or -1 if there is no latest character
    int getLatestChar() {
        // get latest character
        int temp_latest_char{ latest_char };
        // consume latest character (so if this is called again, -1 is returned)
        latest_char = -1;
        // return latest character
        return temp_latest_char;
    }
private:
    /// equal to latest character or -1 when there is no character to get
    ///
    /// this is atomic because it might be read from the main thread at the same
    /// time it is written from the getch thread.
    std::atomic_int latest_char;
};

Next we need to create the thread that calls _getch(). This is a member variable and will be declared in the constuctor, where it will start running and getting the latest character and repeating.

class LatestChar {
public:
    /// initialize latest character to -1,
    /// and starts thread that gets the latest character
    LatestChar() :
        ...,
        getch_thread([this] {
            while (true) {
                latest_char = _getch();
            }
        })
    {}
    ...
private:
    ...
    /// this thread loops until told to stop, and updates the latest character
    std::thread getch_thread;
};

Almost done, we now need to rejoin the thread at the end of the program, we will use another atomic variable, this time a boolean, to represent a flag to say "Hey, we are done getting characters, finish what you are doing and join back up with the main thread.

Note that the getch thread will only check this after it reads a character, so when you close your program you might need to input one more character, the alternative is to call something like std::terminate() which will stop the entire program forcefully, which is probably not desired when it can be avoided.

We will initialize the flag to false in the constructor (make sure it is initialized before the getch thread is, otherwise the getch thread might check the value before it has been set to false. The order is they are initialized is the same order they are declared in the class definition.), check it in the thread, and set it to true in the destructor and then also call the join function which will wait until the thread is finished and connect join it back into main.

class LatestChar {
public:
    /// initialize latest character to -1, end thread flag to false,
    /// and starts thread that gets the latest character
    LatestChar() :
        ...,
        end_thread{ false },
        getch_thread([this] {
            while (!end_thread) {
                latest_char = _getch();
            }
        })
    {}
    /// sets end thread flag to true and joins getch thread with main thread
    ~LatestChar() {
        end_thread = true;
        getch_thread.join();
    }
    ...
private:
    ...
    /// this flag tells the getch thread to stop, like at the end of a program,
    /// _getch() will need to receive a character before this is checked
    /// 
    /// this is atomic because it might be read from the getch thread at the
    /// same time it is written from the main thread.
    ///
    /// make sure this is initialized before the getch thread begins
    std::atomic_bool end_thread;
    ...
};

Finally, let's instantiate the class and call the function.

// somewhere not in a loop
LatestChar latest_char;
...
// somewhere is a loop
_x = latest_char.getLatestChar();
Lily
  • 1,386
  • 2
  • 13
  • 16