14 min read

C++ is one of the most popular languages for game development as it supports a variety of coding styles that provide low-level access to the system. In this article by Druhin Mukherjee, author of the book C++ Game Development Cookbook, we will go through a basics of game development using C++.

(For more resources related to this topic, see here.)

Creating your first simple game

Creating a simple text-based game is really easy. All we need to do is to create some rules and logic and we will have ourselves a game. Of course, as the game gets more complex we need to add more functions. When the game reaches a point where there are multiple behaviors and states of objects and enemies, we should use classes and inheritance to achieve the desired result.

Getting ready

To work through this recipe, you will need a machine running Windows. You also need to have a working copy of Visual Studio installed on your Windows machine. No other prerequisites are required.

How to do it…

In this recipe, we will learn how to create a simple luck-based lottery game:

  1. Open Visual Studio.
  2. Create a new C++ project.
  3. Select Win32 Console Application.
  4. Add a Source.cpp file.
  5. Add the following lines of code to it:
    #include <iostream>
    
    #include <cstdlib>
    
    #include <ctime>
    
     
    
    int main(void) {
    
    srand(time(NULL)); // To not have the same numbers over and over again.
    
     
    
    while (true) { // Main loop.
    
        // Initialize and allocate.
    
    intinumber = rand() % 100 + 1 // System number is stored in here.
    
    intiguess; // User guess is stored in here.
    
    intitries = 0; // Number of tries is stored here.
    
    charcanswer; // User answer to question is stored here.
    
     
    
     
    
     
    
    while (true) { // Get user number loop.
    
          // Get number.
    
    std::cout<< "Enter a number between 1 and 100 (" << 20 - itries<< " tries left): ";
    
    std::cin>>iguess;
    
    std::cin.ignore();
    
     
    
          // Check is tries are taken up.
    
    if (itries>= 20) {
    
    break;
    
          }
    
     
    
          // Check number.
    
    if (iguess>inumber) {
    
    std::cout<< "Too high! Try again.n";
    
          }
    
    else if (iguess<inumber) {
    
    std::cout<< "Too low! Try again.n";
    
          }
    
    else {
    
    break;
    
          }
    
     
    
          // If not number, increment tries.
    
    itries++;
    
        }
    
     
    
        // Check for tries.
    
    if (itries>= 20) {
    
    std::cout<< "You ran out of tries!nn";
    
        }
    
    else {
    
          // Or, user won.
    
    std::cout<< "Congratulations!! "<<std::endl;
    
    std::cout<< "You got the right number in "<<itries<< " tries!n";
    
        }
    
     
    
    while (true) { // Loop to ask user is he/she would like to play again.
    
          // Get user response.
    
    std::cout<< "Would you like to play again (Y/N)? ";
    
    std::cin>>canswer;
    
    std::cin.ignore();
    
     
    
          // Check if proper response.
    
    if (canswer == 'n' || canswer == 'N' || canswer == 'y' || canswer == 'Y') {
    
    break;
    
          }
    
    else {
    
    std::cout<< "Please enter 'Y' or 'N'...n";
    
          }
    
        }
    
     
    
        // Check user's input and run again or exit;
    
    if (canswer == 'n' || canswer == 'N') {
    
    std::cout<< "Thank you for playing!";
    
    break;
    
        }
    
    else {
    
    std::cout<< "nnn";
    
        }
    
      }
    
     
    
      // Safely exit.
    
    std::cout<< "nnEnter anything to exit. . . ";
    
    std::cin.ignore();
    
    return 0;
    
    }

How it works…

The game works by creating a random number from 1 to 100 and asks the user to guess that number. Hints are provided as to whether the number guessed is higher or lower than the actual number. The user is given just 20 tries to guess the number. We first need a pseudo seeder, based on which we are going to generate a random number. The pseudo seeder in this case is srand. We have chosen TIME as a value to generate our random range.

We need to execute the program in an infinite loop so that the program breaks only when all tries are used up or when the user correctly guesses the number. We can set a variable for tries and increment for every guess a user takes. The random number is generated by the rand function. We use rand%100+1 so that the random number is in the range 1 to 100. We ask the user to input the guessed number and then we check whether that number is less than, greater than, or equal to the randomly generated number. We then display the correct message. If the user has guessed correctly, or all tries have been used, the program should break out of the main loop. At this point, we ask the user whether they want to play the game again.

Then, depending on the answer, we go back into the main loop and start the process of selecting a random number.

Creating your first window

Creating a window is the first step in Windows programming. All our sprites and other objects will be drawn on top of this window. There is a standard way of drawing a window. So this part of the code will be repeated in all programs that use Windows programming to draw something.

Getting ready

