419 lines
13 KiB
C
419 lines
13 KiB
C
// This is the main SDL include file
|
|
#include <SDL2/SDL.h>
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
/* 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;
|
|
}
|