I'm writing a basic snake game as console application in c++. It's based on two-dimensional array of "tile" structures. My problem is: when pressing button to change the direction the snake is going it doesn't work immidiately but waits for next "tick" instead. The function that manages game itself looks like this:
void board::play()
{
display();
while(1)
{
getInput();
delay(0.5);
getInput();
resetCursor();
tick();
display();
}
}
display() is pretty self-explanatory, displays whole board in console window.
delay() as well, it's function which gets number of seconds as a float and waits this much time before proceeding
getInput() looks like this:
void board::getInput()
{
int input;
if(kbhit())
{
input=getch();
if(input==KEY_LEFT)
turnLeft();
if(input==KEY_RIGHT)
turnRight();
if(input==KEY_UP)
turnUp();
if(input==KEY_DOWN)
turnDown();
}
}
resetCursor() sets the cursor to 0,0 coordinate so each time the board will write over itself and not one under another
And now for the game itself: class board contains amongst others field int direction. The tiles themselves contain a counter, which counts down by 1 with each move, and when reaches 0 the tile becomes empty. If the counter is equal to the lenght of a snake the tile is considered a head.
The tick()function does just that: decreases all counters by 1, remembers where head was and spawns a new head in the field next to previous one in direction specified. It looks like this:
void board::tick()
{
int posx, posy;
for(int i=0; i<boardSize; i++)
{
for(int j=0; j<boardSize; j++)
{
if(tab[i][j].getCounter()==lenght)
{
posx=i;
posy=j;
}
tab[i][j].tick();
}
}
switch(direction)
{
case UP: tab[posx-1][posy].addSnake(lenght);
break;
case DOWN: tab[posx+1][posy].addSnake(lenght);
break;
case LEFT: tab[posx][posy-1].addSnake(lenght);
break;
case RIGHT: tab[posx][posy+1].addSnake(lenght);
break;
}
}
Problem is, as stated before, that game waits one "tick" before changing direction when it should do so immediately after pressing associated button, e.g. when turning from UP to LEFT it does one more move up and only after that moves left.
It seems to me that you have an extra getInput() call. Could it be the problem?
while(1)
{
getInput(); // <--
delay(0.5);
getInput(); // <--
resetCursor();
tick();
display();
}
Solved.
Turns out there were too many things put into buffer, as the program started going crazy if you mashed controls randomly during waiting time. I solved it by replacing
if(kbhit())
{
input=getch();
//reactions
}
with
while(kbhit())
{
input=getch();
}
//reactions
It now checks every signal in the buffer and reacts only to the last one, so the delay is eliminated.
Related
I have been working on a snake game using sfml and c++ but I am having trouble getting the tail of my snake to follow the snake head which is defined as Snake[0] in my code below. I have implemented code that I think should work but doesn't which is the following
for (int i = 1; i < snakeSize; i++)
{
snakePartX[i] = snakePartX[i-1];
snakePartY[i] = snakePartY[i-1];
}
The way I understand it (and I am probably incredibly wrong and would appreciate if anyone could point out what is actually happening here) is that, that piece of code should set the value of a snake body part to the position of where the previous body part WAS located so that when the program loops they are set to follow the snake as it travels.
What actually does happen though is that when the snake eats an apple the snake will gain 1 block to its tail, but will not grow any farther.
In the classic snake game, the snake is made up of segments. Each segment contains the coordinate of the segment (among other attributes).
Specifically, the snake is a container of segments. The container is your choice, but I recommend a queue.
With the queue, a new head segment is added into the queue and a tail segment removed.
Here's a code fragment to help:
class Snake_Segment
{
public:
int column;
int row;
};
typedef std::deque<Snake_Segment> Segment_Container;
int main(void)
{
Segment_Container snake_body;
Snake_Segment head;
head.row(25);
head.column(30);
snake_body.push_back(head);
Snake_Segment tail = head;
++tail.column;
snake_body.push_back(tail);
return EXIT_SUCCESS;
}
Why do index zero here? Doesn't it get done as part of the for loop too?
Snake[0].setPosition(snakePartX[0], snakePartY[0]);
for(int i = 0; i < snakeSize; i++)
{
Snake[i].setPosition(snakePartX[i], snakePartY[i]);
}
I'm concerned about this section :
snakeSize += 1;
}
Apple.setPosition(applePos.x, applePos.y);
window.draw(Apple);
for(int i = 0; i < snakeSize; i++)
{
window.draw(Snake[i]);
}
Make sure you don't exceed 20 as your parts are a simple array and you will run into problems once you get 20 parts. More importantly, you never set the value for Snake[i] for the new snakeSize. So when you get an apple, you'll use uninitialized memory to draw one snake part.
Another potential problem :
//Clock to set snake speed
ElapsedTime = clock.getElapsedTime();
if(ElapsedTime.asSeconds() >= snakeSpeed)
{
for(int i = 1; i < snakeSize; i++)
{
snakePartX[i] = snakePartX[i - 1];
snakePartY[i] = snakePartY[i - 1];
}
You allow keyboard input to move the snake head along, but you only move the body when the timer expires. So you head can slide way away from the body and the body doesn't move unless the timer goes off.
I printed the field of snake game as a Field[x][y]. So by below code the body of snake following the head. Also, optionMoveNumber==0 in other function checked if Field[x][y]==food or not which means did the snake eat the food of not.
int m=1;
void Snake::Movement(int x, int y){
movey[0]=firstTaily; //first place
movex[0]=firstTailx; //first place
movex[m]=x;
movey[m]=y;
for(int n=m; n>=m-ntail; n--){
Field[movey[n]][movex[n]]=Field[movey[n-1]][movex[n-1]];
}
if(optionMoveNumber==0){
Field[movey[m-1]][movex[m-1]]=tail;
}
else{
Field[movey[m-ntail-1]][movex[m-ntail-1]]=space;
}
m++;
}
just for fun and possibly for a project for my computer science class, I was making a text based "Galaga" game. Your character is ">" and the enemies are "<" and the lasers are "~" or "-", point is, its pretty simple. 600-700 lines of code. Problem is, you have to enter something for the code to loop. I used system("stty raw") so that I don't have to press enter, but the game stops when you stop holding or pressing buttons. I want to make it a real continuous game that is just made out of text. Any way to accomplish this while also being able to control the movement of your character properly using WASD movements? I am also using Xcode on a macbook if that helps. And this is part of the main function just for some context:
int main()
{
cout<<" ██████╗ █████╗ ██╗ █████╗ ██████╗ █████╗ "<<endl;
cout<<"██╔════╝ ██╔══██╗██║ ██╔══██╗██╔════╝ ██╔══██╗"<<endl;
cout<<"██║ ███╗███████║██║ ███████║██║ ███╗███████║"<<endl;
cout<<"██║ ██║██╔══██║██║ ██╔══██║██║ ██║██╔══██║"<<endl;
cout<<"╚██████╔╝██║ ██║███████╗██║ ██║╚██████╔╝██║ ██║"<<endl;
cout<<" ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ "<<endl;
cout<<endl<<endl;
cout<<"Welcome to text-galaga!"<<endl;
cout<<"_________________________"<<endl;
cout<<"Use W to move forward,"<<endl;
cout<<"A to move left, S to move back,"<<endl;
cout<<"D to move right, and J to shoot"<<endl;
cout<<"_________________________"<<endl;
cout<<"Good luck!"<<endl<<endl;
cout<< "Enter any key to begin..."<<endl;
cin>>x;
// Set terminal to raw mode
system("stty raw");
char input ='c';
int height=10,wide=5;
int turns=0;
int num_destroyed=0;
// ACTUAL MAIN GAME OPERATIONS START HERE...
while((input!='Q'&& input!='q') && (num_destroyed<5) && dead==false)
{
// Wait for single character
input = getchar();
int i=0;
int j=0;
get_inputs(i, j, bad);
render(i, j, height, wide, input, destroyed, dead, bad, turns, shot,
constant_height, constant_wide, shot_distance, next, num_destroyed);
draw(height, wide, j, i, destroyed, dead, bad, shot_distance, shot,
constant_height, constant_wide);
if ((num_destroyed<5) && dead==false)
cout<<endl<<endl;
++turns;
}
// Reset terminal to normal "cooked" mode
system("stty cooked");
// Game End Information
if (num_destroyed==5)
{
cout<<"Congradulations! You win!"<<endl<<endl;
}
if (dead==true)
{
cout<<"You died. Game Over."<<endl<<endl;
}
cout<<"Frames used: "<<turns<<endl;
return 0;
// AND END HERE...
}
Anyways, I know that I should probably put "while (true)" and take out the "getchar()" once I have the rest figured out, but that alone doesn't seem to make it into a sufficient game like a text-galaga. Anyways, sorry about the long post. Thanks guys.
I have a pointer that is set to 0, then later on in the same function, inside some loops/conditions I try to re assign it.. (please read the comments)
for(Entity* tile : _originalFloorTiles)
{
for(Turns turn : pointsUpLeftDownRight)
{
if(tile->GetCollisionRect().ContainsPoint(turn.first.x, turn.first.y)){
turn.second = tile; //everything looks fine here, turn.second is still null and tile is a valid pointer
assert(turn.second); //turn.second is definitely assigned the value of tile here.
}
HAPI->DebugText("check pointsUpLeftDownRight now");//!Here's where it gets weird,
// If i hover over turn and inspect it in visual studio now, turn.second is still completely valid
// (with the assigned value of tile).
// But hovering over pointsUpLeftDownRight shows its contents for each turn..
// and inside there the current turn is a NULL pointer for turn.second!
}
}
So one moment i have assignd my pointer no problem, and the next moment the pointer doesn't seem to have changed at all.
To clarify, Turns is a lazy typedef for std::pair<Vect, Entity*> , apologies if that makes my code harder to read, it's some quickly thrown together enemy ai. I'll post the complete function below.
I'm really stumped here and not sure if i'm being an idiot or something weird is going on, would really appreciate anyone taking the time to look.
//looks for turns that the ghost can take.
void IceGhostNPC::RespondToTimePassed()
{
//Entity* t = _originalFloorTiles[0];
//test if enough time has passed since ghost last decided to look for turns
if(_lastTimeTurned < timeGetTime() - _timeBeforeSearchingForTurns)
{
//store points surrounding ghost in a way that we can associate them with a valid floor tile to move onto
std::vector<Turns> pointsUpLeftDownRight;
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos(), GetCenterYPos() - floorTileHeight), 0)); //point above
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos() - floorTileWidth, GetCenterYPos()), 0)); //point left
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos(), GetCenterYPos() + floorTileHeight), 0)); //point down
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos() + floorTileWidth, GetCenterYPos()), 0)); //point right
//look through original floor tiles,
for(Entity* tile : _originalFloorTiles)
{
//see if its possible to take a turn
for(Turns turn : pointsUpLeftDownRight)
{
if(tile->GetCollisionRect().ContainsPoint(turn.first.x, turn.first.y)){
turn.second = tile;
assert(turn.second);
}
HAPI->DebugText("check pointsUpLeftDownRight now");
}
}
//Now to make the behaviour more interesting we have the ghost randomly take one of the turns,
// ( we use associated tile to check the turn is possible, and we can also change that tile to an icy patch )
bool turnTaken = false;
do{
int turnChoice = rand() % 4;
if(pointsUpLeftDownRight[turnChoice].second == 0)
continue; //go back to top of loop if that turn had a null tile
else
{
switch(turnChoice){
case 0: //turn upwards
_moveable->SetYDirection(Controller::UP);
_moveable->SetXDirection(Controller::NONE);
break;
case 1: //turn leftwards
_moveable->SetYDirection(Controller::NONE);
_moveable->SetXDirection(Controller::LEFT);
break;
case 2: //turn downwards
_moveable->SetYDirection(Controller::DOWN);
_moveable->SetXDirection(Controller::NONE);
break;
case 3: //turn right
_moveable->SetYDirection(Controller::NONE);
_moveable->SetXDirection(Controller::RIGHT);
break;
}
turnTaken = true;
_lastTimeTurned = timeGetTime();
//ice tile up baby
}
}while(turnTaken = false);
}
FinishResponding(timeGetTime());
}
Check this line:
for(Turns turn : pointsUpLeftDownRight)
You are iterating over copies of the elements in pointsUpLeftDownRight. Whatever value you assign to that copy will be lost when the copy is destroyed (at the end of the for body). Your assignment changes a temporary.
Try with this instead:
for(Turns& turn : pointsUpLeftDownRight)
Say we have (pseudo) code like this:
GetInput()
{
//insert keyboard stuff here
}
Update()
{
//insert rendering stuff here
}
void game::loop()
{
game.Update();
player.GetInput();
}
How will I go about waiting for the player to give input before updating whats on screen?
Why not switch the order (GetInput first), then block in GetInput and don't return until the user has entered something acceptable?
Are you sure you really want to wait? Don't mean to be intrusive, but usually in games it's best to continue drawing stuff on the screen while waiting for any kind of input. Because players don't want to see the very same picture all the time.
To make the game turn-based, I recommend an array of objects with methods that represent a player's abilities in the game. You can have a game loop, a nested round loop, a nested turn loop, and a boolean that exits the round and turn loops when a player wins.
With your current pseudocode, you could use a switch and integer in the Update method to equate these nested loops.
Example Tic Tac Toe AI:
#include "Shared.h"
#include "Board.h"
#ifndef AI_H_
#define AI_H_
class AI{
public:
AI();
enum difficulty{Easy, Medium, Hard};
void setAlgorithm(difficulty h);
void applyAlgorithm(Board* b, int columns, int rows);
private:
Board::spot type;
difficulty harder;
void easy(Board* b, int columns, int rows);
void medium(Board* b, int columns, int rows);
void hard(Board* b, int columns, int rows);
};
#endif /*AI_H_*/
SDL_WaitEvent() is ridiculously more efficient than SDL_PollEvent(), if you don't want to hog the CPU.
while(!quit){
if(SDL_WaitEvent(&event)){
if(event.type == SDL_QUIT){
quit = true;
}else if(event.type == SDL_MOUSEBUTTONDOWN){
if(!menu.isClicked()){
if(menu.handleEvent(&event)){
}
}
}
}
}
I have the following function that is used as the glutKeyboardFunc function parameter:
void handleKeypress(unsigned char key, //The key that was pressed
int x, int y) { //The current mouse coordinates
switch (key) {
case 27: //Escape key
exit(0); //Exit the program
}
if (key == 'a')
{
moveCircleLeft(0);
}
if (key == 'w')
{
moveCircleUp(0);
}
if (key == 's')
{
moveCircleDown(0);
}
if (key == 'd')
{
moveCircleRight(0);
}
}
Now I will show you moveCircleLeft as an example of how the moveCircle functions are written:
void moveCircleLeft(int x)
{
characterX = characterX - 0.1;
glutPostRedisplay();
x++;
if (x < 10)
{
glutTimerFunc(10, moveCircleLeft, x);
}
}
The other moveCircle functions work similar and the only differences between the four functions is whether its plus of minus 0.1 or if it is characterY as opposed to characterX.
Now, here is the problem:
All the directions are moving in the right direction but, there is a delay/pause in movement of the character when I try to press keys. So, if I press just the d key (moveCircleRight) it moves right a little, stops a for a small amount of time, then moves across the screen in that direction at a constant speed with no pauses. Then if I change to a different key it pause for a little bit before changing directions then moves at a constant speed in that direction.
Any suggestion around this would be appreciated!
Create a boolean variable for each key (preferably an array). Then use KeyDown/KeyUp instead of KeyPress (i believe in GLUT its something like KeyboardUpFunc and KeyboardFunc, but cant remember now). On KeyDown, set the appropriate variable to true, on KeyUp, set it to false. Now you probably have an Idle function or something like that. Call there your "move" functions based on the boolean variables. There should be no pauses now. Beware though that you might need to keep track of the time elapsed between frames and scale the distance moved appropriately.