Pong in Simple DirectMedia Layer 2


(/home/alkaline) #1

(Note: Pong in SDL2 isn’t 15 characters. Damnit.)

So in my gamedev course we’re coding Pong in SDL2. I’m not done it yet but I thought I’d share the code so far:

// Name: Alkaline Thunder
// Course: GAME202
// Prof: SGT. James Heller
// Title of program: Pong
// Date: Jan. 10th, 2019


// Error logging - we need iostream.
#include <iostream>
#include <random>
#include <ctime>

// Include the SDL2 library.
#include "SDL.h"

// Also for error logging so we don't need std:: everywhere.
using namespace std;

// This is the size of the game window.
#define WINDOW_WIDTH 1920
#define WINDOW_HEIGHT 1080

// Are we still running the game?
bool running = true;

// The speed and direction of the ball.
int speedX, speedY;
int direction[2] = { -1, 1 };

// An SDL renderer for the game.
SDL_Renderer* renderer;

// Unknown...
SDL_Event event;

// Holds information about the most recently polled SDL event.
int mouseX, mouseY = 0;

// Holds the player and AI score values.
int PlayerScore, AIScore = 0;

// Rectangles for each object on-screen.
SDL_Rect PlayerPaddle, AIPaddle, Ball;

// Checks if two rectangles collide/overlap with each other.
bool IntersectsWith(SDL_Rect& A, SDL_Rect& B)
{
	// The sides of each rectangle
	int LeftA, LeftB;
	int RightA, RightB;
	int TopA, TopB;
	int BottomA, BottomB;

	// Calculate the sides of rectangle A
	LeftA = A.x;
	RightA = A.x + A.w;
	TopA = A.y;
	BottomA = A.y + A.h;

	// Now do it for Rectangle B
	LeftB = B.x;
	RightB = B.x + B.w;
	TopB = B.y;
	BottomB = B.y + B.h;

	// If any side of Rectangle A are outside of Rectangle B, the rectangles DON'T collide.
	if (BottomA < TopB)
		return false;

	if (TopA > BottomB)
		return false;

	if (LeftA > RightB)
		return false;

	if (RightA < LeftB)
		return false;

	// If we get this far, the rectangles collide.
	return true;
}

