C Space Invaders enemy movement - c

I have to write a clone of space invaders game for university in C language using SDL library for graphics.I am very new to C and programming in general so i struggle with it a lot. For now I have one row of enemies and I'm trying to make it move correctly. This are functions for movement of aliens, which check for the collision with right/left wall(which are SDL_Rects near window edges) and if it happens, enemies move one line lower in the opposite direction. The problem is it works okay for all ten enemies except the first one. Each time when collision with left wall occurs the first alien sort of moves away a bit from others instead of moving in one block as I want it to. I noticed that if I change the first for loop in move_aliens function to start from i=11 and i--, the same thing will happen to the enemy in last column. But I still dont know how to fix it.
I would appreciate if someone could tell me what I'm doing wrong, give me an idea or sollution :).
I uploaded a video of whats happening http://sendvid.com/dt1reizc
void move_down (GameState *game)
{
int i=0;
for(; i < HMALIENS; i++)
game->alien1[i].y += 25;
}
int collided(int a1, int b1, int a2, int b2, int width1,
int height1, int width2, int height2)
{
return (!((a1 > (a2+width2)) || (a2 > (a1+width1)) ||
(b1 > (b2+height2)) || (b2 > (b1+height1))));
}
void move_aliens(GameState *game)
{
int i=0;
for(; i < HMALIENS; i++)
{
if (game->alien1[i].dir==LEFT)
game->alien1[i].x -= 10;
if (game->alien1[i].dir==RIGHT)
game->alien1[i].x += 10;
if (collided(game->alien1[i].x, game->alien1[i].y,
game->leftwall.x, game->leftwall.y, 50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=RIGHT;
move_down(game);
}
}
if(collided(game->alien1[i].x, game->alien1[i].y, game->rightwall.x,
game->rightwall.y, 50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=LEFT;
move_down(game);
}
}
}
//edit
HMALIENS is just a constant (11), the number of living enemies at the start
GameState is a structure.
LEFT/RIGHT stand for direction of movement (quite obvious) [enum Direction {LEFT, RIGHT};]. I have in my alien1 structure enum Direction dir and in the function which load_game function i set it to RIGHT.
typedef struct
{
Player player;
Rightwall rightwall;
Leftwall leftwall;
Alien1 alien1[HMALIENS];
SDL_Texture *bulet;
SDL_Texture *ship;
SDL_Texture *Alien1;
SDL_Renderer *renderer;
} GameState;

Without rewriting your code, the problem is that you have already moved the first alien ([0]) left,
if (game->alien1[i].dir==LEFT)
game->alien1[i].x -= 10;
and then you are doing the collision test and flagging all of them to move right, but the loop continues with [1..10], while [0] has already moved.

typedef enum{
LEFT,
RIGHT
}GAME_DIRECTION;
int dir=LEFT;
#define SPRITE_WIDTH 50
#define SPRITE_HEIGHT 50
#define BOTTOM_LINE 600
void move_down (GameState *game)
{
int i;
for(i=0; i < HMALIENS; i++)
game->alien1[i].y += 25;
}
void do_slide(GameState *game)
{
int i;
for(i=0; i < HMALIENS; i++)
game->alien1[i].x += dir?10:-10;
}
int collided(GameState *game)
{
int i;
for(i=0; i < HMALIENS; i++){
if(
game->alien1[i].x <= game->leftwall.x ||
game->alien1[i].x >= game->rightwall.x +SPRITE_WIDTH ||
game->alien1[i].y >= BOTTOM_LINE - SPRITE_HEIGHT
)
return true;
}
return false;
}
void move_aliens(GameState *game)
{
if(collided(game)){
move_down (game);
dir=!dir; // update direction;
}else{
do_slide (game);
}
}

Maybe something like:
void move_aliens(GameState *game)
{
int i=0;
for(; i < HMALIENS; i++)
{
if (game->alien1[i].dir==LEFT)
game->alien1[i].x -= 10;
if (game->alien1[i].dir==RIGHT)
game->alien1[i].x += 10;
}
if (collided(
game->alien1[0].x, game->alien1[0].y,
game->leftwall.x, game->leftwall.y,
50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=RIGHT;
move_down(game);
}
if(collided(
game->alien1[HMALIENS-1].x, game->alien1[HMALIENS-1].y,
game->rightwall.x, game->rightwall.y,
50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=LEFT;
move_down(game);
}
}
I have tried to make the minimum changes. But #milevyo's more extensive rewrite looks good as well.
The problem, I think (just from looking), is you has the left collision test inside the loop so when that hits, the movement gets out of sync.
Also it was pretty subtle that the right collision test used i from the previous loop which happens to index the last element (the rightmost alien). I changed that to explicitly use HMALIENS - 1. When you start destroying aliens,
you'll have to track the first (leftmost) and last (rightmost) living aliens and use them for you collision tests.
Your indentation and formatting were a little off which makes the code much harder to read. Formatting is important, and will be even more so when your code gets more complicated.

Related

How to avoid object overlapping and overtaking?

I am trying to draw circles from same y coordinate.
and creating arrays for xPos. I put the speed and xPos random, how to make sure they are not overlapping and the one behind it match the speed to the front one so it wouldn't overtake?
I have retried the code, but it still overlapping for some reason that I couldn't find out?
OK now I initialise the k with i+1, so whichever behind it.
and I ran flow chart as well, the logic looks alright, still not doing what it should being doing.
int Num=10;
float dia=50;
float[] xPos= new float[Num];
float[] xSpeed=new float[Num];
void setup() {
size(300, 300);
for (int i= 0; i<xPos.length; i++) {
xPos[i]=random(-dia*Num);
xSpeed[i]=3;
boolean overlapping=false;
for(int k=;k<xPos.length;k++){
float newPos=xPos[k];
float dist=(newPos-xPos[i]);
if(dist<dia+50){
overlapping=true;
break;
}
}
if(!overlapping){
draw();
}
}
}
void draw() {
background(255);
drawBall();
moveBall();
reset();
}
void drawBall() {
for (int i= 0; i<xPos.length; i++) {
circle(xPos[i], 50, 50);
}
}
void moveBall() {
for (int i= 0; i<xPos.length; i++) {
xPos[i]+=xSpeed[i];
}
}
void reset() {
for (int i= 0; i<xPos.length; i++) {
if (xPos[i]>width) {
xPos[i]=0;
}
}
}
how to make sure they are not overlapping
For this, you could naively check if the new position has already been taken by another such as follows (I haven't run the code, so i probably has bugs, think of it more as a pseudocode):
boolean isItTaken(float[] xPos, float newPos) {
for (int i= 0; i<xPos.length; i++) {
if (abs(newPos - xPos[i]) < circleSize) return true;
}
return false;
}
for (int i= 0; i<xPos.length; i++) {
float newPos = random(-50);
while (isItTaken(xPos, newPos)) {
newPos = random(-50);
}
}
I'm sure there are better methods though. Also I think using ArrayList is better than using a simple array.
and the one behind it match the speed to the front one so it wouldn't overtake?
You could set it as a constant number? If you want it to be random, but slower than the previous one, you could set the upper limit of the random function to be the speed of the previous one such as(again, probably buggy):
ArrayList<Float> xSpeed = new ArrayList<Float>;
for (int i= 0; i < Num; i++) {
xSpeed.push(random(2, xSpeed.get(xSpeed.length - 1)));
}

Function is being called four times per loop when there are only two calls to it

I'm very new to programming in C, and have pretty rusty programming skills overall. In order to learn C and re-orient myself with programming in general, I'm challenging myself to try and make a simple rougelike using ncurses.
I've set up a "log", which I should be able to push messages to - the most recent 10 message should be displayed. In order to test this, I've made it so each time either the player or the very simple randomly-moving mob takes a step, a log message is pushed saying "step [direction]". However, even though they each only take one step, for some reason, four messages are pushed to the log. The second-to-most-recent one is always the actual direction the character moved, and I presume one of the other two is the mob moving, but I don't know the origin of the other two. Does anyone spot anything glaring in my code that might be causing this issue? All help is appreciated, thanks!
(I believe the only major relevant sections to look at should be the main() function, pushToLog(), printLog(), and moveCreature(). That said, there is a chance the problem might be somewhere else. I'm not sure.)
#include <stdlib.h>
#include <stdio.h>
#include <ncurses.h>
#include <unistd.h>
#include <string.h>
#define up 65
#define down 66
#define right 67
#define left 68
#define quit 113
struct creature {
int x;
int y;
int hp;
int maxhp;
};
void setupMap();
struct creature setupCreature();
void moveCreature();
void pushToLog();
void printLog();
int breakFlag = FALSE;
char mapShape[15][15];
char mapFeatures[15][15];
char outputLog[10][60];
int main(int argc, char *argv[]){
struct creature player = setupCreature(4, 4, 100, 100);
struct creature mob = setupCreature(5, 7, 100, 100);
setupMap();
initscr();
noecho();
curs_set(FALSE);
while(1){
for (int i = 0; i < 15; i++){
for (int c = 0; c < 15; c++){
mvprintw(c, i, "%c", mapShape[i][c]);
}
}
mvprintw(player.y, player.x, "%c", '#');
mvprintw(mob.y, mob.x, "%c", 'd');
printLog();
int input = getch();
moveCreature(input, &player);
int mobDir = rand() % (68 + 1 - 65) + 65;
moveCreature(mobDir, &mob);
refresh();
usleep(300);
if (breakFlag == TRUE){
break;
}
}
endwin();
return 0;
}
void moveCreature(int dir, struct creature *subject){
int next;
if (dir == up){
next = (subject->y - 1);
if (mapShape[subject->x][next] != '#'){
subject->y = next;
pushToLog("step up ");
}
}
else if (dir == down){
next = (subject->y + 1);
if (mapShape[subject->x][next] != '#'){
subject->y = next;
pushToLog("step down ");
}
}
else if (dir == right){
next = (subject->x + 1);
if (mapShape[next][subject->y] != '#'){
subject->x = next;
pushToLog("step right ");
}
}
else if (dir == left){
next = (subject->x - 1);
if (mapShape[next][subject->y] != '#'){
subject->x = next;
pushToLog("step left ");
}
}
else if (dir == quit){
breakFlag = TRUE;
}
}
void pushToLog(char string[]){
for (int i = 10; i > 0; i--){
strcpy(outputLog[i], outputLog[i-1]);
}
strcpy(outputLog[0], string);
}
void printLog(){
for (int i = 0; i < 10; i++){
mvprintw(28-i, 0, outputLog[i]);
}
}
struct creature setupCreature(int x,int y,int hp,int maxhp){
struct creature frankenstien;
frankenstien.x = x;
frankenstien.y = y;
frankenstien.hp = hp;
frankenstien.maxhp = maxhp;
return frankenstien;
}
void setupMap(){
for (int i = 0; i < 15; i++){
for (int c = 0; c < 15; c++){
mapShape[i][c] = '.';
}
}
for (int i = 0; i < 15; i++){
mapShape[0][i] = '#';
mapShape[14][i] = '#';
mapShape[i][0] = '#';
mapShape[i][14] = '#';
}
}
Your problem is at the input stage. You expect directional commands via the arrow keys, but those generate multiple bytes per keypress. All but one are invalid as commands.
As a secondary problem, you do not reject invalid commands. You go ahead and move the mob after each command character read, whether that command was valid or not.
The overall upshot is that when you press an arrow key, the program zips through three iterations of the main loop, one right after the other, producing log messages for one valid player move, no log messages for two invalid player moves, and log messages for each of three mob moves.
You could have detected this by logging invalid commands, or by running your program in a debugger.

Need some suggestions on how to print a histogram more neatly

I'm writing a program that will read input and then give back a histogram of the character count from K & R - Ex. 1.13
Any suggestions on how I can improve my code? Does it matter whether or not if I test for status in condition or out first? I have noticed in my examples people test to see if c is a blank or tab first.
I think I need to revisit my histogram. It doesn't really scale the results. It just draws a hyphen based on the length.
Revised to make a little bit more readable I think.
// Print a histogram of the length of words in it's input.
#include <stdio.h>
#define IN 1
#define OUT 2
#define MAX 99
int main(){
int c; // the character
int countOfLetters = 0;
int insideWord = OUT;
int frequencyOfLengths[MAX];
int longestWordCount = 0;
int i, j; // Counters
for (i = 0; i < MAX; i++){
frequencyOfLengths[i] = 0;
}
while ((c = getchar()) != EOF){
if (c == ' ' || c == '\n' || c == '\t'){
if (insideWord == IN){
if (countOfLetters > MAX){
return 1;
}
++frequencyOfLengths[countOfLetters];
if (countOfLetters >= longestWordCount) longestWordCount = countOfLetters;
}
countOfLetters = 0;
}
else {
countOfLetters++;
insideWord = IN;
}
}
for (i = 1; i <= longestWordCount; i++){
printf("%3i : %3i ", i, frequencyOfLengths[i]);
for (j = 0; j < frequencyOfLengths[i]; j++){
printf("*");
}
printf("\n");
}
return 0;
}
Definitely scale results, check out my Character Histogram that does a horizontal scaling histogram.
Also, you could benefit a y-axis label. It's hard to tell which bar is for which kind of word length. I have no idea which bar is for what word length.
I added this code right before you display the histogram, it basically halves every value, which does throw off your bar number labels. You can figure it out!
// Iterates and tells us the most frequent word length
int mostFrequent = 0;
for (i = 1; i < MAXWORD; i++)
if (charCount[i] > mostFrequent)
mostFrequent = charCount[i];
// If the bar will be too big, cut every value in half
while (mostFrequent > 60) {
for (i = 1; i < MAXWORD; i++)
if (charCount[i] > 0) {
charCount[i] /= 2;
charCount[i] |= 1;
}
// Check again to find the most frequent word length category
mostFrequent = 0;
for (i = 1; i < MAXWORD; i++)
if (charCount[i] > mostFrequent)
mostFrequent = charCount[i];
}
Honestly the bars are hard to read, maybe just use a single row of characters such as █ !
Great book so far, we're practically reading it together and are on the same page!
Cheers

Simple problem moving enemy in game (C / SDL)

I'm hacking away at a simple game to teach myself C and I've come up against an infuriatingly simple problem that I haven't been able to Google an answer to.
Code follows, apologies for its noobie terribleness (criticisms appreciated!):
#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#define AMOUNT_OF_ENEMIES 10
#define AMOUNT_OF_PIXELS_TO_MOVE 50.0
struct enemy
{
int alive;
SDL_Rect rect;
};
void create_enemy(struct enemy *position)
{
// Take a pointer to an array. Iterate through array looking for any 'dead' instances.
// (Re)initialise when found, ignore entirely if array is full of alive instances.
int j = 0;
while(position[j].alive == 1 && j < AMOUNT_OF_ENEMIES)
{
++j;
}
if(position[j].alive == 0)
{
position[j].alive = 1;
position[j].rect.y = 0;
}
}
void update_enemies(struct enemy *position)
{
// Iterate through a passed array looking for alive instances. If found increment vertical position,
// unless instance is at bottom of screen in which case it's marked as dead.
int j = 0;
while(j < AMOUNT_OF_ENEMIES)
{
if(position[j].alive == 1)
{
position[j].rect.y += 1;
if(position[j].rect.y > 570)
{
position[j].alive = 0;
}
}
++j;
}
}
int main(void)
{
// INITS *********************************************************************
int k;
int current_time = 0;
int previous_time = 0;
float difference_in_time = 0.0;
// Load SDL library
if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
printf("Problem, yo\n");
return 1;
}
// Setup event queue
SDL_Event event;
// Create array to store enemys, initialise it
struct enemy *enemy_array = malloc(sizeof(struct enemy) * AMOUNT_OF_ENEMIES);
int j;
for(j = 0; j < AMOUNT_OF_ENEMIES; ++j)
{
enemy_array[j].alive = 0;
enemy_array[j].rect.x = 150;
enemy_array[j].rect.y = 0;
}
// Create an array to flag keypresses, initialise it
int pressed_keys[323];
int l;
for(l = 0; l < 323; ++l)
{
pressed_keys[l] = 0;
}
// Create surfaces
SDL_Surface *screen = SDL_SetVideoMode(300, 600, 0, SDL_HWSURFACE);
int black = SDL_MapRGB(screen->format, 0, 0, 0);
SDL_Surface *tower = SDL_LoadBMP("tower.bmp");
SDL_Rect tower_rect;
tower_rect.x = 50;
tower_rect.y = 0;
tower_rect.w = 200;
tower_rect.h = 600;
SDL_Surface *dude = SDL_LoadBMP("dude.bmp");
float dude_x = 0.0;
SDL_Rect dude_rect;
dude_rect.x = 120;
dude_rect.y = 500;
dude_rect.w = 60;
dude_rect.h = 100;
SDL_Surface *enemy = SDL_LoadBMP("enemy.bmp");
// GAME LOOP *****************************************************************
while(1)
{
current_time = SDL_GetTicks();
difference_in_time = (float)(current_time - previous_time) / 1000;
previous_time = current_time;
if(SDL_PollEvent(&event))
{
if(event.key.keysym.sym == SDLK_DOWN)
{
create_enemy(enemy_array);
}
else
{
switch(event.type)
{
case SDL_QUIT:
printf("NOOOOOO\n");
SDL_FreeSurface(screen);
SDL_FreeSurface(tower);
SDL_FreeSurface(enemy);
free(enemy_array);
SDL_Quit();
return 0;
case SDL_KEYDOWN:
pressed_keys[event.key.keysym.sym] = 1;
break;
case SDL_KEYUP:
pressed_keys[event.key.keysym.sym] = 0;
break;
}
}
}
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
update_enemies(enemy_array);
SDL_FillRect(screen, NULL, black);
SDL_BlitSurface(tower, NULL, screen, &tower_rect);
for(k = 0; k < AMOUNT_OF_ENEMIES; ++k)
{
if(enemy_array[k].alive == 1)
{
SDL_BlitSurface(enemy, NULL, screen, &enemy_array[k].rect);
}
}
SDL_BlitSurface(dude, NULL, screen, &dude_rect);
SDL_Flip(screen);
}
return 0;
}
The issue arises at this part:
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
The 'dude' object moves to the left correctly, but nothing happens when the right arrow key is pressed.
Adding a printf tells me the if statement is being executed correctly. Removing difference_in_time makes it work, so it's either something to do with that variable or the operation of it and AMOUNT_OF_PIXELS_TO_MOVE.
I just can't for the life of me figure out why the former block executes correctly and the latter (which is essentially the same thing) doesn't. I'm sure it's something simple I've overlooked but I'm going insane trying to find it.
Your problem is due to rounding.
For your "dude" you are using a SDL_Rect, that uses integer coordinates (short int if I remember correct).
You configured your dude speed to 50 and if your game is running at 60fps (probably due to its simplicity and it may be much more if vsync is off) you will get each frame a movement value of 0.83333.
This value will be truncated to a int and the result will be zero, for example, if dude.x is 10 and you press right, the calculated value will be 10.83 and when truncated this will result in 10.
For left, it works because the value is rounded down, assuming again dude.x is 10, when left is pressed, on the first iteration the calculated value would be 9.17, truncating this will give you 9.
Simple, bad and Hack Solution
Increase AMOUNT_OF_PIXELS_TO_MOVE to a higher value that forces the int to increase, this will fix the problem.
Good Solution
Does not use SDL_Rect for storing your characters position, create a "MyRect" and use float values in it and only does rounding when drawing the character. Actually you only need to store the character position, so I would create a Point2D struct with only x and y and use this to keep track of characters position.

Selection of primitives when clicked by mouse not working

I'm developing a solitary board game, which has a piece per square, and each piece can be of two colors. If I click a piece, the four adjacent ones (top, bottom, left and right) all change to the next color.
I'm having problems with detecting in which piece the mouse was clicked on.
I have the following code for the mouse callback:
GLuint selectBuf[BUFSIZE]; // BUFSIZE is defined to be 512
GLint hits;
GLint viewport[4];
if( ( state != GLUT_DOWN ) && ( button != GLUT_LEFT_BUTTON ) )
return;
glGetIntegerv (GL_VIEWPORT, viewport);
glSelectBuffer (BUFSIZE, selectBuf);
(void) glRenderMode (GL_SELECT);
glInitNames();
glPushName(0);
gluPickMatrix ((GLdouble) x, (GLdouble) y, 20.0,20.0, viewport);
draw(GL_SELECT); // the function that does the rendering of the pieces
hits = glRenderMode(GL_RENDER);
processHits (hits, selectBuf); // a function that displays the hits obtained
Now, the problem I have is that I don't quite know how to process the hits occurred which are on selectBuf. I have the following code for processHits:
void processHits (GLint hits, GLuint buffer[])
{
unsigned int i, j;
GLuint ii, jj, names, *ptr;
printf ("hits = %d\n", hits);
ptr = (GLuint *) buffer;
for(i = 0; i < hits; i++) {
printf("hit n. %d ---> %d",i, *(buffer+i));
}
}
Finally, in the draw function I have:
void draw(GLenum mode) {
glClear (GL_COLOR_BUFFER_BIT);
GLuint x,y;
int corPeca; //colourpiece in english
int corCasa; //colourHouse (each square has a diferent color, like checkers)
for (x =0; x < colunas; x++) { //columns
for(y=0; y < colunas; y++) {
if ( (tabuleiro[y*colunas+x].peca) == 1) //board
corPeca = 1;
else
corPeca = 2;
if((tabuleiro[y*colunas+x].quadrado)==1) //square
corCasa = 1;
else
corCasa = 2;
if (mode == GL_SELECT){
GLuint name = 4;
glLoadName(name);
}
desenhaCasa(x,y,corCasa); //draws square
desenhaPeca(x,y,corPeca, mode); //draws piece
}
}
}
Now, has you can see, I've just put 4 into the buffer with glLoadName. However when I pull the number out in processHits I always get 1. I know that's because of the structure of the buffer that gets the hits, but what is that structure and how can I access the number 4?
Thank you very much for helping me.
The structure of the selection buffer is a bit more complex than that. For each hit, a "hit record" consisting of several values is appended to the selection buffer. You can look at Question 20.020 in the OpenGL FAQ for details. In your case, where there is only one name on the stack at a time, the hit record will consist 4 values, with the name being the fourth one. So, in your processHits function, you should write
for(i = 0; i < hits; i++) {
printf("hit n. %d ---> %d",i, *(buffer+4*i+3));
}
Also, the size of your name buffer should probably be 4 times longer as well.

Resources