diff --git a/source/Bakery2D/Level/Level.cpp b/source/Bakery2D/Level/Level.cpp new file mode 100644 index 0000000..81b6d30 --- /dev/null +++ b/source/Bakery2D/Level/Level.cpp @@ -0,0 +1,39 @@ +#include "Level.hpp" + +template +Level::Level(T* levelName, char* spritesheetPath, C3D_RenderTarget* top, C3D_RenderTarget* bottom) : level(levelName), top(top), bottom(bottom) { + this->spritesheet = C2D_SpriteSheetLoad(spritesheetPath); + size_t numImages = C2D_SpriteSheetCount(spritesheet); + + for (size_t i = 0; i < numImages; i++) { + Sprite* thisSprite = &sprites[i]; + float x = SCREEN_WIDTH / 2; + float y = SCREEN_HEIGHT / 2; + + C2D_SpriteFromSheet(&thisSprite->spr, spritesheet, i); + C2D_SpriteSetCenter(&thisSprite->spr, 0.5f, 0.5f); + thisSprite->setPosition(x, y); + } +} + +void Level::run() { + if (!hasBeenSetup) { + level->setup(); + this->hasBeenSetup = true; + } + level->update(); + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(top, C3D_CLEAR_COLOR); + C2D_SceneBegin(top); + level->drawTop(); + + C2D_TargetClear(bottom, C3D_CLEAR_COLOR); + C2D_SceneBegin(bottom); + level->drawBottom(); + C3D_FrameEnd(0); +} + +void Level::cleanup() { + C2D_SpriteSheetFree(spriteSheet); +} \ No newline at end of file diff --git a/source/Bakery2D/Level/Level.hpp b/source/Bakery2D/Level/Level.hpp new file mode 100644 index 0000000..6d0f84f --- /dev/null +++ b/source/Bakery2D/Level/Level.hpp @@ -0,0 +1,75 @@ +/* + Level.hpp + Bakery2D + Created by breadone +*/ + +#pragma once +#ifndef LEVEL_HPP +#define LEVEL_HPP + +#include +#include "../Sprite/Sprite.hpp" + +#define SCREEN_WIDTH 400 +#define SCREEN_HEIGHT 240 +#define MAX_SPRITES 768 +/** + The Level class, the backdrops for your game, and the gateway to how they are controlled. + + Example: + ```cpp + struct mainLevel: Level { + mainLevel(): Level(this, "romfs:/gfx/mainlevel/sprites.t3x"); + + int score = 0; + bool gameOver = false; + + void setup() { + // set background sprite to correct location and scale + sprites[0].setPosition(200, 120); + C2D_SpriteSetScale(&sprites[SPR_BG].spr, 2.7778, 2.7907); // scale image to 400x240 (3ds screen res) + } + + void update() { + sprites[1].move(0, -0.4); // move bird down + sprites[2].move(-1, 0); // move pipe left + + // other game logic goes here... + } + + void drawTop() { + sprites[0].draw(); // draw background + sprites[1].draw(); // draw bird + sprites[2].draw(); // draw pipe + } + + void drawBottom() { + sprites[3].draw(); // draw scorecard + } + }; + ``` + */ +class Level { +public: + Sprite sprites[MAX_SPRITES]; + C2D_SpriteSheet spritesheet; + C3D_RenderTarget* top; + C3D_RenderTarget* bottom; + Level* level; + bool hasBeenSetup = false; + + virtual void setup() = 0; + virtual void update() = 0; + virtual void drawTop() = 0; + virtual void drawBottom() = 0; + + template + Level(T* levelName, char* spritesheetPath, C3D_RenderTarget* top, C3D_RenderTarget* bottom); + + // sets up the level if it hasnt been, then updates the logic, and draws the frame + void run(); + + virtual void cleanup(); +}; +#endif diff --git a/source/Bakery2D/Sprite/Sprite.cpp b/source/Bakery2D/Sprite/Sprite.cpp new file mode 100644 index 0000000..a1ea7bd --- /dev/null +++ b/source/Bakery2D/Sprite/Sprite.cpp @@ -0,0 +1,106 @@ +#include "Sprite.hpp" + +Sprite::Sprite() { + // C2D_SpriteSetCenter(&this->spr, 0.5f, 0.5f); + // C2D_SpriteSetPos(&this->spr, 400/2, 240/2); + this->x = 200; this->y = 120; +} + +Sprite::Sprite(C2D_Sprite* spr, float x, float y) { + this->x = x; this->y = y; this->spr = *spr; + + C2D_SpriteSetCenter(&this->spr, 0.5f, 0.5f); + C2D_SpriteSetPos(&this->spr, x, y); +} + +void Sprite::setPosition(float x, float y) { + this->x = x; + this->y = y; + + C2D_SpriteSetPos(&this->spr, x, y); +} + +void Sprite::move(float dx, float dy, bool overrideOOB) { + if (!overrideOOB) { + // make sure translate doesnt move to OOB + const float newX = this->x + dx; + const float newY = this->y + dy; + if (!((unsigned)(newX-1) <= 398 && (unsigned)(newY-1) <= 238)) { + return; + } + } + + this->x += dx; + this->y += dy; + + C2D_SpriteSetPos(&this->spr, this->x, this->y); +} + +void Sprite::setCenter(float x, float y) { + this->center[0] = x; this->center[1] = y; + + C2D_SpriteSetCenter(&this->spr, x, y); +} + +void Sprite::setHitbox(float w, float h) { + this->width = w; this->height = h; +} + +bool Sprite::isCollidingWith(Sprite &other) { + // hitbox not set + if (this->width == 0 && this->height == 0) return false; + + const float thisHalfWidth = this->width/2; + const float otherHalfWidth = other.width/2; + const float thisHalfHeight = this->height/2; + const float otherHalfHeight = other.height/2; + + // check x collision + if (this->x + thisHalfWidth >= other.x - otherHalfWidth || + this->x - thisHalfWidth <= other.x + otherHalfWidth + ) { + return true; + } + + // check y collision + if (this->y + thisHalfHeight >= other.y - otherHalfHeight || + this->y - thisHalfHeight <= other.y + otherHalfHeight + ) { + return true; + } + + return false; +} + +float* Sprite::getCenter() { + return this->center; +} + +void Sprite::draw() { + C2D_DrawSprite(&this->spr); +} + +float Sprite::getPosX() { + return this->x; +} + +float Sprite::getPosY() { + return this->y; +} + +C2D_Sprite* Sprite::getSpr() { + return &this->spr; +} + +void Sprite::setSpr(C2D_Sprite* newSpr) { + this->spr = *newSpr; +} + +void Sprite::copy(Sprite* other) { + memcpy(&other->x, &this->x, sizeof(this->x)); + memcpy(&other->y, &this->y, sizeof(this->y)); + memcpy(&other->width, &this->width, sizeof(this->width)); + memcpy(&other->height, &this->height, sizeof(this->height)); + memcpy(other->center, this->center, sizeof(this->center)); + memcpy(&other->spr, &this->spr, sizeof(this->spr)); +} \ No newline at end of file diff --git a/source/Bakery2D/Sprite/Sprite.hpp b/source/Bakery2D/Sprite/Sprite.hpp new file mode 100644 index 0000000..6222d1b --- /dev/null +++ b/source/Bakery2D/Sprite/Sprite.hpp @@ -0,0 +1,46 @@ +#pragma once + +#ifndef SPRITE_H +#define SPRITE_H + +#include + +class Sprite { +public: + Sprite(); + + Sprite(C2D_Sprite* spr, float x, float y); + + void setPosition(float x, float y); + + void move(float dx, float dy, bool overrideOOB=false); + + void setCenter(float x, float y); + + void setHitbox(float width, float height); + + bool isCollidingWith(Sprite &other); + + void draw(); + + float getPosX(); + + float getPosY(); + + float* getCenter(); + + C2D_Sprite* getSpr(); + + void setSpr(C2D_Sprite* newSpr); + + void copy(Sprite* other); + + C2D_Sprite spr; + +private: + float x, y; + float width, height; // for hitbox + float center[2]; +}; + +#endif \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 1759d8b..ad84111 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -12,58 +12,7 @@ #include "Sprite.hpp" #include "SpriteList.h" - -#define MAX_SPRITES 768 -#define SCREEN_WIDTH 400 -#define SCREEN_HEIGHT 240 -#define NUM_PIPES 5 - -// init spritesheet -static C2D_SpriteSheet spriteSheet; -Sprite sprites[MAX_SPRITES]; -Sprite pipes[NUM_PIPES]; - -static void initSprites() { - srand(time(NULL)); - size_t numImages = C2D_SpriteSheetCount(spriteSheet); - - for (size_t i = 0; i < numImages; i++) { - Sprite* thisSprite = &sprites[i]; - float x = SCREEN_WIDTH / 2; - float y = SCREEN_HEIGHT / 2; - - C2D_SpriteFromSheet(&thisSprite->spr, spriteSheet, i); - C2D_SpriteSetCenter(&thisSprite->spr, 0.5f, 0.5f); - thisSprite->setPosition(x, y); - } - - // set bird consts - sprites[SPR_BIRD].setPosition(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 3); - sprites[SPR_BIRD].setHitbox(17, 12); - - // set bottom screen scorecard - sprites[SPR_SCORECARD].setCenter(0.0, 0.0); - - sprites[SPR_SCORECARD].setPosition(2, 40); - - sprites[SPR_PIPETOP].setPosition(280, 3); - sprites[SPR_PIPEBOTTOM].setPosition(280, 280); - - // if theres a better way to do this,,, i dont know it - for (int i = 0; i < NUM_PIPES; i++) { - memcpy(&pipes[i], &sprites[SPR_BOTHPIPES], sizeof(sprites[SPR_BOTHPIPES])); - pipes[i].setCenter(0.5f, 0.5f); - pipes[i].setPosition(SCREEN_WIDTH + 40 + i * (100), (rand() % 150) + 50); - pipes[i].setHitbox(26, 403); - } -} - -void introScene(); -void gameScene(); -void gameOverScene(); - -// void (*currentScene)(C3D_RenderTarget*, C3D_RenderTarget*) = &gameScene; - +#include "scenes/MainLevel.cpp" int main(int argc, char* argv[]) { @@ -75,120 +24,20 @@ int main(int argc, char* argv[]) { C2D_Prepare(); // consoleInit(GFX_BOTTOM, NULL); - // Create screens + // Create screens and level C3D_RenderTarget* top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); C3D_RenderTarget* bottom = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); + Level* level; - // Load fonts and text - C2D_TextBuf g_staticBuf; - C2D_Text scoreText; - C2D_Font font; - - g_staticBuf = C2D_TextBufNew(4096); - font = C2D_FontLoad("romfs:/gfx/fbfont.bcfnt"); - C2D_TextFontParse(&scoreText, font, g_staticBuf, "0"); - C2D_TextOptimize(&scoreText); - - // Load graphics - spriteSheet = C2D_SpriteSheetLoad("romfs:/gfx/sprites.t3x"); - if (!spriteSheet) { - svcBreak(USERBREAK_PANIC); - } - - // Initialize sprites - initSprites(); - - // init gravity calc vars - float a = 0.4; // acceleration - float v = 0; // velocity - float pipeSpeed = -1; - - // set bg properties - sprites[SPR_BG].setPosition(200, 120); - C2D_SpriteSetScale(&sprites[SPR_BG].spr, 2.7778, 2.7907); // scale image to 400x240 (3ds screen res) - - size_t score = 0; - bool gameOver = false; + MainLevel* game = new MainLevel(top, bottom); + level = game; // Main loop while (aptMainLoop()) { - hidScanInput(); - - // Respond to user input - u32 kDown = hidKeysDown(); - if (kDown & KEY_START) - break; // break in order to return to hbmenu - - - // gravity calcs - v += a; - sprites[SPR_BIRD].move(0, v); - C2D_SpriteSetRotationDegrees(&sprites[SPR_BIRD].spr, v*9.8); - - if (!gameOver && (kDown & KEY_A)) { - v = -5.5; - sprites[SPR_BIRD].move(0, v, true); - } - - // bird hitting ground is game over - if (sprites[SPR_BIRD].getPosY() == SCREEN_HEIGHT) { - gameOver = true; - } - - if (!gameOver) { - for (int i = 0; i < NUM_PIPES; i++) { - pipes[i].move(pipeSpeed, 0, true); - - // collision detection - if (pipes[i].isCollidingWith(sprites[SPR_BIRD])) { - gameOver = true; - break; - } - - // inc score - if (pipes[i].getPosX() == sprites[SPR_BIRD].getPosX()) - score++; - - // send pipes back to front of screen - if (pipes[i].getPosX() < -(SCREEN_WIDTH/NUM_PIPES)) { - pipes[i].setPosition(SCREEN_WIDTH, (rand() % 150) + 50); - } - } - } - - - - // make score text - char scoreString[(((sizeof score) * CHAR_BIT) + 2)/3 + 2]; - sprintf(scoreString, "%d", score); - C2D_TextFontParse(&scoreText, font, g_staticBuf, scoreString); - C2D_TextOptimize(&scoreText); - - - // Render the scene - C3D_FrameBegin(C3D_FRAME_SYNCDRAW); - C2D_TargetClear(top, C3D_CLEAR_COLOR); - C2D_SceneBegin(top); - sprites[SPR_BG].draw(); - sprites[SPR_BIRD].draw(); - - for (int i = 0; i < NUM_PIPES; i++) { - pipes[i].draw(); - } - - C2D_TargetClear(bottom, C2D_Color32f(0.3294f, 0.7529f, 0.7882f, 1.0f)); - C2D_SceneBegin(bottom); - sprites[SPR_SCORECARD].draw(); - - C2D_DrawText(&scoreText, 0, 240, 88, 0.0f, 0.9f, 0.9f); - C3D_FrameEnd(0); - - + level->run(); } - // Delete graphics - C2D_SpriteSheetFree(spriteSheet); - C2D_TextBufDelete(g_staticBuf); - C2D_FontFree(font); + + level->cleanup(); // Deinit libs C2D_Fini(); diff --git a/source/scenes/MainLevel.cpp b/source/scenes/MainLevel.cpp new file mode 100644 index 0000000..6133626 --- /dev/null +++ b/source/scenes/MainLevel.cpp @@ -0,0 +1,140 @@ +#include "../Bakery2D/Level/Level.hpp" + +#define SPRITES_H + +#define SPR_BIRD 0 + +#define SPR_BG 1 +#define SPR_FLOOR 2 +#define SPR_LOGO 3 +#define SPR_PIPEBOTTOM 4 +#define SPR_PIPETOP 5 + +#define SPR_SCORECARD 6 +#define SPR_BOTHPIPES 7 + +#define NUM_PIPES 5 + +struct MainLevel: Level { + float a = 0.4; // acceleration + float v = 0; // velocity + float pipeSpeed = -1; + + Sprite pipes[NUM_PIPES]; + + int score = 0; + bool gameOver = false; + + C2D_TextBuf g_staticBuf; + C2D_Text scoreText; + C2D_Font font; + + MainLevel(C3D_RenderTarget* top, C3D_RenderTarget* bottom): Level(this, "romfs:/gfx/sprites.t3x", top, bottom) {} + + void setup() { + //* SPRITE INIT *// + // set bird consts + sprites[SPR_BIRD].setPosition(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 3); + sprites[SPR_BIRD].setHitbox(17, 12); + + // set bottom screen scorecard + sprites[SPR_SCORECARD].setCenter(0.0, 0.0); + + sprites[SPR_SCORECARD].setPosition(2, 40); + + sprites[SPR_PIPETOP].setPosition(280, 3); + sprites[SPR_PIPEBOTTOM].setPosition(280, 280); + + // set bg properties + sprites[SPR_BG].setPosition(200, 120); + C2D_SpriteSetScale(&sprites[SPR_BG].spr, 2.7778, 2.7907); // scale image to 400x240 (3ds screen res) + + // if theres a better way to do this,,, i dont know it + for (int i = 0; i < NUM_PIPES; i++) { + memcpy(&pipes[i], &sprites[SPR_BOTHPIPES], sizeof(sprites[SPR_BOTHPIPES])); + pipes[i].setCenter(0.5f, 0.5f); + pipes[i].setPosition(SCREEN_WIDTH + 40 + i * (100), (rand() % 150) + 50); + pipes[i].setHitbox(26, 403); + } + + //* FONT INIT *// + // Load fonts and text + g_staticBuf = C2D_TextBufNew(4096); + font = C2D_FontLoad("romfs:/gfx/fbfont.bcfnt"); + C2D_TextFontParse(&scoreText, font, g_staticBuf, "0"); + C2D_TextOptimize(&scoreText); + } + + void update() { + hidScanInput(); + + // Respond to user input + u32 kDown = hidKeysDown(); + if (kDown & KEY_START) + svcBreak(USERBREAK_USER); // break in order to return to hbmenu + + + // gravity calcs + v += a; + sprites[SPR_BIRD].move(0, v); + C2D_SpriteSetRotationDegrees(&sprites[SPR_BIRD].spr, v*9.8); + + if (!gameOver && (kDown & KEY_A)) { + v = -5.5; + sprites[SPR_BIRD].move(0, v, true); + } + + // bird hitting ground is game over + if (sprites[SPR_BIRD].getPosY() == SCREEN_HEIGHT) { + gameOver = true; + } + + if (!gameOver) { + for (int i = 0; i < NUM_PIPES; i++) { + pipes[i].move(pipeSpeed, 0, true); + + // collision detection + if (pipes[i].isCollidingWith(sprites[SPR_BIRD])) { + gameOver = true; + break; + } + + // inc score + if (pipes[i].getPosX() == sprites[SPR_BIRD].getPosX()) + score++; + + // send pipes back to front of screen + if (pipes[i].getPosX() < -(SCREEN_WIDTH/NUM_PIPES)) { + pipes[i].setPosition(SCREEN_WIDTH, (rand() % 150) + 50); + } + } + } + + // make score text + char scoreString[(((sizeof score) * CHAR_BIT) + 2)/3 + 2]; + sprintf(scoreString, "%d", score); + C2D_TextFontParse(&scoreText, font, g_staticBuf, scoreString); + C2D_TextOptimize(&scoreText); + } + + void drawTop() { + sprites[SPR_BG].draw(); + sprites[SPR_BIRD].draw(); + + for (int i = 0; i < NUM_PIPES; i++) { + pipes[i].draw(); + } + } + + void drawBottom() { + sprites[SPR_SCORECARD].draw(); + C2D_DrawText(&scoreText, 0, 240, 88, 0.0f, 0.9f, 0.9f); + } + + void cleanup() override { + C2D_SpriteSheetFree(spritesheet); + C2D_TextBufDelete(g_staticBuf); + C2D_FontFree(font); + } + +}; \ No newline at end of file