// Initializes SDL, the game window, and starts up the game.
void LoadGame()
{
	// Seed the random number generator
	srand(time(0));

	// Attempts to create a window for the game.
	SDL_Window* GameWindow = SDL_CreateWindow("Pong", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
	
	// Create the renderer object
	renderer = SDL_CreateRenderer(GameWindow, 0, SDL_RENDERER_ACCELERATED);

	// If the window is invalid, throw an error.
	if (!GameWindow)
	{
		cerr << "FATAL: Couldn't start up the SDL window.  Stop." << endl;
		return;
	}

	// Initialize the rectangles to their original positions.
	PlayerPaddle.x = 20;
	PlayerPaddle.h = 100;
	// Starts the player paddle in the center of the Y axis.
	PlayerPaddle.y = (WINDOW_HEIGHT - PlayerPaddle.h) / 2;
	PlayerPaddle.w = 20;

	// Copies the position of the Player Paddle to the AI paddle to save lines of code.
	AIPaddle = SDL_Rect(PlayerPaddle);

	// Sets the x axis of the AI paddle to the far right of the screen.
	AIPaddle.x = (WINDOW_WIDTH - 20) - AIPaddle.w;

	// Set the ball size.
	Ball.w = Ball.h = 20;

	// Set the ball to the center of the screen.
	Ball.x = (WINDOW_WIDTH - Ball.w) / 2;
	Ball.y = (WINDOW_HEIGHT - Ball.h) / 2;

	// Initialize the speed values for the ball.
	speedX = speedY = -1;


}

// Processes all user input. I think.
void Input()
{
	// Poll events from SDL until we run out.
	while (SDL_PollEvent(&event))
	{
		// Handle the user quitting the game
		if (event.type == SDL_QUIT)
		{
			running = false;
		}

		// Handle mouse movement.
		if (event.type == SDL_MOUSEMOTION)
		{
			// Grab the mouse's current location.
			SDL_GetMouseState(&mouseX, &mouseY);
		}
		
		// Handle keyboard presses.
		if (event.type == SDL_KEYDOWN)
		{
			switch (event.key.keysym.sym)
			{
			case SDLK_ESCAPE:
				// Quit the game.
				running = false;
				break;
			}
		}

	}
}

// Updates the game's state.
void Update()
{
	// Set the Y coordinate of the player's paddle based on the mouse's Y position.
	PlayerPaddle.y = mouseY - (PlayerPaddle.h / 2); // this also makes it so the paddle is center-aligned to the mouse.

	// Move the ball.
	Ball.x += speedX;
	Ball.y += speedY;

	// If the ball is outside the left wall, AI gets a point.
	if (Ball.x + Ball.w < 0)
	{
		AIScore++;
		cout << "The AI beat you. They now have " << AIScore << " point(s)." << endl;
	}

	// If the ball is outside the right wall, then the player gets a point.
	if (Ball.x - Ball.w > WINDOW_WIDTH)
	{
		PlayerScore++;
		cout << "You beat the AI. You now have " << PlayerScore << " point(s)." << endl;
	}

	// If the ball is outside of the left or right of the screen we reset it to the centre.
	if (Ball.x + Ball.w < 0 || (Ball.x - Ball.w) > WINDOW_WIDTH)
	{
		Ball.x = (WINDOW_WIDTH - Ball.w) / 2;
		Ball.y = (WINDOW_HEIGHT - Ball.h) / 2;
	
		// Initialize the speed with random numbers.
		speedX = (rand() % 2 + 1) * direction[rand() % 2];
		speedY = (rand() % 2 + 1) * direction[rand() % 2];
	}

	if (Ball.y < 0 || (Ball.y + Ball.h) > WINDOW_HEIGHT)
	{
		speedY = -speedY;
	}

	// Generate a random byte.
	int randByte = rand() % 255;

	// If this byte is divisible by 15, we fail to update the AI.
	// This results in the AI having an "error," which makes
	// it more human.
	// if (randByte % 15 == 0)
	// {
		// Get the distance between the ball and the AI paddle.
		int aiCenterY = AIPaddle.y + (AIPaddle.h / 2);
		int ballCenterY = Ball.y + (Ball.h / 2);

		if (aiCenterY > 0 && aiCenterY < WINDOW_HEIGHT)
		{
			int ballCenterX = Ball.x + (Ball.w / 2);
			if (ballCenterX > (WINDOW_WIDTH / 2))
			{
				int distance = ballCenterY - aiCenterY;

				// Normalize the value.
				if (distance < 0)
					distance = -1;
				else if (distance > 0)
					distance = 1;

				// Multiply the distance by the ball speed. This is the velocity the AI will travel in.
				int aiVelocity = distance * abs(speedY);

				// Increment the AI Y coordinate by the velocity.
				AIPaddle.y += aiVelocity;
			}
		}
	//}

	// Collision check for the player.
	if(IntersectsWith(Ball, PlayerPaddle))
	{
		// X speed goes positive.
		speedX = abs(speedX);

		// Ball x coordinate goes to the right of the player paddle.
		// This fixes the possibility of the ball glitching inside
		// the paddle and never being able to escape.
		Ball.x = PlayerPaddle.x + PlayerPaddle.w;
	}

	// Do the same for the AI.
	if (IntersectsWith(Ball, AIPaddle))
	{
		// X speed goes negative.
		speedX = -abs(speedX);

		// Ball x coordinate goes to the left of the AI paddle.
		// This fixes the possibility of the ball glitching inside
		// the paddle and never being able to escape.
		Ball.x = AIPaddle.x - Ball.w;
	}

	// Slow the game down to 60 ticks per second.
	SDL_Delay(4);
}

// Draws the game to the screen based on the current state.
void DrawScreen()
{
	// Clear the screen so we can draw on it.
	SDL_RenderClear(renderer);

	// Render the pong background.
	SDL_Rect background{ 0,0,WINDOW_WIDTH,WINDOW_HEIGHT };
	SDL_SetRenderDrawColor(renderer, 0x22, 0x22, 0x22, 0xFF);
	SDL_RenderFillRect(renderer, &background);

	// Set the color to white.
	SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);

	// Render the two paddles.
	SDL_RenderFillRect(renderer, &PlayerPaddle);
	SDL_RenderFillRect(renderer, &AIPaddle);

	// Draw a line down the center of the screen.
	int lineWidth = 4;
	SDL_Rect CenterLine = { (WINDOW_WIDTH - lineWidth) / 2, 0, lineWidth, WINDOW_HEIGHT };
	SDL_RenderFillRect(renderer, &CenterLine);

	// Render the ball.
	SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
	SDL_RenderFillRect(renderer, &Ball);

	// Present the backbuffer to the screen.
	SDL_RenderPresent(renderer);
}

// Shuts down the game and SDL.
void Quit()
{
	SDL_Quit();
}

// Starts up the game loop.
int main(int argc, char* argv[])
{
	// Start up SDL.
	LoadGame();

	// Enter the game loop. It ends when running = false.
	while (running)
	{
		// Process player input.
		Input();

		// Update game state.
		Update();
		
		// Render the game world.
		DrawScreen();
	}

	// Tear down SDL.
	Quit();

	// Tear down C++.
	return 0;
}