I am just starting with coding and I am following the book "Beginning C++ Game Programming Second Edition" by John Horton, and so far everything works more or less as I expect, apparently there are some mistakes in the book, but nothing I couldn't handle so far.
Some info: The project is a c++ console app in Visual Studio 2022. Solution platform x86.
The main functions lets the game start out in a paused state called "GAME_OVER".
enum class State { PAUSED, LEVELING_UP, GAME_OVER, PLAYING };
State state = State::GAME_OVER;
How I expected things to work according to the code: You are in game over. You press "return" to enter the "LEVELING_UP" state. Then you press any NUM button from 1 to 6 to enter the "PLAYING" state, at which point the game is drawn.
Right now neither game over nor leveling up will draw anything, so the screen is black, but that's okay.
This should be the relevant code:
while (window.isOpen())
{
/*
*************
HANDLE INPUT
*************
*/
//handle events by polling
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::KeyPressed)
{
//start game while in GAME_OVER state
if (event.key.code == Keyboard::Return && state == State::GAME_OVER)
{
state = State::LEVELING_UP;
}
}
}//end event polling
//handle LEVELING_UP state
if (state == State::LEVELING_UP)
{
if (event.key.code == Keyboard::Num1)
{
state = State::PLAYING;
}
if (event.key.code == Keyboard::Num2)
{
state = State::PLAYING;
}
if (event.key.code == Keyboard::Num3)
{
state = State::PLAYING;
}
if (event.key.code == Keyboard::Num4)
{
state = State::PLAYING;
}
if (event.key.code == Keyboard::Num5)
{
state = State::PLAYING;
}
if (event.key.code == Keyboard::Num6)
{
state = State::PLAYING;
}
}
}
What actually happens: Game starts in black screen. I need to press the "return" key. So far nothing happens (as it should). Pressing Num1 through 6 now doesn't do anything, although it should return the state to "PLAYING".
The game only starts when pressing space. Why space? The word "space" is in this class exactly once, at the very beginning outside main in "namespace". Any other event is either "return" or Num 1 through 6.
Why do I need to press space after pressing enter, and why do the Num keys not work?
I tried searching my code for instances of "space" in case I had just forgotten it somewhere, but the only instances of "space" in any classes or headers are "namespace".
I tried using the Num keys with Numlock on and off, makes no difference.
I tried using all kinds of other keys after pressing "enter" the first time, the game only starts when pressing space.
At this point I am out of ideas. Maybe this is a Visual Studio quirk? Or a console game quirk? I have no idea, and googling hasn't helped me, so far.
Thank you for reading, I am hoping someone can explain to me what is happening.
As requested, the full code for the main class:
#include <SFML/Graphics.hpp>
#include "Player.h"
#include "ZombieArena.h"
#include "TextureHolder.h"
#include "Bullet.h"
using namespace sf;
int main()
{
TextureHolder holder;
//the game will always be in on of four stages
enum class State { PAUSED, LEVELING_UP, GAME_OVER, PLAYING };
State state = State::GAME_OVER;
//get screen resolution and create an SFML window
Vector2f resolution;
resolution.x = VideoMode::getDesktopMode().width;
resolution.y = VideoMode::getDesktopMode().height;
RenderWindow window(VideoMode(resolution.x, resolution.y), "Zombie Arena", Style::Fullscreen);
//create SFML window for main action
View mainView(sf::FloatRect(0, 0, resolution.x, resolution.y));
//clock for timing
Clock clock;
//how long PLAYING has been active
Time gameTimeTotal;
//where the mouse is in relation to world
Vector2f mouseWorldPosition;
//where the mouse is in relation to screen
Vector2i mouseScreenPotition;
//create player
Player player;
//boundaries of arena
IntRect arena;
//create background
VertexArray background;
Texture textureBackground = TextureHolder::GetTexture("graphics/background_sheet.png");
//prepare horde
int numZombies;
int numZombiesAlive;
Zombie* zombies = nullptr;
//100 bullets array should do
Bullet bullets[100];
int currentBullet = 0;
int bulletSpare = 24;
int bulletsInClip = 6;
int clipSize = 6;
float fireRate = 1;
//when was fire button last pressed?
Time lastPressed;
//hide mouse pointer and replace with crosshair
window.setMouseCursorVisible(true);
Sprite spriteCrosshair;
Texture textureCrosshair = TextureHolder::GetTexture("graphics/crosshair.png");
spriteCrosshair.setTexture(textureCrosshair);
spriteCrosshair.setOrigin(25, 25);
//main game loop
while (window.isOpen())
{
/*
*************
HANDLE INPUT
*************
*/
//handle events by polling
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::KeyPressed)
{
//Pause game
if (event.key.code == Keyboard::Return && state == State::PLAYING)
{
state = State::PAUSED;
}
//restart while paused
else if (event.key.code == Keyboard::Return && state == State::PAUSED)
{
state = State::PLAYING;
//reset clock so there is no frame jump
clock.restart();
}
//start game while in GAME_OVER state
else if (event.key.code == Keyboard::Return && state == State::GAME_OVER)
{
state = State::LEVELING_UP;
}
if (state == State::PLAYING)
{
//reloading
if (event.key.code == Keyboard::R)
{
if (bulletSpare >= clipSize)
{
//enough bullets
bulletsInClip = clipSize;
bulletSpare -= clipSize;
}
else if (bulletSpare > 0)
{
//not a full clip left
bulletsInClip = bulletSpare;
bulletSpare = 0;
}
else
{
//out of bullets
}
}
}
}
}//end event polling
//handle quitting
if (Keyboard::isKeyPressed(Keyboard::Escape))
{
window.close();
}
//handle wasd while playing
if (state == State::PLAYING)
{
if (Keyboard::isKeyPressed(Keyboard::W))
{
player.moveUp();
}
else
{
player.stopUp();
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
player.moveDown();
}
else
{
player.stopDown();
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
player.moveLeft();
}
else
{
player.stopLeft();
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
player.moveRight();
}
else
{
player.stopRight();
}
//fire
if (Mouse::isButtonPressed(sf::Mouse::Left))
{
if (gameTimeTotal.asMilliseconds() - lastPressed.asMilliseconds() > 1000 / fireRate && bulletsInClip > 0)
{
//pass center of player and center of crosshair to shoot function
bullets[currentBullet].shoot(player.getCenter().x, player.getCenter().y, mouseWorldPosition.x, mouseWorldPosition.y);
currentBullet++;
if (currentBullet > 99)
{
currentBullet = 0;
}
lastPressed = gameTimeTotal;
bulletsInClip--;
}
}//end fire bullet
}//end wasd handling while playing
//handle LEVELING_UP state
if (state == State::LEVELING_UP)
{
if (event.key.code == Keyboard::Num1)
{
state = State::PLAYING;
}
if (event.key.code == Keyboard::Num2)
{
state = State::PLAYING;
}if (event.key.code == Keyboard::Num3)
{
state = State::PLAYING;
}if (event.key.code == Keyboard::Num4)
{
state = State::PLAYING;
}if (event.key.code == Keyboard::Num5)
{
state = State::PLAYING;
}if (event.key.code == Keyboard::Num6)
{
state = State::PLAYING;
}
if (state == State::PLAYING)
{
//prepare the level
arena.width = 500;
arena.height = 500;
arena.left = 0;
arena.top = 0;
//pass vertex array by reference to createBackground function
int tileSize = createBackground(background, arena);
player.spawn(arena, resolution, tileSize);
//create horde
numZombies = 10;
//delete previous memory of horde
delete[] zombies;
zombies = createHorde(numZombies, arena);
numZombiesAlive = numZombies;
//frame jump
clock.restart();
}
}//end LEVELING_UP
/*
*****************
UPDATE THE FRAME
*****************
*/
if (state == State::PLAYING)
{
//update delta time
Time dt = clock.restart();
//update total game time
gameTimeTotal += dt;
//make a decimal fraction of 1 from the delta time
float dtAsSeconds = dt.asSeconds();
//where is the mouse
mouseScreenPotition = Mouse::getPosition();
//convert mouse position to coordinates of mainView
mouseWorldPosition = window.mapPixelToCoords(Mouse::getPosition(), mainView);
//set crosshair to mouse world location
spriteCrosshair.setPosition(mouseWorldPosition);
//update player
player.update(dtAsSeconds, Mouse::getPosition());
//make note of player new position
Vector2f playerPosition(player.getCenter());
//make view centre around player
mainView.setCenter(player.getCenter());
//loop through zombies and update
for (int i = 0; i < numZombies; i++)
{
if (zombies[i].isAlive())
{
zombies[i].update(dt.asSeconds(), playerPosition);
}
}
//loop through bullets and update
for (int i = 0; i < 100; i++)
{
if (bullets[i].isInFlight())
{
bullets[i].update(dtAsSeconds);
}
}
}//end updating scene
/*
***************
DRAW THE SCENE
***************
*/
if (state == State::PLAYING)
{
window.clear();
//set mainView and draw
window.setView(mainView);
//draw background
window.draw(background, &textureBackground);
//draw zombies
for (int i = 0; i < numZombies; i++)
{
window.draw(zombies[i].getSprite());
}
for (int i = 0; i < 100; i++)
{
if (bullets[i].isInFlight())
{
window.draw(bullets[i].getShape());
}
}
//draw player
window.draw(player.getSprite());
//draw crosshair
window.draw(spriteCrosshair);
}
if (state == State::LEVELING_UP)
{
}
if (state == State::PAUSED)
{
}
if (state == State::GAME_OVER)
{
}
window.display();
}//end game loop
//delete memory
delete[] zombies;
return 0;
}