mirror of
https://github.com/thepigeongenerator/breakout_clone.git
synced 2025-12-17 06:15:46 +01:00
add missing files
This commit is contained in:
BIN
assets/bounce.wav
Normal file
BIN
assets/bounce.wav
Normal file
Binary file not shown.
71
build.sh
Executable file
71
build.sh
Executable file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# define variables
|
||||||
|
PROJECT_NAME=${PWD##*/}
|
||||||
|
BUILD_DIR="./build"
|
||||||
|
INCLUDE_IN_BUILD="./assets"
|
||||||
|
|
||||||
|
# compilation targets
|
||||||
|
args_linux86_64()
|
||||||
|
{
|
||||||
|
ARCHITECTURE="linux-86_64"
|
||||||
|
COMPILER="/bin/gcc"
|
||||||
|
ARGS="-lSDL2"
|
||||||
|
FILE_EXSTENSION=""
|
||||||
|
}
|
||||||
|
|
||||||
|
args_win86-64()
|
||||||
|
{
|
||||||
|
ARCHITECTURE="win-86_64"
|
||||||
|
COMPILER="/usr/bin/x86_64-w64-mingw32-gcc"
|
||||||
|
ARGS="-lmingw32 -lSDL2main -lSDL2 -mwindows"
|
||||||
|
INCLUDE_IN_BUILD="$INCLUDE_IN_BUILD /usr/x86_64-w64-mingw32/bin/SDL2.dll"
|
||||||
|
FILE_EXSTENSION=".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
args_emscripten()
|
||||||
|
{
|
||||||
|
ARCHITECTURE="web"
|
||||||
|
COMPILER="/home/user/.local/bin/emcc"
|
||||||
|
ARGS="-s USE_SDL=2"
|
||||||
|
FILE_EXSTENSION=".html"
|
||||||
|
}
|
||||||
|
|
||||||
|
# handle arguments
|
||||||
|
if [ "$1" = "linux86_64" ]; then args_linux86_64
|
||||||
|
elif [ "$1" = "win86_64" ]; then args_win86-64
|
||||||
|
elif [ "$1" = "web" ]; then args_emscripten
|
||||||
|
else echo -e "\033[91mdidn't include any arguments! D:\033[0m" && exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check whether $BUILD_DIR or $ARCHITECTURE isn't set
|
||||||
|
if [[ -z $BUILD_DIR ]] || [[ -z "$ARCHITECTURE" ]]; then
|
||||||
|
echo -e "\033[91mBUILD_DIR or ARCHITECTURE not set D:\033[0m"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# make (and clear) the build directory
|
||||||
|
mkdir $BUILD_DIR $BUILD_DIR/$ARCHITECTURE
|
||||||
|
rm -rf $BUILD_DIR/$ARCHITECTURE/*
|
||||||
|
|
||||||
|
# copy included files or directories to the build directory
|
||||||
|
if [[ ! -z $INCLUDE_IN_BUILD ]]; then
|
||||||
|
cp -r $INCLUDE_IN_BUILD $BUILD_DIR/$ARCHITECTURE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get the executable path
|
||||||
|
EXE_PATH=$BUILD_DIR/$ARCHITECTURE/$PROJECT_NAME$FILE_EXSTENSION
|
||||||
|
echo "building at: $EXE_PATH"
|
||||||
|
|
||||||
|
# check whether the compiler path contains an executable at said path
|
||||||
|
if [ ! -x $COMPILER ]; then
|
||||||
|
echo -e "\033[91mCouldn't find an executable at path: \033[0m $COMPILER"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# compile the code
|
||||||
|
COMMAND="$COMPILER `find ./src -name *.c` -o $EXE_PATH -Wall -g -lm $ARGS"
|
||||||
|
echo "using command: $COMMAND"
|
||||||
|
$COMMAND
|
||||||
|
exit $?
|
||||||
32
src/errors.c
Normal file
32
src/errors.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define MAX_STR_LEN 128
|
||||||
|
|
||||||
|
void error(const ErrorCode error_code, const char* format, ...) {
|
||||||
|
char buffer[MAX_STR_LEN] = {0}; // contains the buffer of the final string
|
||||||
|
|
||||||
|
va_list args = {0};
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(buffer, MAX_STR_LEN, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
printf("\033[91mE\033[0m: %s\n", buffer);
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "something went wrong! :O", buffer, NULL);
|
||||||
|
|
||||||
|
exit(error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void warn(const char* format, ...) {
|
||||||
|
char buffer[MAX_STR_LEN] = {0}; // contains the buffer of the final string
|
||||||
|
|
||||||
|
va_list args = {0};
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(buffer, MAX_STR_LEN, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
printf("\033[93mW\033[0m: %s\n", buffer);
|
||||||
|
}
|
||||||
24
src/errors.h
Normal file
24
src/errors.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef unsigned char ErrorCode;
|
||||||
|
enum {
|
||||||
|
SUCCESS = 0,
|
||||||
|
FAILURE = -1,
|
||||||
|
|
||||||
|
FAILURE_STD = 1 << 0, // 1 : marks a generic C library error
|
||||||
|
FAILURE_MEMORY = FAILURE_STD | (1 << 1), // 3 : marks a memory related error
|
||||||
|
|
||||||
|
// SDL ERRORS
|
||||||
|
FAILURE_SDL = 1 << 1, // 2 : marks a generic SDL error
|
||||||
|
FAILURE_SDL_INIT = FAILURE_SDL | (1 << 2), // 6 : marks an error during SDL initialisation
|
||||||
|
FAILURE_SDL_WINDOW = FAILURE_SDL | (1 << 3), // 10 : marks an error with the window
|
||||||
|
FAILURE_SDL_RENDERER = FAILURE_SDL | (1 << 4), // 18 : marks an error with the renderer
|
||||||
|
FAILURE_SDL_AUDIO = FAILURE_SDL | (1 << 5), // 34 : marks an error with audio
|
||||||
|
|
||||||
|
// GAME ERRORS
|
||||||
|
FAILURE_GAME = 1 << 2, // 4 : marks a generic game error
|
||||||
|
};
|
||||||
|
|
||||||
|
// call when a fatal error has occurred, the program will immediately terminate when called
|
||||||
|
void error(const ErrorCode error_code, const char* format, ...);
|
||||||
|
void warn(const char* format, ...);
|
||||||
168
src/game/level.c
Normal file
168
src/game/level.c
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#include "level.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "../constants.h"
|
||||||
|
#include "../main.h"
|
||||||
|
#include "../window/renderer.h"
|
||||||
|
#include "vector2.h"
|
||||||
|
|
||||||
|
|
||||||
|
// prepares the level to be in a playable state
|
||||||
|
void level_init(Level* level) {
|
||||||
|
// initialize bouncer
|
||||||
|
level->bouncer.pos.x = (SCREEN_WIDTH / 2) - (BOUNCER_WIDTH_DEFAULT / 2);
|
||||||
|
level->bouncer.pos.y = SCREEN_HEIGHT - (BOUNCER_HEIGHT * 2);
|
||||||
|
level->bouncer.width = BOUNCER_WIDTH_DEFAULT;
|
||||||
|
|
||||||
|
// initialize ball
|
||||||
|
level->ball.pos.x = (SCREEN_WIDTH / 2) - (BALL_SIZE_DEFAULT / 2);
|
||||||
|
level->ball.pos.y = level->bouncer.pos.y - BOUNCER_HEIGHT - BALL_SIZE_DEFAULT;
|
||||||
|
level->ball.moving = false;
|
||||||
|
level->ball.size = BALL_SIZE_DEFAULT;
|
||||||
|
level->ball.direction = (Vector2){0, -BALL_SPEED};
|
||||||
|
|
||||||
|
// initialize bricks
|
||||||
|
// define the colours of the brick rows
|
||||||
|
const Colour colours[BRICK_COLUMNS] = {
|
||||||
|
{0x5BCEFAFF},
|
||||||
|
{0xF5A9B8FF},
|
||||||
|
{0xFFFFFFFF},
|
||||||
|
{0xF5A9B8FF},
|
||||||
|
{0x5BCEFAFF}};
|
||||||
|
|
||||||
|
int brick_width = BRICK_WIDTH + BRICK_PADDING;
|
||||||
|
int brick_height = BRICK_HEIGHT + (BRICK_PADDING / 2);
|
||||||
|
float level_padding_left = ((float)SCREEN_WIDTH - ((SCREEN_WIDTH / brick_width) * brick_width)) / 2.0F; // for centering
|
||||||
|
|
||||||
|
// store bricks in the level
|
||||||
|
for (int x = 0; x < BRICK_COLUMNS; x++) {
|
||||||
|
for (int y = 0; y < BRICK_ROWS; y++) {
|
||||||
|
Brick* brick = &level->bricks[x][y];
|
||||||
|
brick->colour = colours[y];
|
||||||
|
brick->pos.x = (x * brick_width) + level_padding_left;
|
||||||
|
brick->pos.y = (y * brick_height) + BRICK_PADDING_TOP;
|
||||||
|
brick->destroyed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates the player's "bouncer"
|
||||||
|
static void update_player(Bouncer* bouncer, Ball* ball, const Uint8* keys) {
|
||||||
|
// if move bouncer LEFT
|
||||||
|
if (keys[SDL_SCANCODE_A] || keys[SDL_SCANCODE_LEFT]) {
|
||||||
|
if (((bouncer->pos.x) < 0) == false) {
|
||||||
|
bouncer->pos.x -= BOUNCER_SPEED;
|
||||||
|
|
||||||
|
if (ball->moving == false)
|
||||||
|
ball->pos.x -= BOUNCER_SPEED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if move bouncer RIGHT
|
||||||
|
if (keys[SDL_SCANCODE_D] || keys[SDL_SCANCODE_RIGHT]) {
|
||||||
|
if (((bouncer->pos.x + bouncer->width) > SCREEN_WIDTH) == false) {
|
||||||
|
bouncer->pos.x += BOUNCER_SPEED; // increase the bouncer pos
|
||||||
|
|
||||||
|
if (ball->moving == false)
|
||||||
|
ball->pos.x += BOUNCER_SPEED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ball launching
|
||||||
|
if (ball->moving == false && keys[SDL_SCANCODE_SPACE]) {
|
||||||
|
ball->moving = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates the player's ball
|
||||||
|
static void update_ball(Level* level, Ball* ball, Bouncer* bouncer) {
|
||||||
|
// update ball position
|
||||||
|
ball->pos.x += ball->direction.x;
|
||||||
|
ball->pos.y += ball->direction.y;
|
||||||
|
|
||||||
|
|
||||||
|
// check X axis out of bounds collisions
|
||||||
|
if ((ball->pos.x + ball->size) > SCREEN_WIDTH || ball->pos.x < 0) {
|
||||||
|
audio_play(level->audio_device, level->bounce_sfx);
|
||||||
|
ball->direction.x *= -1;
|
||||||
|
ball->pos.x = (ball->pos.x < 0) ? 0 : SCREEN_WIDTH - ball->size; // correct the ball's position
|
||||||
|
}
|
||||||
|
|
||||||
|
// check Y axis out of bounds collisions
|
||||||
|
if (ball->pos.y < 0) {
|
||||||
|
audio_play(level->audio_device, level->bounce_sfx);
|
||||||
|
ball->direction.y *= -1;
|
||||||
|
ball->pos.y = 0; // correct the ball's position
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// check bouncer collisions
|
||||||
|
if ((ball->pos.x + ball->size) > bouncer->pos.x && ball->pos.x < (bouncer->pos.x + bouncer->width) &&
|
||||||
|
(ball->pos.y + ball->size) > bouncer->pos.y && ball->pos.y < (bouncer->pos.y + BOUNCER_HEIGHT)) {
|
||||||
|
float x = ball->pos.x - bouncer->pos.x + (ball->size / 2) + 2; // get the X axis relative to the bouncer (add 2, see below)
|
||||||
|
unsigned max = bouncer->width + 4; // get the maxiumum of this X axis (add 4 to make it feel more accurate)
|
||||||
|
float angle = (x - (max / 2.0F)) / max * (PI / 1.5F); // calculate the angle in radians where the bouncer X axis falls on -60° to 60°
|
||||||
|
|
||||||
|
// change the ball direction
|
||||||
|
audio_play(level->audio_device, level->bounce_sfx);
|
||||||
|
ball->direction.x = SDL_sinf(angle) * BALL_SPEED;
|
||||||
|
ball->direction.y = -SDL_cosf(angle) * BALL_SPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check brick collisions
|
||||||
|
bool collided = false;
|
||||||
|
for (int x = 0; x < BRICK_COLUMNS; x++) {
|
||||||
|
for (int y = 0; y < BRICK_ROWS; y++) {
|
||||||
|
const Brick* brick = &level->bricks[x][y];
|
||||||
|
|
||||||
|
if (brick->destroyed == true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float max_brick_x = brick->pos.x + BRICK_WIDTH;
|
||||||
|
const float brick_max_y = brick->pos.y + BRICK_HEIGHT;
|
||||||
|
if (ball->pos.x < max_brick_x && (ball->pos.x + ball->size) > brick->pos.x &&
|
||||||
|
ball->pos.y < brick_max_y && (ball->pos.y + ball->size) > brick->pos.y) {
|
||||||
|
level->bricks[x][y].destroyed = true;
|
||||||
|
|
||||||
|
// skip changing direction of we already did
|
||||||
|
if (collided == true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// float ball_centre_x = ball->pos.x + (ball->size / 2.0F);
|
||||||
|
float ball_centre_y = ball->pos.y + (ball->size / 2.0F);
|
||||||
|
|
||||||
|
// manage ball bounce direction; only bounce along the X axis if the ball's Y centre is in between dthe top and bottom of the block
|
||||||
|
if (brick->pos.y < ball_centre_y && ball_centre_y < brick_max_y) {
|
||||||
|
ball->direction.x *= -1;
|
||||||
|
} else {
|
||||||
|
ball->direction.y *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_play(level->audio_device, level->bounce_sfx);
|
||||||
|
collided = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates the level
|
||||||
|
void level_update(Level* level, const Uint8* keys) {
|
||||||
|
Bouncer* bouncer = &level->bouncer;
|
||||||
|
Ball* ball = &level->ball;
|
||||||
|
|
||||||
|
update_player(bouncer, ball, keys);
|
||||||
|
|
||||||
|
if (ball->moving == true)
|
||||||
|
update_ball(level, ball, bouncer);
|
||||||
|
|
||||||
|
|
||||||
|
// check lose condition
|
||||||
|
if ((ball->pos.y + ball->size) > SCREEN_HEIGHT) {
|
||||||
|
stop();
|
||||||
|
ball->moving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/game/level.h
Normal file
38
src/game/level.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "../constants.h"
|
||||||
|
#include "../window/colour.h"
|
||||||
|
#include "../window/audio.h"
|
||||||
|
#include "vector2.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vector2 pos;
|
||||||
|
Vector2 direction;
|
||||||
|
unsigned size;
|
||||||
|
bool moving;
|
||||||
|
} Ball;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vector2 pos;
|
||||||
|
unsigned width;
|
||||||
|
} Bouncer;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vector2 pos;
|
||||||
|
Colour colour;
|
||||||
|
bool destroyed;
|
||||||
|
} Brick;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Ball ball;
|
||||||
|
Bouncer bouncer;
|
||||||
|
Brick bricks[BRICK_COLUMNS][BRICK_ROWS];
|
||||||
|
AudioDevice* audio_device;
|
||||||
|
AudioData bounce_sfx;
|
||||||
|
} Level;
|
||||||
|
|
||||||
|
|
||||||
|
void level_init(Level* level);
|
||||||
|
void level_update(Level* level, const Uint8* keys);
|
||||||
18
src/game/vector2.h
Normal file
18
src/game/vector2.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define VECTOR2_UP ((Vector2){0, 1})
|
||||||
|
#define VECTOR2_DOWN ((Vector2){0, -1})
|
||||||
|
#define VECTOR2_LEFT ((Vector2){-1, 0})
|
||||||
|
#define VECTOR2_RIGHT ((Vector2){1, 0})
|
||||||
|
#define VECTOR2_ZERO ((Vector2){0, 0})
|
||||||
|
#define VECTOR2_ONE ((Vector2){1, 1})
|
||||||
|
|
||||||
|
#define vec2_add(v1, v2) ((Vector2){v1.x + v2.x, v1.y + v2.y})
|
||||||
|
#define vec2_subt(v1, v2) ((Vector2){v1.x - v2.x, v1.y - v2.y})
|
||||||
|
#define vec2_mult(v, a) ((Vector2){v.x * a, v.y * a})
|
||||||
|
#define vec2_div(v, a) ((Vector2){v.x / a, v.y / a})
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
} Vector2;
|
||||||
4
src/main.h
Normal file
4
src/main.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// stops execution of the game
|
||||||
|
void stop(void);
|
||||||
110
src/window/audio.c
Normal file
110
src/window/audio.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#include "audio.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include "../errors.h"
|
||||||
|
|
||||||
|
// the maximum amount of sounds that can play at once
|
||||||
|
#define MAX_SOUNDS 64
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
AudioData* playing_audio;
|
||||||
|
AudioDevice audio_device;
|
||||||
|
} AudioCallbackData;
|
||||||
|
|
||||||
|
// audio callback from SDL_AudioSpec; called when the audio device needs more data
|
||||||
|
static void audio_mixer(void* userdata, Uint8* stream, int len) {
|
||||||
|
memset(stream, 0, len); // clear the playing audio
|
||||||
|
AudioDevice* device = userdata; // get the callback data
|
||||||
|
AudioData* audio = device->playing_audio;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SOUNDS; i++) {
|
||||||
|
// skip if the audio doesn't conain any further data
|
||||||
|
if (audio[i].length <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the length of which we shall be mixing
|
||||||
|
Uint32 mix_length = SDL_min(audio[i].length, len);
|
||||||
|
|
||||||
|
// mix the audio with the stream
|
||||||
|
SDL_MixAudioFormat(stream, audio[i].buffer, device->format, mix_length, SDL_MIX_MAXVOLUME);
|
||||||
|
audio[i].buffer += mix_length; // move the pointer up a a bit
|
||||||
|
audio[i].length -= mix_length; // move up the mixed amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts the audio to the format of the audio device
|
||||||
|
static void convert_audio(const AudioDevice* audio_device, const SDL_AudioSpec wav_spec, Uint8** wav_buffer, Uint32* wav_length) {
|
||||||
|
// build the audio converter with the audio given
|
||||||
|
SDL_AudioCVT cvt = {0};
|
||||||
|
SDL_BuildAudioCVT(&cvt, wav_spec.format, wav_spec.channels, wav_spec.freq, audio_device->format, audio_device->channels, audio_device->freq);
|
||||||
|
|
||||||
|
cvt.len = (*wav_length) * wav_spec.channels; // the buffer length
|
||||||
|
cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult); // allocate size for the new buffer
|
||||||
|
memcpy(cvt.buf, *wav_buffer, *wav_length); // copy wav data to cvt buffer;
|
||||||
|
|
||||||
|
// convert
|
||||||
|
SDL_ConvertAudio(&cvt);
|
||||||
|
|
||||||
|
// output
|
||||||
|
*wav_length = cvt.len_cvt; // set the length to the new length
|
||||||
|
memcpy(*wav_buffer, cvt.buf, cvt.len_cvt); // copy converted cvt buffer back to wav buffer
|
||||||
|
|
||||||
|
free(cvt.buf); // free the memory allocated to the cvt buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// loads a WAV file and returns the relevant information
|
||||||
|
AudioData audio_load_wav(const AudioDevice* audio_device, const char* file_path) {
|
||||||
|
SDL_AudioSpec wav_spec = {0};
|
||||||
|
AudioData audio = {0};
|
||||||
|
|
||||||
|
SDL_LoadWAV(file_path, &wav_spec, &audio.buffer, &audio.length);
|
||||||
|
convert_audio(audio_device, wav_spec, &audio.buffer, &audio.length);
|
||||||
|
|
||||||
|
return audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initializes the audio device
|
||||||
|
AudioDevice* audio_device_init(const int freq, const SDL_AudioFormat format, const Uint8 channels, const Uint16 samples) {
|
||||||
|
// allocate memory for the audio device
|
||||||
|
AudioDevice* audio_device = malloc(sizeof(AudioDevice));
|
||||||
|
|
||||||
|
// define the audio specification
|
||||||
|
SDL_AudioSpec spec = {freq, format, channels, samples};
|
||||||
|
spec.callback = audio_mixer;
|
||||||
|
spec.userdata = audio_device;
|
||||||
|
|
||||||
|
// create the audio device
|
||||||
|
*audio_device = (AudioDevice){
|
||||||
|
SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0),
|
||||||
|
freq,
|
||||||
|
format,
|
||||||
|
channels,
|
||||||
|
calloc(MAX_SOUNDS, sizeof(AudioData)), // allocate memory on the heap for the playing audio array
|
||||||
|
};
|
||||||
|
|
||||||
|
// if the audio device isn't set, cause an error
|
||||||
|
if (audio_device->id < 1) {
|
||||||
|
error(FAILURE_SDL_AUDIO, "AudioDivice failed to open! SDL Error: %s", SDL_GetError());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// default state of the device is paused, so we unpause it here
|
||||||
|
SDL_PauseAudioDevice(audio_device->id, 0);
|
||||||
|
return audio_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
// plays the audio
|
||||||
|
void audio_play(const AudioDevice* audio_device, const AudioData audio) {
|
||||||
|
AudioData* playing_audio = audio_device->playing_audio;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SOUNDS; i++) {
|
||||||
|
// overrite audio that has been deallocated
|
||||||
|
if (playing_audio[i].length <= 0) {
|
||||||
|
// override the audio
|
||||||
|
playing_audio[i] = audio;
|
||||||
|
break; // don't continue. :3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/window/audio.h
Normal file
20
src/window/audio.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Uint32 length;
|
||||||
|
Uint8* buffer;
|
||||||
|
} AudioData;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SDL_AudioDeviceID id;
|
||||||
|
int freq;
|
||||||
|
SDL_AudioFormat format;
|
||||||
|
Uint8 channels;
|
||||||
|
AudioData* playing_audio;
|
||||||
|
} AudioDevice;
|
||||||
|
|
||||||
|
|
||||||
|
AudioData audio_load_wav(const AudioDevice* audio_device, const char* file_path);
|
||||||
|
AudioDevice* audio_device_init(const int freq, const SDL_AudioFormat format, const Uint8 channels, const Uint16 samples);
|
||||||
|
void audio_play(const AudioDevice* audio_device, const AudioData audio);
|
||||||
9
src/window/colour.h
Normal file
9
src/window/colour.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
typedef union {
|
||||||
|
unsigned packed;
|
||||||
|
struct {
|
||||||
|
unsigned char a;
|
||||||
|
unsigned char b;
|
||||||
|
unsigned char g;
|
||||||
|
unsigned char r;
|
||||||
|
};
|
||||||
|
} Colour;
|
||||||
73
src/window/renderer.c
Normal file
73
src/window/renderer.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include "renderer.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "../constants.h"
|
||||||
|
#include "../errors.h"
|
||||||
|
#include "../game/level.h"
|
||||||
|
|
||||||
|
// initializes the window and renderer
|
||||||
|
int renderer_init(SDL_Window** window, SDL_Renderer** renderer) {
|
||||||
|
// init the window
|
||||||
|
*window = SDL_CreateWindow("Quinn's Breakout Clone", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
|
||||||
|
if (*window == NULL) {
|
||||||
|
error(FAILURE_SDL_WINDOW, "Window failed to be created! SDL_Error: %s", SDL_GetError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a renderer
|
||||||
|
*renderer = SDL_CreateRenderer(*window, -1, SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
|
||||||
|
if (*renderer == NULL) {
|
||||||
|
error(FAILURE_SDL_RENDERER, "Renderer failed to be created! SDL_Error: %s", SDL_GetError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// renders the screen
|
||||||
|
void renderer_update(RenderData* render_data) {
|
||||||
|
SDL_Renderer* renderer = render_data->renderer;
|
||||||
|
Level* level = render_data->level;
|
||||||
|
|
||||||
|
int success = 0; // if an error occurs, this value is <0
|
||||||
|
|
||||||
|
// render background
|
||||||
|
success |= SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
|
||||||
|
success |= SDL_RenderClear(renderer);
|
||||||
|
|
||||||
|
// draw player components
|
||||||
|
success |= SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||||
|
success |= SDL_RenderFillRectF(renderer, &(SDL_FRect){level->bouncer.pos.x, level->bouncer.pos.y, level->bouncer.width, 5}); // draw bouncer
|
||||||
|
success |= SDL_RenderFillRectF(renderer, &(SDL_FRect){level->ball.pos.x, level->ball.pos.y, level->ball.size, level->ball.size}); // draw ball
|
||||||
|
|
||||||
|
// draw bricks
|
||||||
|
for (int x = 0; x < BRICK_COLUMNS; x++) {
|
||||||
|
for (int y = 0; y < BRICK_ROWS; y++) {
|
||||||
|
Brick* brick = &level->bricks[x][y];
|
||||||
|
|
||||||
|
if (brick->destroyed == true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
success |= SDL_SetRenderDrawColor(renderer, brick->colour.r, brick->colour.g, brick->colour.b, brick->colour.a);
|
||||||
|
success |= SDL_RenderFillRectF(renderer, &(SDL_FRect){brick->pos.x, brick->pos.y, BRICK_WIDTH, BRICK_HEIGHT}); // draw brick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success < 0) {
|
||||||
|
error(FAILURE_SDL_RENDERER, "something went wrong whilst rendering: %s\n", SDL_GetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// present the result to the renderer
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_destroy(SDL_Window* window, SDL_Renderer* renderer) {
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
15
src/window/renderer.h
Normal file
15
src/window/renderer.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include "../game/level.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SDL_Window* window;
|
||||||
|
SDL_Renderer* renderer;
|
||||||
|
Level* level;
|
||||||
|
} RenderData;
|
||||||
|
|
||||||
|
int renderer_init(SDL_Window** window, SDL_Renderer** renderer);
|
||||||
|
void renderer_update(RenderData* render_data);
|
||||||
|
void renderer_destroy(SDL_Window* window, SDL_Renderer* renderer);
|
||||||
Reference in New Issue
Block a user