From b46b5a907d0be7ee5b32341c3d2967b71a3185f6 Mon Sep 17 00:00:00 2001 From: wgroeneveld Date: Thu, 9 Aug 2018 11:35:52 +0200 Subject: [PATCH] dynamically adding sprites to current scene tryout --- CMakeLists.txt | 3 +- demos/demo2-arkanoid/src/main.cpp | 2 +- demos/demo3-foodthrowing/CMakeLists.txt | 13 + demos/demo3-foodthrowing/avatar.png | Bin 0 -> 2811 bytes demos/demo3-foodthrowing/src/avatar.h | 724 ++++++++++++++++++ demos/demo3-foodthrowing/src/bullet.h | 28 + demos/demo3-foodthrowing/src/food_scene.cpp | 91 +++ demos/demo3-foodthrowing/src/food_scene.h | 36 + demos/demo3-foodthrowing/src/main.cpp | 19 + engine/CMakeLists.txt | 2 +- .../include/libgba-sprite-engine/gba_engine.h | 2 + engine/include/libgba-sprite-engine/scene.h | 2 + .../libgba-sprite-engine/sprites/sprite.h | 4 +- .../sprites/sprite_manager.h | 3 +- engine/src/gba_engine.cpp | 11 +- engine/src/scene.cpp | 10 + engine/src/sprites/affine_sprite.cpp | 8 +- engine/src/sprites/sprite.cpp | 13 +- engine/src/sprites/sprite_manager.cpp | 31 +- 19 files changed, 975 insertions(+), 27 deletions(-) create mode 100644 demos/demo3-foodthrowing/CMakeLists.txt create mode 100644 demos/demo3-foodthrowing/avatar.png create mode 100644 demos/demo3-foodthrowing/src/avatar.h create mode 100644 demos/demo3-foodthrowing/src/bullet.h create mode 100644 demos/demo3-foodthrowing/src/food_scene.cpp create mode 100644 demos/demo3-foodthrowing/src/food_scene.h create mode 100644 demos/demo3-foodthrowing/src/main.cpp create mode 100644 engine/src/scene.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f5695fa..5d24261 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,4 +27,5 @@ add_subdirectory(engine) add_subdirectory(test) add_subdirectory(demos/demo1-basicfeatures) -add_subdirectory(demos/demo2-arkanoid) \ No newline at end of file +add_subdirectory(demos/demo2-arkanoid) +add_subdirectory(demos/demo3-foodthrowing) \ No newline at end of file diff --git a/demos/demo2-arkanoid/src/main.cpp b/demos/demo2-arkanoid/src/main.cpp index 58a9ec3..2c64793 100644 --- a/demos/demo2-arkanoid/src/main.cpp +++ b/demos/demo2-arkanoid/src/main.cpp @@ -10,7 +10,7 @@ int main() { std::shared_ptr engine(new GBAEngine()); - ArkanoidGameScene* scene = new ArkanoidGameScene(engine); + auto scene = new ArkanoidGameScene(engine); engine->setScene(scene); while (true) { diff --git a/demos/demo3-foodthrowing/CMakeLists.txt b/demos/demo3-foodthrowing/CMakeLists.txt new file mode 100644 index 0000000..33d244f --- /dev/null +++ b/demos/demo3-foodthrowing/CMakeLists.txt @@ -0,0 +1,13 @@ +project(foodthrowing) + +add_executable(${PROJECT_NAME}.elf + src/main.cpp + src/food_scene.cpp + src/food_scene.h + src/avatar.h src/bullet.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 + ) diff --git a/demos/demo3-foodthrowing/avatar.png b/demos/demo3-foodthrowing/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..0fce9e8880ec4eadf98b187d63090d6d79530384 GIT binary patch literal 2811 zcmVWFU8GbZ8()Nlj2>E@cM*01978L_t(|+U;Fkh#S`x zK3fFQgP?*6IuJb!cpw4`B8b5Iuuv5TybzZzG{rG4*bglfnunAYoYF!mq_lo0eTl&j z4m4n}OV{AiHEduDGN`Zy1+*Z83KLL30X?Xof}j%+s1N6S<2|P{yV}u@sdLZEpw-+v zgU)xp^KH)cdcRj;TtRW0%7q9CDMEYNXe6!Xal8Ap-Q@K~v zKb|b~12_a^9SB>rIY1!UU^UX3yHT&q7h%dg$Qdj`BAX*b^Th=Y0jtUdfCOlMDjKp8 zA~c%Mo%~2>7#jCXg(=P|WnMf#B8PzWA|OqIAP}2m4dsUH4GK|CWq~$gE13#Xi~!FQ zufU)C{eB`6&l&+b1S|^yU~Oos==sU_$+akm$eY?A5QBLjdxsqrQ9gX~IS~-BFE z5z(e!a&f>)>j8iJ*`*l~(Y{1-h`dk}JnZW%uT;#d2ym;yoZwe#L`2kV?z848@8x8?cY`NJn04gt&Lfa#Qf`7O!wp^2W< z?~$+HZS~ak$^IAL|C4f%5{ZK>&VXi0|uc3^wR* z{eW%QXWMJH+eAck`}Xa`O45T)XuN=q@^y>iz=dw zkty%hljqoqlS4pO1Ryr=8Jh~clDZDZ{QW_@qcqfzS|74S*k+|tN#vS_VRy`fd4Z5> zSx1a^WvOf`R1N`I5nyY7_>9=SSY*xCM5ALtfgSt3LkwXKrn9CAmOct&Jp~H(P#U&6nAhd+nyM zG;0vGyhDudaMm{!gkWz`jFfsq=K_KNXdV&dIC8nbY7qdNy7}Gnv*QsUR&rqbs0a;1 z_S@|~vv|BRgqq)^lZdP^q=)wi&uIG&0a-Z!`FLFS7X*OqqcLl! zCpKM`ND-BxzG%?K@|zMD_`)Gzf6RmVag900tUovoSTzFTU0P-p7aD`0Qj|CKeZRV? zyaNPmAB}Xp0DY%Le(^qmsaqLZZ6s*rcURt+aW<&;`VGcKCt|g|4XB{2n4gt&1yzL;jnztR~$n4Vkj$;m3 zLkGFb1{WEe);RYmXQn0+d&SEVx^b{$-73-3lK`y7-xGC z+j?ER%Q8C19RgM{X<7%l-RE2P%ZdvQI$d=iyuoGZ7g|LJxkJD*CYiT`JTID^7`Bf0 z{+U?*hwr}6-*=m8UJsq-cke?wjyTl)cWS;uNU%g61M1Q<~ zLEUHHYaZ|55HJ@4Ob5ATZRGvXe7AXzh=^|8x$pCmImLkGD-z&tmlb&$i)VlUusdzT|N=?1;VBu|x) z|Nfu9{E|6g??BCqNDwu|d++jT6uqAG4_~{cp1XhdSE{~%dEhnddpiUyih$EPv*FJn zyB+q+=rbl_q@ZIQ-5<|Sk1DFaKkqDO1XQOzg}}FKL1G+{rGwn%0%y4y9pqU1!?hxv z-nPaEO^%2C#C$%jgB+dXkhp_PvO)AF+TKE}JmES|7pdcbMF!)SMkIzx4^D#^3X9`} zn3(lX=^&50OK=W}tSzDr(CYS79MKtM?;COm$TA4OJR+_3WT^6T2sDqXE>4e{w}TuJ z9_D#RvPB@R*&8I*1snoag#fT>tz4QB9Y5_MxyA>CAY$B+$gS1uC)zH&$_=R(pkjmw z&#oMyhKTb_P)j_^83GOgSq6>cl^)scH5n)RZM%=a`uDAhpEy760|E->?4tNNQ@!JK z-t)s80v1KUZyp|PNJVC*HLp)}!CE2K1ov8;BeeS`caqm74!{~AYLDZTRba#mcrS;5 zbwz?!6JX)se=16HCSa4V)he@mTKDLFlWT?_yqWxZ_WY5-$n6k$F$l0H#>eM+;ba+e z(#+A(k-DBH!+Jr@WBUp6=Kq&D1gskYxBmKUgNW$Y-+O&_Tr(rHXW07Z>#w}R5!;tv zp7{5Xi8s=+KD>uRz`7A&BQon9u&WEP`ZcNU*5n*<-<~}r6{V@H0F{eocbM@yQO2`_ z90HEXQu_vR(j3?sySc1)XitL^fK*hh$SN8`wHz}4O`43*! +#include +#include +#include +#include "food_scene.h" +#include "avatar.h" +#include "bullet.h" + +#define AVATAR_ROTATION_DIFF 300 +#define MAX_AMOUNT_OF_BULLETS 40 + +FoodScene::FoodScene(const std::shared_ptr &engine) : Scene(engine) {} + +std::vector FoodScene::backgrounds() { + return {}; +} + +std::vector FoodScene::sprites() { + std::vector sprites = { + avatar.get() // order is important for affine transformations because of updateSpritesInScene() + }; + for(auto& b : bullets) { + if(!b->isOffScreen()) { + sprites.push_back(b.get()); + } + } + + return sprites; +} + +void FoodScene::removeBulletsOffScreen() { + // TODO crashes after this + bullets.erase( + std::remove_if(bullets.begin(), bullets.end(), [](std::unique_ptr &s) { return s->isOffScreen(); }), + bullets.end()); +} + +void FoodScene::tick(u16 keys) { + avatar->animateToFrame(0); + + //removeBulletsOffScreen(); + TextStream::instance().setText(std::to_string(bullets.size()) + std::string(" bullets"), 1, 1); + + if(keys & KEY_LEFT) { + avatarRotation -= AVATAR_ROTATION_DIFF; + } else if(keys & KEY_RIGHT) { + avatarRotation += AVATAR_ROTATION_DIFF; + } + if(keys & KEY_A) { + if(bullets.size() < MAX_AMOUNT_OF_BULLETS) { + avatar->animateToFrame(1); + bullets.push_back(createBullet()); + + engine->updateSpritesInScene(); + //addSprite(bullets.at(bullets.size() - 1).get()); + } + } + + avatar->rotate(avatarRotation); + } + + std::unique_ptr FoodScene::createBullet() { + // TODO shared bullet data tiles + return this->spriteBuilder->withData(bullet_data, sizeof(bullet_data)) + .withSize(SIZE_16_16) + .withVelocity(1, 1) + .withLocation(avatar->getX(), avatar->getY()) + .buildPtr(); +} + +void FoodScene::load() { + foregroundPalette = std::unique_ptr(new ForegroundPaletteManager(avatar_palette, sizeof(avatar_palette))); + backgroundPalette = std::unique_ptr(new BackgroundPaletteManager()); + + spriteBuilder = std::unique_ptr>(new SpriteBuilder); + SpriteBuilder affineBuilder; + + // Doom avatar copyright iD Software + avatar = affineBuilder.withData(avatar_data, sizeof(avatar_data)) + .withSize(SIZE_64_64) + .withAnimated(2, 3) + .withLocation(GBA_SCREEN_WIDTH / 2 - 32, GBA_SCREEN_HEIGHT / 2 - 32) + .buildPtr(); + avatar->stopAnimating(); + avatarRotation = 0; + +} \ No newline at end of file diff --git a/demos/demo3-foodthrowing/src/food_scene.h b/demos/demo3-foodthrowing/src/food_scene.h new file mode 100644 index 0000000..dd70955 --- /dev/null +++ b/demos/demo3-foodthrowing/src/food_scene.h @@ -0,0 +1,36 @@ +// +// Created by Wouter Groeneveld on 09/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_PROJECT_FOOD_SCENE_H +#define GBA_SPRITE_ENGINE_PROJECT_FOOD_SCENE_H + + +#include +#include +#include + +class FoodScene : public Scene { +private: + std::unique_ptr avatar; + std::vector> bullets; + int avatarRotation; + + std::unique_ptr> spriteBuilder; + std::unique_ptr createBullet(); + void removeBulletsOffScreen(); + +public: + explicit FoodScene(const std::shared_ptr &engine); + FoodScene(FoodScene& other) = delete; + + std::vector sprites() override; + std::vector backgrounds() override; + + void load() override; + void tick(u16 keys) override; + +}; + + +#endif //GBA_SPRITE_ENGINE_PROJECT_FOOD_SCENE_H diff --git a/demos/demo3-foodthrowing/src/main.cpp b/demos/demo3-foodthrowing/src/main.cpp new file mode 100644 index 0000000..86d9e45 --- /dev/null +++ b/demos/demo3-foodthrowing/src/main.cpp @@ -0,0 +1,19 @@ +// +// Created by Wouter Groeneveld on 09/08/18. +// + +#include +#include "food_scene.h" + +int main() { + std::shared_ptr engine(new GBAEngine()); + + auto scene = new FoodScene(engine); + engine->setScene(scene); + + while (true) { + engine->update(); + } + + return 0; +} \ No newline at end of file diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 4b6e152..b1877a3 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(${PROJECT_NAME} src/background/text_stream.cpp src/background/background.cpp src/effects/fade_out_scene.cpp - src/sound_control.cpp) + src/sound_control.cpp src/scene.cpp) target_include_directories(${PROJECT_NAME} PUBLIC $ diff --git a/engine/include/libgba-sprite-engine/gba_engine.h b/engine/include/libgba-sprite-engine/gba_engine.h index b70335f..838a61f 100644 --- a/engine/include/libgba-sprite-engine/gba_engine.h +++ b/engine/include/libgba-sprite-engine/gba_engine.h @@ -40,6 +40,7 @@ public: GBAEngine(); void setScene(Scene* scene); + void dynamicallyAddSprite(Sprite* s) { spriteManager.add(s); } void transitionIntoScene(Scene* scene, SceneEffect* effect); bool isTransitioning() { return currentEffectForTransition != nullptr; } @@ -53,6 +54,7 @@ public: u16 readKeys(); void update(); + void updateSpritesInScene(); void delay(int times) { for(int i = 0; i < times; i++){} } diff --git a/engine/include/libgba-sprite-engine/scene.h b/engine/include/libgba-sprite-engine/scene.h index 1b90fbf..148e54a 100644 --- a/engine/include/libgba-sprite-engine/scene.h +++ b/engine/include/libgba-sprite-engine/scene.h @@ -19,6 +19,8 @@ protected: std::unique_ptr backgroundPalette; std::shared_ptr engine; + void addSprite(Sprite* sprite); + public: ForegroundPaletteManager* getForegroundPalette() { return foregroundPalette.get(); } BackgroundPaletteManager* getBackgroundPalette() { return backgroundPalette.get(); } diff --git a/engine/include/libgba-sprite-engine/sprites/sprite.h b/engine/include/libgba-sprite-engine/sprites/sprite.h index beef000..fefc7fc 100644 --- a/engine/include/libgba-sprite-engine/sprites/sprite.h +++ b/engine/include/libgba-sprite-engine/sprites/sprite.h @@ -61,7 +61,7 @@ protected: void setAttributesBasedOnSize(SpriteSize size); public: - Sprite(const void *imageData, int imageSize, int x, int y, SpriteSize size); + explicit Sprite(const void *imageData, int imageSize, int x, int y, SpriteSize size); virtual ~Sprite() {} void makeAnimated(int amountOfFrames, int animationDelay) { @@ -70,6 +70,7 @@ public: animate(); } void animate() { this->animating = true; } + void animateToFrame(int frame) { this->currentFrame = frame; } void stopAnimating() { this->animating = false; } void setStayWithinBounds(bool b) { stayWithinBounds = b; } void setVelocity(int dx, int dy) { @@ -91,6 +92,7 @@ public: int getWidth() { return w; } int getY() { return y; } int getCurrentFrame() { return currentFrame; } + bool isOffScreen(); friend class SpriteManager; }; diff --git a/engine/include/libgba-sprite-engine/sprites/sprite_manager.h b/engine/include/libgba-sprite-engine/sprites/sprite_manager.h index 6689984..880638a 100644 --- a/engine/include/libgba-sprite-engine/sprites/sprite_manager.h +++ b/engine/include/libgba-sprite-engine/sprites/sprite_manager.h @@ -15,12 +15,13 @@ private: std::vector sprites; void copyOverSpriteOAMToVRAM(); + void copyOverImageDataToVRAM(Sprite* s); void copyOverImageDataToVRAM(); public: int getSpriteSize() { return sprites.size(); } - void add(const Sprite& sprite); + void add(Sprite* sprite); void set(std::vector sprites); void persist(); // copies over image and palette data to VRAM, modifies sprite OAM indiches void render(); // copies over OAM buffer to OAM RAM, called in game loop diff --git a/engine/src/gba_engine.cpp b/engine/src/gba_engine.cpp index e0c04fd..ad6755e 100644 --- a/engine/src/gba_engine.cpp +++ b/engine/src/gba_engine.cpp @@ -151,15 +151,18 @@ void GBAEngine::setScene(Scene* scene) { } bgPalette->persist(); - Allocator::free(); TextStream::instance().persist(); - spriteManager.set(scene->sprites()); - spriteManager.persist(); - for(const auto bg : scene->backgrounds()) { bg->persist(); } this->currentScene = scene; + updateSpritesInScene(); } + +void GBAEngine::updateSpritesInScene() { + Allocator::free(); + spriteManager.set(currentScene->sprites()); + spriteManager.persist(); +} \ No newline at end of file diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp new file mode 100644 index 0000000..697ec34 --- /dev/null +++ b/engine/src/scene.cpp @@ -0,0 +1,10 @@ +// +// Created by Wouter Groeneveld on 09/08/18. +// + +#include +#include + +void Scene::addSprite(Sprite *sprite) { + engine->dynamicallyAddSprite(sprite); +} diff --git a/engine/src/sprites/affine_sprite.cpp b/engine/src/sprites/affine_sprite.cpp index efcfafc..9a52f25 100644 --- a/engine/src/sprites/affine_sprite.cpp +++ b/engine/src/sprites/affine_sprite.cpp @@ -6,6 +6,7 @@ #include #include +#include void AffineSprite::identity() { obj_aff_identity(this->affine.get()); @@ -46,6 +47,9 @@ void AffineSprite::buildOam(int tileIndex) { (this->shape_bits << 14); rebuildOamAttr1ForAffineIndex(); - this->affine = std::unique_ptr(new OBJ_AFFINE()); - identity(); + if(!affine) { + TextStream::instance() << "building"; + this->affine = std::unique_ptr(new OBJ_AFFINE()); + identity(); + } } diff --git a/engine/src/sprites/sprite.cpp b/engine/src/sprites/sprite.cpp index 56898af..d7f924e 100644 --- a/engine/src/sprites/sprite.cpp +++ b/engine/src/sprites/sprite.cpp @@ -20,6 +20,10 @@ void Sprite::moveTo(int x, int y) { syncOam(); } +bool Sprite::isOffScreen() { + return x < 0 || x > GBA_SCREEN_WIDTH || y < 0 || y > GBA_SCREEN_HEIGHT; +} + void Sprite::flipHorizontally(bool flip) { if(flip) { oam->attr1 |= ATTR1_HFLIP; @@ -42,8 +46,8 @@ void Sprite::syncVelocity() { } void Sprite::syncAnimation() { - if(!animating) return; - int newTileIndex = this->tileIndex + (currentFrame * w); + int offset = w == 64 ? 2 : 1; // 64xY sprites don't seem to cut currFrame * w + int newTileIndex = this->tileIndex + (currentFrame * w * offset); oam->attr2 &= OAM_TILE_OFFSET_CLEAR; oam->attr2 |= (newTileIndex & OAM_TILE_OFFSET_NEW); @@ -118,7 +122,10 @@ bool Sprite::collidesWith(Sprite &s2) { void Sprite::buildOam(int tileIndex) { this->tileIndex = tileIndex; - this->oam = std::unique_ptr(new OBJ_ATTR()); + + if(!oam) { + this->oam = std::unique_ptr(new OBJ_ATTR()); + } this->oam->attr0 = ATTR0_Y(this->y) | ATTR0_MODE(0) | diff --git a/engine/src/sprites/sprite_manager.cpp b/engine/src/sprites/sprite_manager.cpp index d54521c..3449511 100644 --- a/engine/src/sprites/sprite_manager.cpp +++ b/engine/src/sprites/sprite_manager.cpp @@ -7,36 +7,37 @@ #include #include +#include #define MAX_SPRITE_SIZE 128 #define MAX_AFFINE_SIZE 31 -void SpriteManager::set(std::vector sprites) { +void SpriteManager::set(std::vector sprites) { initialized = false; this->sprites.clear(); this->sprites.insert(this->sprites.end(), sprites.begin(), sprites.end()); } -void SpriteManager::add(const Sprite &sprite) { +void SpriteManager::add(Sprite* sprite) { if(sprites.size() == MAX_SPRITE_SIZE) { - throw std::runtime_error("maximum sprite limit reached"); + failure(MaxSpriteSizeReached); } - const Sprite* sPtr = &sprite; - sprites.push_back(const_cast(sPtr)); + sprites.push_back(sprite); + copyOverImageDataToVRAM(sprite); } void SpriteManager::render() { if(!initialized) { - throw std::runtime_error("can't render before initialization"); + failure(Cant_Render_Before_Init); } copyOverSpriteOAMToVRAM(); } void SpriteManager::persist() { - copyOverImageDataToVRAM(); + copyOverImageDataToVRAM(); initialized = true; } @@ -46,7 +47,7 @@ void SpriteManager::copyOverSpriteOAMToVRAM() { for(auto sprite : this->sprites) { if(affineIndex > MAX_AFFINE_SIZE) { - throw std::runtime_error("max amount of sprites with affine matriches reached"); + failure(MaxSpritesWithAffineReached); } sprite->update(); @@ -70,11 +71,15 @@ void SpriteManager::copyOverSpriteOAMToVRAM() { } } -void SpriteManager::copyOverImageDataToVRAM() { - for(auto sprite : this->sprites) { - const auto allocated = Allocator::allocateObjectTiles(sprite->imageSize); - dma3_cpy(allocated.pointer(), sprite->data, allocated.size); +void SpriteManager::copyOverImageDataToVRAM(Sprite *sprite) { + const auto allocated = Allocator::allocateObjectTiles(sprite->imageSize); + dma3_cpy(allocated.pointer(), sprite->data, allocated.size); - sprite->buildOam(allocated.getTileLocation()); + sprite->buildOam(allocated.getTileLocation()); +} + +void SpriteManager::copyOverImageDataToVRAM() { + for(auto sprite : sprites) { + copyOverImageDataToVRAM(sprite); } }