You need to have a working copy of Visual Studio installed on your Windows machine.

How to do it…

In this recipe, we will find out how easy it is to create a window:

  1. Open Visual Studio.
  2. Create a new C++ project.
  3. Select a Win32 Windows application.
  4. Add a source file called Source.cpp.
  5. Add the following lines of code to it:
    #define WIN32_LEAN_AND_MEAN
    
     
    
    #include <windows.h>   // Include all the windows headers.
    
    #include <windowsx.h>  // Include useful macros.
    
    #include "resource.h"
    
     
    
    #define WINDOW_CLASS_NAMEL"WINCLASS1"
    
     
    
     
    
    voidGameLoop()
    
    {
    
      //One frame of game logic occurs here...
    
    }
    
     
    
    LRESULT CALLBACK WindowProc(HWND _hwnd,
    
    UINT _msg,
    
    WPARAM _wparam,
    
    LPARAM _lparam)
    
    {
    
      // This is the main message handler of the system.
    
    PAINTSTRUCTps; // Used in WM_PAINT.
    
    HDChdc;        // Handle to a device context.
    
     
    
      // What is the message?
    
    switch (_msg)
    
      {
    
    caseWM_CREATE:
    
      {
    
                // Do initialization stuff here.
    
     
    
                // Return Success.
    
    return (0);
    
      }
    
    break;
    
     
    
    caseWM_PAINT:
    
      {
    
               // Simply validate the window.
    
    hdc = BeginPaint(_hwnd, &ps);
    
     
    
               // You would do all your painting here...
    
     
    
    EndPaint(_hwnd, &ps);
    
     
    
               // Return Success.
    
    return (0);
    
      }
    
    break;
    
     
    
    caseWM_DESTROY:
    
      {
    
                 // Kill the application, this sends a WM_QUIT message.
    
    PostQuitMessage(0);
    
     
    
                 // Return success.
    
    return (0);
    
      }
    
    break;
    
     
    
     
    
    default:break;
    
      } // End switch.
    
     
    
      // Process any messages that we did not take care of...
    
     
    
    return (DefWindowProc(_hwnd, _msg, _wparam, _lparam));
    
    }
    
     
    
    intWINAPIWinMain(HINSTANCE _hInstance,
    
    HINSTANCE _hPrevInstance,
    
    LPSTR _lpCmdLine,
    
    int _nCmdShow)
    
    {
    
    WNDCLASSEXwinclass; // This will hold the class we create.
    
    HWNDhwnd;           // Generic window handle.
    
      MSG msg;             // Generic message.
    
     
    
    HCURSORhCrosshair = LoadCursor(_hInstance, MAKEINTRESOURCE(IDC_CURSOR2));
    
     
    
      // First fill in the window class structure.
    
    winclass.cbSize = sizeof(WNDCLASSEX);
    
    winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    
    winclass.lpfnWndProc = WindowProc;
    
    winclass.cbClsExtra = 0;
    
    winclass.cbWndExtra = 0;
    
    winclass.hInstance = _hInstance;
    
    winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    
    winclass.hCursor = LoadCursor(_hInstance, MAKEINTRESOURCE(IDC_CURSOR2));
    
    winclass.hbrBackground =
    
    static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
    
    winclass.lpszMenuName = NULL;
    
    winclass.lpszClassName = WINDOW_CLASS_NAME;
    
    winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    
     
    
      // register the window class
    
    if (!RegisterClassEx(&winclass))
    
      {
    
    return (0);
    
      }
    
     
    
      // create the window
    
    hwnd = CreateWindowEx(NULL, // Extended style.
    
    WINDOW_CLASS_NAME,      // Class.
    
    L"Packt Publishing",   // Title.
    
    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    
        0, 0,                    // Initial x,y.
    
        400, 400,                // Initial width, height.
    
        NULL,                   // Handle to parent.
    
        NULL,                   // Handle to menu.
    
        _hInstance,             // Instance of this application.
    
        NULL);                  // Extra creation parameters.
    
     
    
    if (!(hwnd))
    
      {
    
    return (0);
    
      }
    
     
    
      // Enter main event loop
    
    while (true)
    
      {
    
        // Test if there is a message in queue, if so get it.
    
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    
        {
    
          // Test if this is a quit.
    
    if (msg.message == WM_QUIT)
    
          {
    
    break;
    
          }
    
     
    
          // Translate any accelerator keys.
    
    TranslateMessage(&msg);
    
          // Send the message to the window proc.
    
    DispatchMessage(&msg);
    
        }
    
     
    
        // Main game processing goes here.
    
    GameLoop(); //One frame of game logic occurs here...
    
      }
    
     
    
      // Return to Windows like this...
    
    return (static_cast<int>(msg.wParam));
    
    }

