// This is the main SDL include file #include #include #include #include #include /* data structures */ typedef struct paddle { double x; double y; double width; double height; char side; /* left: 1; right -1 */ double speed; } paddle; typedef struct ball { double x; double y; double radius; double speedX; double speedY; } ball; void initializePaddle(paddle *p,double x,double y,double w,double h,char sd,double sp) { p->x=x; p->y=y; p->width=w; p->height=h; p->side=sd; p->speed=sp; } void initializeBall(ball *b,double x,double y, double r,double sx,double sy) { b->x=x; b->y=y; b->radius=r; b->speedX=sx; b->speedY=sy; } void updatePaddle(paddle *p,double f,int d,int h) { p->y+=p->speed*f*(double)d; /* calculate next position */ /* ensure the paddle does not go beyond the boundaries */ if(p->y+(p->height/2.0)>=(double)(h/2)) p->y=(double)(h/2)-(p->height/2.0); if(p->y-(p->height/2.0)<=(double)(h/-2)) p->y=(double)(h/-2)+(p->height/2.0); } void printscore(int *p1s, int *p2s) { printf("P1: %d, P2: %d\r",*p1s,*p2s); fflush(stdout); } char ballXpaddle(ball *b,paddle *p) /* collision detection */ { /* return if the ball has collided with the side of the paddle that faces the centre of the screen */ if((b->y <= p->y+(p->height/2.0))&&(b->y >= p->y-(p->height/2.0))) /* ball y matches the paddle */ { /* check what the x coordinate of the correct side of the paddle is */ double colX=p->x+(p->width/2.0*(double)p->side); /* now check if the ball has collided with this side */ if(p->side==1) /* left - so <= */ { if((b->x-b->radius<=colX)&&(b->x-b->radius>=p->x)) return 1; /* return 1 if colliding */ } else /* right - so >= */ { if((b->x+b->radius>=colX)&&(b->x+b->radius<=p->x)) return 1; /* return 1 if colliding */ } } return 0; /* clearly no collision */ } void updateBall(ball *b,double f,paddle *p1,paddle *p2,int w,int h, int *p1s, int *p2s) { /* collision detection & resolution with scene boundaries */ if((b->x-b->radius)<=-1.0*(double)(w/2)) { //b->x=-1.0*(double)(w/2)+b->radius; /* ensure the ball does not go beyond the boundaries */ //b->speedX*=-1.0; *p1s-=1; printscore(p1s,p2s); initializeBall(b,0.0,0.0,8.0,70.0,30.0); // b->x=0.0; // b->y=0.0; // b->speedX=70.0; // b->speedY=30.0; } if((b->x+b->radius)>=(double)(w/2)) { //b->x=(double)(w/2)-b->radius; /* ensure the ball does not go beyond the boundaries */ //b->speedX*=-1.0; *p2s-=1; printscore(p1s,p2s); b->x=0.0; b->y=0.0; b->speedX=-70.0; b->speedY=30.0; } if((b->y-b->radius)<=-1.0*(double)(h/2)) { b->y=-1.0*(double)(h/2)+b->radius; /* ensure the ball does not go beyond the boundaries */ b->speedY*=-1.0; } if((b->y+b->radius)>=(double)(h/2)) { b->y=(double)(h/2)-b->radius; /* ensure the ball does not go beyond the boundaries */ b->speedY*=-1.0; } /* collision detection with paddles - incomplete */ /* paddle 1 - not quite enough - paddle 2 todo */ if(ballXpaddle(b,p1)) { //printf("ping\n");fflush(stdout); b->speedX*=-1.0; *p1s+=1; printscore(p1s,p2s); } if(ballXpaddle(b,p2)) { b->speedX*=-1.0; *p2s+=1; printscore(p1s,p2s); } /* update position */ b->x+=f*b->speedX; b->y+=f*b->speedY; if(b->speedX<0.0) b->speedX-=0.05; else b->speedX+=0.05; if(b->speedY<0.0)b->speedY-=0.05; else b->speedY+=0.05; p1->speed+=0.1; p2->speed+=0.1; } void drawBall(ball *b) { GLint matrixmode=0; glGetIntegerv(GL_MATRIX_MODE,&matrixmode); /* get current matrix mode */ glMatrixMode(GL_MODELVIEW); /* set the modelview matrix */ glPushMatrix(); /* store current modelview matrix */ glTranslated(b->x,b->y,0.0); /* move the ball to its correct position */ glBegin(GL_QUADS); /* draw ball */ glColor3f(1.0,1.0,1.0); glVertex3d(b->radius/-2.0,b->radius/2.0,0.0); glVertex3d(b->radius/2.0,b->radius/2.0,0.0); glVertex3d(b->radius/2.0,b->radius/-2.0,0.0); glVertex3d(b->radius/-2.0,b->radius/-2.0,0.0); glEnd(); glPopMatrix(); /* restore previous modelview matrix */ glMatrixMode(matrixmode); /* set the previous matrix mode */ } void drawPaddle(paddle *p) { GLint matrixmode=0; glGetIntegerv(GL_MATRIX_MODE,&matrixmode); /* get current matrix mode */ glMatrixMode(GL_MODELVIEW); /* set the modelview matrix */ glPushMatrix(); /* store current modelview matrix */ glTranslated(p->x,p->y,0.0); /* move the ball to its correct position */ glBegin(GL_QUADS); /* draw paddle */ glColor3f(1.0,1.0,1.0); glVertex3d(p->width/-2.0,p->height/2.0,0.0); glVertex3d(p->width/2.0,p->height/2.0,0.0); glVertex3d(p->width/2.0,p->height/-2.0,0.0); glVertex3d(p->width/-2.0,p->height/-2.0,0.0); glEnd(); glPopMatrix(); /* restore previous modelview matrix */ glMatrixMode(matrixmode); /* set the previous matrix mode */ } void render(ball *b,paddle *p1, paddle *p2) { /* Start by clearing the framebuffer (what was drawn before) */ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); /* Set the scene transformations */ glMatrixMode(GL_MODELVIEW); /* set the modelview matrix */ glLoadIdentity(); /* Set it to the identity (no transformations) */ /* draw the objects */ drawBall(b); drawPaddle(p1); drawPaddle(p2); /* now draw the background */ glBegin(GL_LINES); glVertex3f(0.0f,220.0f,0.0f); glVertex3f(0.0f,-220.0f,0.0f); glEnd(); glFlush(); } int main(void) { /* These are some variables to help show you what the parameters are for the initialisation function. You can experiment with the numbers to see what they do. */ int winPosX = 100; int winPosY = 100; int winWidth = 640; int winHeight = 480; int go; int p1s=0; // Player 1 score int p2s=0; // Player 2 score Uint32 timer; /* animation timer (in milliseconds) */ paddle p1,p2; ball myB; int p1dir=0,p2dir=0; /* This is our initialisation phase SDL_Init is the main initialisation function for SDL It takes a 'flag' parameter which we use to tell SDL what systems we are going to use Here, we want to initialise everything, so we give it the flag for this. This function also returns an error value if something goes wrong, so we can put this straight in an 'if' statement to check and exit if need be */ if( SDL_Init( SDL_INIT_EVERYTHING ) != 0 ) { /* Something went very wrong in the initialisation, all we can do is exit */ perror("Whoops! Something went very wrong, cannot initialise SDL :("); return -1; } /* Now we have got SDL initialised, we are ready to create an OpenGL window! */ SDL_Window *window = SDL_CreateWindow("My Pongy Game!!!", /* The first parameter is the window title */ winPosX, winPosY, winWidth, winHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); /* ensure that OpenGL gets enabled here */ /* The last parameter lets us specify a number of options. Here, we tell SDL that we want the window to be shown and that it can be resized. You can learn more about SDL_CreateWindow here: https://wiki.libsdl.org/SDL_CreateWindow?highlight=%28\bCategoryVideo\b%29|%28CategoryEnum%29|%28CategoryStruct%29 The flags you can pass in for the last parameter are listed here: https://wiki.libsdl.org/SDL_WindowFlags The SDL_CreateWindow function returns an SDL_Window. This is a structure which contains all the data about our window (size, position, etc). We will also need this when we want to draw things to the window. This is therefore quite important we do not lose it! */ if( window == NULL ) { printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() ); exit(EXIT_FAILURE); /* crash out if there has been an error */ } /* Use OpenGL 1.5 compatibility */ SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1 ); SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 5 ); SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY ); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); /* set up Z-Buffer */ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); /* enable double buffering */ /* SDL_GLContext is the OpenGL rendering context - this is the equivalent to the SDL_Renderer when drawing pixels to the window */ SDL_GLContext context = SDL_GL_CreateContext( window ); if( context == NULL ) { printf( "OpenGL Rendering Context could not be created! SDL Error: %s\n", SDL_GetError() ); exit(EXIT_FAILURE); /* crash out if there has been an error */ } /* Set up the parts of the scene that will stay the same for every frame. */ glFrontFace(GL_CCW); /* Enforce counter clockwise face ordering (to determine front and back side) */ glEnable(GL_NORMALIZE); glShadeModel(GL_FLAT); /* enable flat shading - Gouraud shading would be GL_SMOOTH */ glEnable(GL_DEPTH_TEST); /* Set the clear (background) colour */ glClearColor(0.0f, 0.0f, 0.0f, 1.0f); /* Set up the camera/viewing volume (projection matrix) */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-1.0*(GLdouble)(winWidth/2), (GLdouble)(winWidth/2),-1.0*(GLdouble)(winHeight/2),(GLdouble)(winHeight/2)); glViewport(0,0,winWidth,winHeight); /* initialize the timer */ timer = SDL_GetTicks(); /* initialize objects */ initializeBall(&myB,0.0,0.0,8.0,70.0,30.0); initializePaddle(&p1,-295.0,0.0,6,30,1,50.0); initializePaddle(&p2,295.0,0.0,6,30,-1,50.0); /* We are now preparing for our main loop. This loop will keep going round until we exit from our program by changing the int 'go' to the value false (0). This loop is an important concept and forms the basis of most SDL programs you will be writing. Within this loop we generally do the following things: * Check for input from the user (and do something about it!) * Update our graphics (if necessary, e.g. for animation) * Draw our graphics */ go = 1; while( go ) { /* Here we are going to check for any input events. Basically when you press the keyboard or move the mouse, the parameters are stored as something called an 'event' or 'message'. SDL has a queue of events. We need to check for each event and then do something about it (called 'event handling'). The SDL_Event is the data type for the event. */ SDL_Event incomingEvent; double fraction=0.0; /* SDL_PollEvent will check if there is an event in the queue - this is the program's 'message pump'. If there is nothing in the queue it will not sit and wait around for an event to come along (there are functions which do this, and that can be useful too!). Instead for an empty queue it will simply return 'false' (0). If there is an event, the function will return 'true' (!=0) and it will fill the 'incomingEvent' we have given it as a parameter with the event data */ while( SDL_PollEvent( &incomingEvent ) ) { /* If we get in here, we have an event and need to figure out what to do with it. For now, we will just use a switch based on the event's type */ switch( incomingEvent.type ) { case SDL_QUIT: /* The event type is SDL_QUIT. This means we have been asked to quit - probably the user clicked on the 'x' at the top right corner of the window. To quit we need to set our 'go' variable to false (0) so that we can escape out of the main loop. */ go = 0; break; case SDL_KEYDOWN: switch( incomingEvent.key.keysym.sym ) { case SDLK_UP: p2dir=1; break; case SDLK_DOWN: p2dir=-1; break; case SDLK_w: p1dir=1; break; case SDLK_s: p1dir=-1; break; } break; case SDL_KEYUP: switch( incomingEvent.key.keysym.sym ) { case SDLK_UP: case SDLK_DOWN: p2dir=0; break; case SDLK_w: case SDLK_s: p1dir=0; break; case SDLK_ESCAPE: go = 0; break; } break; /* If you want to learn more about event handling and different SDL event types, see: https://wiki.libsdl.org/SDL_Event and also: https://wiki.libsdl.org/SDL_EventType */ } } /* update timer */ { Uint32 old=timer; timer = SDL_GetTicks(); fraction = (double) (timer-old)/1000.0; /* calculate the frametime by finding the difference in ms from the last update/frame and divide by 1000 to get to the fraction of a second */ } /* update positions */ updatePaddle(&p1,fraction,p1dir,winHeight); /* move paddle 1 */ /* move paddle 2 */ updatePaddle(&p2,fraction,p2dir,winHeight); updateBall(&myB,fraction,&p1,&p2,winWidth,winHeight,&p1s,&p2s); /* move ball and check collisions with the paddles */ char windowTitle[100]; sprintf(windowTitle, "P1: %d | P2: %d", p1s, p2s); SDL_SetWindowTitle(window, windowTitle); /* Render our scene. */ render(&myB,&p1,&p2); /* This does the double-buffering page-flip, drawing the scene onto the screen. */ SDL_GL_SwapWindow( window ); } /* If we get outside the main loop, it means our user has requested we exit. */ /* Our cleanup phase, hopefully fairly self-explanatory ;) */ SDL_GL_DeleteContext(context); SDL_DestroyWindow( window ); SDL_Quit(); return 0; }