demo 2 empty arkanoid thing

This commit is contained in:
wgroeneveld 2018-08-08 14:43:34 +02:00
parent fad1953682
commit a9a59e1b7c
27 changed files with 3940 additions and 27 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.DS_Store
# Created by https://www.gitignore.io/api/clion,cmake

View File

@ -22,7 +22,9 @@ project(gba-sprite-engine-project VERSION 0.1 LANGUAGES CXX)
include(GNUInstallDirs)
add_subdirectory(engine)
add_subdirectory(demos/demo1-basicfeatures)
# this should be a part of the engine CMakeLists.txt file but cross-compiling and gtest doesn't work
add_subdirectory(test)
add_subdirectory(demos/demo1-basicfeatures)
add_subdirectory(demos/demo2-arkanoid)

View File

@ -10,7 +10,7 @@
#include <libgba-sprite-engine/scene.h>
#include <libgba-sprite-engine/background/background.h>
class FlyingStuffScene : public scene {
class FlyingStuffScene : public Scene {
private:
std::unique_ptr<Sprite> smiley;
std::unique_ptr<AffineSprite> player;
@ -22,7 +22,7 @@ private:
int rotation;
int rotationDiff = 128;
public:
FlyingStuffScene(std::shared_ptr<GBAEngine> engine) : scene(engine), rotation(0), rotationDiff(128), scrollX(0), scrollY(0) {}
FlyingStuffScene(std::shared_ptr<GBAEngine> engine) : Scene(engine), rotation(0), rotationDiff(128), scrollX(0), scrollY(0) {}
std::vector<Sprite *> sprites() override;
std::vector<Background *> backgrounds() override;

View File

@ -7,7 +7,7 @@
#include <libgba-sprite-engine/scene.h>
class SampleStartScene : public scene {
class SampleStartScene : public Scene {
private:
std::unique_ptr<Sprite> animation;
std::unique_ptr<Sprite> smiley;
@ -16,7 +16,7 @@ public:
std::vector<Sprite *> sprites() override;
std::vector<Background *> backgrounds() override;
SampleStartScene(std::shared_ptr<GBAEngine> engine) : scene(engine) {}
SampleStartScene(std::shared_ptr<GBAEngine> engine) : Scene(engine) {}
void load() override;
void tick(u16 keys) override;

View File

@ -0,0 +1,13 @@
project(arkanoid)
add_executable(${PROJECT_NAME}.elf
src/main.cpp
src/pats.h
src/arkanoid_game_scene.cpp
src/arkanoid_game_scene.h src/spritedata.h src/dead.h)
target_link_libraries(${PROJECT_NAME}.elf gba-sprite-engine)
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.gba
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

View File

@ -0,0 +1,99 @@
//
// Created by Wouter Groeneveld on 08/08/18.
//
#include <libgba-sprite-engine/sprites/sprite_builder.h>
#include <libgba-sprite-engine/background/text_stream.h>
#include "arkanoid_game_scene.h"
#include "spritedata.h"
#include "pats.h"
#include "dead.h"
std::vector<Sprite *> ArkanoidGameScene::sprites() {
return {
paddle.get(), ball.get()
};
}
std::vector<Background *> ArkanoidGameScene::backgrounds() {
return {};
}
void ArkanoidGameScene::youDied() {
if(highscore < ticks) highscore = ticks;
engine.get()->enqueueSound(raw_dead, raw_dead_bytes, 32000);
ball->setVelocity(0, 0);
TextStream::instance() << "You DIED - start to reset";
dead = true;
}
void ArkanoidGameScene::resetGame() {
dead = false;
ticks = 0;
TextStream::instance().clear();
ball->moveTo(110, 140);
ball->setVelocity(1, 1);
paddle->moveTo(100, 150);
}
void ArkanoidGameScene::tick(u16 keys) {
if(dead && (keys & KEY_START)) {
resetGame();
return;
}
if(dead) return;
TextStream::instance().setText(std::string("Ticks: ") + std::to_string(ticks), 5, 10);
TextStream::instance().setText(std::string("Highscore: ") + std::to_string(highscore), 7, 10);
if(ball->getX() <= 0 || ball->getX() >= (GBA_SCREEN_WIDTH - ball->getWidth())) {
ball->setVelocity(-ball->getDx(), ball->getDy());
}else if(ball->getY() <= 0) {
ball->setVelocity(ball->getDx(), -ball->getDy());
} else if(ball->getY() >= (GBA_SCREEN_HEIGHT - ball->getHeight())) {
youDied();
return;
} else if(ball->collidesWith(*paddle)) {
if(ticks > 1 && ticks % 5 == 0) {
ball->setVelocity(ball->getDx() + 1, ball->getDy() + 1);
}
// lousy implementation; ball could also hit paddle from right/left, meaning *BOOM*
ball->setVelocity(ball->getDx(), -ball->getDy());
engine.get()->enqueueSound(pats, sizeof(pats), 32000);
ticks++;
}
if(keys & KEY_LEFT) {
paddle->setVelocity(-2, 0);
} else if(keys & KEY_RIGHT) {
paddle->setVelocity(+2, 0);
} else {
paddle->setVelocity(0, 0);
}
}
void ArkanoidGameScene::load() {
foregroundPalette = std::unique_ptr<ForegroundPaletteManager>(new ForegroundPaletteManager(paletteSharedPal, sizeof(paletteSharedPal)));
backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager());
SpriteBuilder<Sprite> builder;
ball = builder
.withSize(SIZE_8_8)
.withLocation(110, 140)
.withData(ballTiles, sizeof(ballTiles))
.withVelocity(1, 1)
.buildPtr();
paddle = builder
.withSize(SIZE_32_8)
.withLocation(100, 150)
.withData(paddleTiles, sizeof(paddleTiles))
.withinBounds()
.buildPtr();
}

View File

@ -0,0 +1,35 @@
//
// Created by Wouter Groeneveld on 08/08/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_GAME_H
#define GBA_SPRITE_ENGINE_PROJECT_GAME_H
#include <libgba-sprite-engine/gba_engine.h>
#include <libgba-sprite-engine/scene.h>
#include <vector>
class ArkanoidGameScene : public Scene {
private:
bool dead;
int ticks, highscore;
std::unique_ptr<Sprite> paddle;
std::unique_ptr<Sprite> ball;
void youDied();
void resetGame();
public:
std::vector<Sprite *> sprites() override;
std::vector<Background *> backgrounds() override;
ArkanoidGameScene(std::shared_ptr<GBAEngine> engine) : Scene(engine), ticks(0), highscore(0) {}
void load() override;
void tick(u16 keys) override;
};
#endif //GBA_SPRITE_ENGINE_PROJECT_GAME_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
//
// Created by Wouter Groeneveld on 08/08/18.
//
#include <libgba-sprite-engine/scene.h>
#include <libgba-sprite-engine/gba_engine.h>
#include "arkanoid_game_scene.h"
int main() {
std::shared_ptr<GBAEngine> engine(new GBAEngine());
ArkanoidGameScene* scene = new ArkanoidGameScene(engine);
engine->setScene(scene);
while (true) {
engine->update();
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,134 @@
//
// Created by Wouter Groeneveld on 08/08/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_SPRITEDATA_H
#define GBA_SPRITE_ENGINE_PROJECT_SPRITEDATA_H
//{{BLOCK(block_green)
//======================================================================
//
// block_green, 16x8@8,
// + 2 tiles not compressed
// Total size: 128 = 128
//
// Time-stamp: 2018-08-08, 13:34:09
// Exported by Cearn's GBA Image Transmogrifier, v0.8.6
// ( http://www.coranac.com/projects/#grit )
//
//======================================================================
const unsigned int block_greenTiles[32] __attribute__((aligned(4)))=
{
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01010101,0x01010101,
0x00000000,0x01000000,0x00000000,0x01000000,0x00000000,0x01000000,0x00000000,0x01000000,
0x00000000,0x01000000,0x00000000,0x01000000,0x00000000,0x01000000,0x01010101,0x01010101,
};
//}}BLOCK(block_green)
//{{BLOCK(block_red)
//======================================================================
//
// block_red, 16x8@8,
// + 2 tiles not compressed
// Total size: 128 = 128
//
// Time-stamp: 2018-08-08, 13:34:09
// Exported by Cearn's GBA Image Transmogrifier, v0.8.6
// ( http://www.coranac.com/projects/#grit )
//
//======================================================================
const unsigned int block_redTiles[32] __attribute__((aligned(4)))=
{
0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x01010101,0x01010101,
0x02020202,0x01020202,0x02020202,0x01020202,0x02020202,0x01020202,0x02020202,0x01020202,
0x02020202,0x01020202,0x02020202,0x01020202,0x02020202,0x01020202,0x01010101,0x01010101,
};
//}}BLOCK(block_red)
//{{BLOCK(ball)
//======================================================================
//
// ball, 8x8@8,
// + 1 tiles not compressed
// Total size: 64 = 64
//
// Time-stamp: 2018-08-08, 13:34:09
// Exported by Cearn's GBA Image Transmogrifier, v0.8.6
// ( http://www.coranac.com/projects/#grit )
//
//======================================================================
const unsigned int ballTiles[16] __attribute__((aligned(4)))=
{
0x05050503,0x03030303,0x04040405,0x03030305,0x04040405,0x03030305,0x05050503,0x03030303,
0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,
};
//}}BLOCK(ball)
//{{BLOCK(paddle)
//======================================================================
//
// paddle, 48x8@8,
// + 6 tiles not compressed
// Total size: 384 = 384
//
// Time-stamp: 2018-08-08, 13:34:09
// Exported by Cearn's GBA Image Transmogrifier, v0.8.6
// ( http://www.coranac.com/projects/#grit )
//
//======================================================================
const unsigned int paddleTiles[96] __attribute__((aligned(4)))=
{
0x02020303,0x03020202,0x02020503,0x05020202,0x02020503,0x05020202,0x02020502,0x05020202,
0x02020502,0x05020202,0x02020503,0x05020202,0x02020203,0x05020202,0x02020303,0x03020202,
0x06060606,0x06060606,0x05050505,0x05050505,0x06060606,0x06060606,0x06060606,0x06060606,
0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x03030303,0x03030303,
0x06060606,0x06060606,0x05050505,0x05050505,0x06060606,0x06060606,0x06060606,0x06060606,
0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x03030303,0x03030303,
0x06060606,0x06060606,0x05050505,0x05050505,0x06060606,0x06060606,0x06060606,0x06060606,
0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x03030303,0x03030303,
0x06060606,0x06060606,0x05050505,0x05050505,0x06060606,0x06060606,0x06060606,0x06060606,
0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x06060606,0x03030303,0x03030303,
0x02020203,0x03030202,0x02020205,0x03050202,0x02020205,0x03050202,0x02020205,0x02050202,
0x02020205,0x02050202,0x02020205,0x03050202,0x02020205,0x03020202,0x02020203,0x03030202,
};
//}}BLOCK(paddle)
//{{BLOCK(paletteShared)
//======================================================================
//
// paletteShared, 16x16@8,
// + palette 7 entries, not compressed
// Total size: 14 = 14
//
// Time-stamp: 2018-08-08, 13:34:09
// Exported by Cearn's GBA Image Transmogrifier, v0.8.6
// ( http://www.coranac.com/projects/#grit )
//
//======================================================================
const unsigned short paletteSharedPal[8] __attribute__((aligned(4)))=
{
0x0B50,0x0000,0x00BB,0x7FFF,0x1E7F,0x7FFF,0x39CE,
};
//}}BLOCK(paletteShared)
#endif //GBA_SPRITE_ENGINE_PROJECT_SPRITEDATA_H

View File

@ -33,6 +33,7 @@ private:
public:
void clear();
void setText(std::string text, int row, int col);
void setText(const char* text, int row, int col);
void setTextColor(COLOR color);
static TextStream& instance();

View File

@ -10,9 +10,9 @@
class SceneEffect {
protected:
// WHY no reference? Scene& operator= is implicitly deleted and no intentions to use that
scene* sceneToAffect;
Scene* sceneToAffect;
public:
void setSceneToAffect(scene* scene) { sceneToAffect = scene; };
void setSceneToAffect(Scene* scene) { sceneToAffect = scene; };
virtual void update() = 0;
virtual bool isDone() = 0;

View File

@ -13,11 +13,14 @@
#include "scene.h"
#include "sound_control.h"
#define GBA_SCREEN_WIDTH 240
#define GBA_SCREEN_HEIGHT 160
class GBAEngine {
private:
// WHY raw pointers? the engine does the transition and cleanup work itself
scene* currentScene;
scene* sceneToTransitionTo;
Scene* currentScene;
Scene* sceneToTransitionTo;
SceneEffect* currentEffectForTransition;
SpriteManager spriteManager;
@ -36,16 +39,16 @@ private:
public:
GBAEngine();
void setScene(scene* scene);
void transitionIntoScene(scene* scene, SceneEffect* effect);
void setScene(Scene* scene);
void transitionIntoScene(Scene* scene, SceneEffect* effect);
bool isTransitioning() { return currentEffectForTransition != nullptr; }
void dequeueAllSounds();
void enqueueMusic(const s8 *data, int totalSamples) {
enqueueSound(data, totalSamples, 16000, ChannelA);
void enqueueMusic(const s8 *data, int totalSamples, int sampleRate = 16000) {
enqueueSound(data, totalSamples, sampleRate, ChannelA);
}
void enqueueSound(const s8 *data, int totalSamples) {
enqueueSound(data, totalSamples, 16000, ChannelB);
void enqueueSound(const s8 *data, int totalSamples, int sampleRate = 16000) {
enqueueSound(data, totalSamples, sampleRate, ChannelB);
}
u16 readKeys();

View File

@ -71,6 +71,7 @@ protected:
}
public:
ForegroundPaletteManager() : PaletteManager() {}
ForegroundPaletteManager(const COLOR paletteData[]) : PaletteManager(paletteData) {}
ForegroundPaletteManager(const COLOR paletteData[], int size) : PaletteManager(paletteData, size) {}
};

View File

@ -13,7 +13,7 @@
class GBAEngine;
class scene {
class Scene {
protected:
std::unique_ptr<ForegroundPaletteManager> foregroundPalette;
std::unique_ptr<BackgroundPaletteManager> backgroundPalette;
@ -30,8 +30,8 @@ public:
virtual void load() = 0;
virtual void tick(u16 i) = 0;
scene(std::shared_ptr<GBAEngine> engine) : engine(engine) { }
virtual ~scene() {
Scene(std::shared_ptr<GBAEngine> engine) : engine(engine) { }
virtual ~Scene() {
// scenes should manage their own resources - use std::unique_ptr
}

View File

@ -49,6 +49,7 @@ protected:
const void *data;
int x, y, priority, dx, dy;
int w, h, size_bits, shape_bits;
bool stayWithinBounds;
int imageSize, tileIndex;
int animationDelay, amountOfFrames, currentFrame, animationCounter;
bool animating;
@ -70,6 +71,7 @@ public:
}
void animate() { this->animating = true; }
void stopAnimating() { this->animating = false; }
void setStayWithinBounds(bool b) { stayWithinBounds = b; }
void setVelocity(int dx, int dy) {
this->dx = dx;
this->dy = dy;
@ -77,11 +79,13 @@ public:
void update();
void moveTo(int x, int y);
bool collidesWith(const Sprite &other);
bool collidesWith(Sprite &s2);
void flipVertically(bool flip);
void flipHorizontally(bool flip);
int getDx() { return dx; }
int getDy() { return dy; }
int getX() { return x; }
int getHeight() { return h; }
int getWidth() { return w; }

View File

@ -10,6 +10,7 @@
template<typename T> class SpriteBuilder {
private:
int imageSize;
bool stayWithinBounds;
const void *imageData;
int x, y, dx, dy;
int numberOfFrames, animationDelay;
@ -24,7 +25,10 @@ public:
SpriteBuilder() {
reset();
}
SpriteBuilder& withinBounds() {
stayWithinBounds = true;
return *this;
}
SpriteBuilder& withData(const void* imageData, int imageSize) {
this->imageData = imageData;
this->imageSize = imageSize;
@ -60,6 +64,7 @@ template<typename T> std::unique_ptr<T> SpriteBuilder<T>::buildPtr() {
if(this->numberOfFrames > 0) {
s->makeAnimated(this->numberOfFrames, this->animationDelay);
}
s->setStayWithinBounds(stayWithinBounds);
reset();
return std::unique_ptr<T>(s);
@ -71,6 +76,7 @@ template<typename T> T SpriteBuilder<T>::build() {
if(this->numberOfFrames > 0) {
s.makeAnimated(this->numberOfFrames, this->animationDelay);
}
s.setStayWithinBounds(stayWithinBounds);
reset();
return s;

View File

@ -51,6 +51,10 @@ TextStream& TextStream::instance() {
return *inst;
}
void TextStream::setText(std::string text, int row, int col) {
setText(text.c_str(), row, col);
}
// thank you Ian
// http://cs.umw.edu/~finlayson/class/spring18/cpsc305/
void TextStream::setText(const char* text, int row, int col) {

View File

@ -61,7 +61,6 @@ void GBAEngine::dequeueAllSounds() {
void GBAEngine::enqueueSound(const s8 *data, int totalSamples, int sampleRate, SoundChannel channel) {
SoundControl* control;
stopOnVBlank();
if(channel == ChannelA) { // repeating bg music can be restarted
GBAEngine::activeChannelA = SoundControl::channelAControl();
@ -69,11 +68,13 @@ void GBAEngine::enqueueSound(const s8 *data, int totalSamples, int sampleRate, S
} else { // sound still playing, don't stop that
if(GBAEngine::activeChannelB) {
if(!GBAEngine::activeChannelB->done()) return;
GBAEngine::activeChannelB = nullptr;
}
GBAEngine::activeChannelB = SoundControl::channelBControl();
control = GBAEngine::activeChannelB.get();
}
stopOnVBlank();
REG_TM0CNT = 0;
control->disable();
@ -119,7 +120,7 @@ void GBAEngine::update() {
spriteManager.render();
}
void GBAEngine::transitionIntoScene(scene *scene, SceneEffect* effect) {
void GBAEngine::transitionIntoScene(Scene* scene, SceneEffect* effect) {
sceneToTransitionTo = scene;
currentEffectForTransition = effect;
currentEffectForTransition->setSceneToAffect(this->currentScene);
@ -131,7 +132,7 @@ void GBAEngine::cleanupPreviousScene() {
delete currentEffectForTransition;
}
void GBAEngine::setScene(scene* scene) {
void GBAEngine::setScene(Scene* scene) {
dequeueAllSounds();
if(this->currentScene) {
cleanupPreviousScene();

View File

@ -6,6 +6,7 @@
#include <libgba-sprite-engine/gba/tonc_memdef.h>
#include <libgba-sprite-engine/background/text_stream.h>
#include <libgba-sprite-engine/sprites/sprite.h>
#include <libgba-sprite-engine/gba_engine.h>
Sprite::Sprite(const void *imageData, int imageSize, int x, int y, SpriteSize size)
: x(x), y(y), data(imageData), imageSize(imageSize),
@ -56,6 +57,13 @@ void Sprite::syncOam() {
void Sprite::updateVelocity() {
this->x += this->dx;
this->y += this->dy;
if(stayWithinBounds) {
if(this->x < 0) this->x = 0;
if(this->y < 0) this->y = 0;
if(this->x > (GBA_SCREEN_WIDTH - this->w)) this->x = GBA_SCREEN_WIDTH - this->w;
if(this->y > (GBA_SCREEN_HEIGHT - this->h)) this->y = GBA_SCREEN_HEIGHT - this->h;
}
}
void Sprite::updateAnimation() {
@ -95,10 +103,13 @@ void Sprite::setAttributesBasedOnSize(SpriteSize size) {
}
}
bool Sprite::collidesWith(const Sprite &o) {
const Sprite &s = *this;
if((abs(s.x - o.x) < (s.w + o.w) / 2)
&& abs(s.y - o.y) < (s.h + o.h) / 2) {
bool Sprite::collidesWith(Sprite &s2) {
const Sprite &s1 = *this;
if(s1.x < s2.x + s2.w &&
s1.x + s1.w > s2.x &&
s1.y < s2.y + s2.h &&
s1.h + s1.y > s2.y) {
return true;
}
return false;