How it works…

In this example, we have used the standard Windows API callback. We query on the message parameter that is passed and, based on that, we intercept and perform suitable actions. We have used the WM_PAINT message to paint the window for us and the WM_DESTROY message to destroy the current window. To paint the window, we need a handle to the device context and then we can use BeginPaint and EndPaint appropriately. In the main structure, we need to fill up the Windows structures and specify the current cursor and icons that need to be loaded. Here, we can specify what color brush we are going to use to paint the window. Finally, the size of the window is specified and registered. After that, we need to continuously peek messages, translate them, and finally dispatch them to the Windows procedure.

Adding artificial intelligence to a game

Adding artificial intelligence to a game may be easy or extremely difficult, based on the level of realism or complexity we are trying to achieve. In this recipe, we will start with the basics of adding artificial intelligence.

Getting ready

To work through this recipe, you will need a machine running Windows and a version of Visual Studio. No other prerequisites are required.

How to do it…

In this recipe, we will see how easy it is to add a basic artificial intelligence to the game. Add a source file called Source.cpp. Add the following code to it:

// Basic AI : Keyword identification

 

#include <iostream>

#include <string>

#include <string.h>

 

 

std::stringarr[] = { "Hello, what is your name ?", "My name is Siri" };

 

int main()

{

 

std::stringUserResponse;

 

std::cout<< "Enter your question? ";

std::cin>>UserResponse;

 

if (UserResponse == "Hi")

  {

std::cout<<arr[0] <<std::endl;

std::cout<<arr[1];

  }

 

 

int a;

std::cin>> a;

return 0;

 

}

How it works…

In the previous example, we are using a string array to store a response. The idea of the software is to create an intelligent chat bot that can reply to questions asked by users and interact with them as if it were human. Hence the first task was to create an array of responses. The next thing to do is to ask the user for the question. In this example, we are searching for a basic keyword called Hi and, based on that, we are displaying the appropriate answer.  Of course, this is a very basic implementation. Ideally we would have a list of keywords and responses when either of the keywords is triggered. We can even personalize this by asking the user for their name and then appending it to the answer every time.

The user may also ask to search for something. That is actually quite an easy thing to do. If we have detected the word that the user is longing to search for correctly, we just need to enter that into the search engine. Whatever result the page displays, we can report it back to the user. We can also use voice commands to enter the questions and give the responses. In this case, we would also need to implement some kind of NLP (Natural Language Processing). After the voice command is correctly identified, all the other processes are exactly the same.

Using the const keyword to optimize your code

Aconst keyword is used to make data or a pointer constant so that we cannot change the value or address, respectively. There is one more advantage of using the const keyword. This is particularly useful in the object-oriented paradigm.

Getting ready

For this recipe, you will need a Windows machine and an installed version of Visual Studio.

How to do it…

In this recipe, we will find out how easy it is to use the const keyword effectively:

#include <iostream>

 

class A

{

public:

 

voidCalc()const

  {

Add(a, b);

    //a = 9;       // Not Allowed

  }

A()

  {

    a = 10;

    b = 10;

 

  }

private:

 

int a, b;

void Add(int a, int b)const

  {

 

std::cout<< a + b <<std::endl;

  }

};

 

int main()

{

 

  A _a;

  _a.Calc();

 

int a;

std::cin>> a;

 

return 0;

}

How it works…

In this example, we are writing a simple application to add two numbers. The first function is a public function. This mean that it is exposed to other classes. Whenever we write public functions, we must ensure that they are not harming any private data of that class. As an example, if the public function was to return the values of the member variables or change the values, then this public function is very risky. Therefore, we must ensure that the function cannot modify any member variables by adding the const keyword at the end of the function. This ensures that the function is not allowed to change any member variables. If we try to assign a different value to the member, we will get a compiler error:

errorC3490: 'a' cannot be modified because it is being accessed through a const object.

So this makes the code more secure. However, there is another problem. This public function internally calls another private function. What if this private function modifies the values of the member variables? Again, we will be at the same risk. As a result, C++ does not allow us to call that function unless it has the same signature of const at the end of the function. This is to ensure that the function cannot change the values of the member variables.

Summary

C++ is still used as the game programming languageof choice by many as it gives game programmers control of the entire architecture, including memorypatterns and usage. For detailed game development using C++ you can refer to:

  • https://www.packtpub.com/game-development/procedural-content-generation-c-game-development
  • https://www.packtpub.com/game-development/learning-c-creating-games-ue4

Resources for Article:


Further resources on this subject:

LEAVE A REPLY

Please enter your comment!
Please enter your name here