Howdy,
I have been quite silent since I received my console. As I couldn't figure out which game to make, I decided to try and create game engine for the Gamebuino-META :)
https://github.com/jlauener/GBX
The engine comes as an Arduino library so that it can be installed in the 'libraries' folder. As it's only two files (header and compilation unit) it can easily be copied into a sketch folder and hacked.
It's still very early in development but so far I'm quite satisfied with it. It has been quite difficult to find the right balance between ease of use and efficient RAM usage. But I feel like I'm slowly getting there :) You can check the README on the repo for features and the roadmap.
Here is a demo of a basic breakout game made in GBX. Not a full game though! It is just to show core things such as sprites, animations and collisions. It runs at 61% of CPU (when there are all the blocks) and free RAM is 12495 (it doesn't go down as blocks are destroyed because all allocated memory is cached to prevent fragmentation).
The code for the breakout is (only?) 248 lines. You can easily test it on your device if you put GBX.h and GBX.cpp in the sketch folder. Debug console can be turned on/off using the menu button.
#include "GBX.h"#define TYPE_BALL 0
#define TYPE_PADDLE 1
#define TYPE_BRICK 2
#define TYPE_WALL 3//-----------------------------------------------------------------------------
// Paddle
//-----------------------------------------------------------------------------const uint16_t paddleSpriteData[] =
{
// width, height
16, 4,
// transparent color
0xF81F,
// bitmap
0xF81F, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xfeb2, 0xF81F, 0xfeb2, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xfd42, 0xfeb2, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xcc68, 0xfd42, 0xF81F, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xfd42, 0xF81F
};const uint8_t paddleSolidType[] =
{
// count
1,
// entity types
TYPE_WALL
};class Paddle : public Entity
{
public:
Paddle() :
sprite(paddleSpriteData, -8, -2)
{
setHitbox(-8, -2, 16, 4);
}void update()
{
if (gbx::isDown(BUTTON_LEFT))
{
moveBy(-2, 0, paddleSolidType);
}
else if (gbx::isDown(BUTTON_RIGHT))
{
moveBy(2, 0, paddleSolidType);
}
}void draw(int16_t x, int16_t y)
{
sprite.draw(x, y);
}private:
Sprite sprite;
};//-----------------------------------------------------------------------------
// Brick
//-----------------------------------------------------------------------------const uint16_t brickSpriteData[] =
{
// width, height
8, 4,
// transparent color
0xF81F,
// bitmap
0xca30,0xca30,0xca30,0xca30,0xca30,0xca30,0xca30,0xd8e4,0xca30,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0x9008,0xca30,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0x9008,0xd8e4,0x9008,0x9008,0x9008,0x9008,0x9008,0x9008,0x9008,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xffff,0xffff,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd8e4,0xd8e4,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0x7ddf,0x7ddf,0x7ddf,0x7ddf,0x7ddf,0x7ddf,0x7ddf,0x4439,0x7ddf,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x210,0x7ddf,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x210,0x4439,0x210,0x210,0x210,0x210,0x210,0x210,0x210,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0xffff,0xffff,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x4439,0x4439,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439,0x4439
};#define IDLE 0
#define BREAK 1const uint8_t brickAnimData[] =
{
// IDLE (frame count, mode, interval, frames...)
1, LOOP, 0, 0,
// BREAK (frame count, mode, interval, frames...)
2, ONE_SHOT, 2, 1, 2
};#define BRICK_RED 0
#define BRICK_BLUE 1class Brick : public Entity
{
public:
Brick() :
sprite(brickSpriteData, brickAnimData)
{
setHitbox(8, 4);
sprite.play(IDLE);
}void setBrickType(uint8_t brickType)
{
sprite.frameOffset = brickType * 3;
}void draw(int16_t x, int16_t y)
{
sprite.draw(x, y);
}void hit()
{
setFlag(FLAG_COLLIDABLE, false);
sprite.play(BREAK);
}private:
Anim sprite;
};//-----------------------------------------------------------------------------
// Ball
//-----------------------------------------------------------------------------const uint16_t ballSpriteData[] =
{
// width, height
4, 4,
// transparent color
0xF81F,
// bitmap
0xF81F,0xacd0,0xacd0,0xF81F,0xacd0,0xffff,0xffff,0xacd0,0xacd0,0xffff,0xacd0,0xacd0,0xF81F,0xacd0,0xacd0,0xF81F
};const uint8_t ballSolidType[] =
{
// count
3,
// entity types
TYPE_PADDLE, TYPE_BRICK, TYPE_WALL
};class Ball : public Entity
{
public:
Ball() :
sprite(ballSpriteData, -2, -2)
{
setHitbox(-2, -2, 4, 4);
}void update()
{
moveBy(dx, dy, ballSolidType);
}void draw(int16_t x, int16_t y)
{
sprite.draw(x, y);
}protected:
bool onMoveCollideX(Entity& other)
{
if (other.getType() == TYPE_BRICK)
{
Brick& brick = (Brick&)other;
brick.hit();
}
dx = -dx;
return true;
}bool onMoveCollideY(Entity& other)
{
if (other.getType() == TYPE_BRICK)
{
Brick& brick = (Brick&)other;
brick.hit();
}
dy = -dy;
return true;
}private:
int16_t dx = 1;
int16_t dy = -1;
Sprite sprite;
};//-----------------------------------------------------------------------------
// GameScene
//-----------------------------------------------------------------------------class GameScene : public Scene
{
public:
GameScene() :
ball(this, TYPE_BALL, 1),
paddle(this, TYPE_PADDLE, 1),
bricks(this, TYPE_BRICK, 48),
walls(this, TYPE_WALL, 4)
{
}void onInit()
{
walls.spawn()->setHitbox(-8, 0, 8, (uint8_t) gbx::height); // left wall
walls.spawn()->setHitbox((int8_t) gbx::width, 0, 8, (uint8_t) gbx::height); // right wall
walls.spawn()->setHitbox(0, -8, (uint8_t) gbx::width, 8); // top wall
walls.spawn()->setHitbox(0, (int8_t) gbx::height, (uint8_t) gbx::width, 8); // bottom wallball.spawn(gbx::width / 2, gbx::height - 16); paddle.spawn(gbx::width / 2, gbx::height - 8); bool flipFlop = false; for (int16_t ix = 0; ix < 8; ix++) { for (int16_t iy = 0; iy < 6; iy++) { bricks.spawn(8 + ix * 8, 8 + iy * 4)->setBrickType(flipFlop ? BRICK_RED : BRICK_BLUE); flipFlop = !flipFlop; } flipFlop = !flipFlop; }}
private:
EntityPool<Ball> ball;
EntityPool<Paddle> paddle;
EntityPool<Brick> bricks;
EntityPool<Entity> walls;
};//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------GameScene gameScene;
void setup()
{
gbx::init();
gbx::setScene(gameScene);
}void loop()
{
gbx::update();
}
Hope someone finds it useful.