Handcrank Engine is an easy-to-use game engine based on simple principles that are quick to understand and build with.
Current version: pre-alpha
Getting Started
Creating and rendering objects in Handcrank was made to be as easy as possible, with the most simple example taking only a few lines to achieve.
#include "Handcrank/Handcrank.hpp"
#include "Handcrank/RectRenderObject.hpp"
using namespace Handcrank;
auto game = std::make_unique<Game>();
auto main(int argc, char *argv[]) -> int
{
game->SetScreenSize(500, 300);
auto rect = std::make_shared<RectRenderObject>();
rect->SetAnchor(RectAnchor::HCENTER | RectAnchor::VCENTER);
rect->SetRect(game->GetWidth() / 2, game->GetHeight() / 2, 250, 250);
rect->SetFillColor(255, 0, 0, 255);
game->AddChildObject(rect);
return game->Run();
}
Simple Event Loop
#include <iostream>
#include "Handcrank/Handcrank.hpp"
using namespace Handcrank;
class LoopDebugger : public RenderObject
{
public:
void Update(const double deltaTime) override
{
std::cout << "Update " << std::to_string(deltaTime) << std::endl;
}
void FixedUpdate(const double fixedDeltaTime) override
{
std::cout << "Fixed Update " << std::to_string(fixedDeltaTime)
<< std::endl;
}
};
auto game = std::make_unique<Game>();
auto main(int argc, char *argv[]) -> int
{
game->SetScreenSize(500, 300);
game->AddChildObject(std::move(std::make_unique<LoopDebugger>()));
return game->Run();
}
Sprite Animations
Sprite animations can be generated via automatic frame calculation based on image width and frame count, or manually like in the example below. Either way, sprite animations were made to be simple to work with and customize.
#include "alienGreen.h"
#include "Handcrank/Handcrank.hpp"
#include "Handcrank/SpriteRenderObject.hpp"
using namespace Handcrank;
class GreenAlien : public SpriteRenderObject
{
public:
void Start() override
{
LoadTexture(game->GetRenderer(), images_alienGreen_png,
images_alienGreen_png_len);
SDL_SetTextureScaleMode(texture, SDL_ScaleMode::SDL_ScaleModeBest);
SetAnchor(RectAnchor::HCENTER | RectAnchor::VCENTER);
SetPosition(game->GetWidth() / 2, game->GetHeight() / 2);
SetFrameSpeed(0.25);
AddFrame({69, 193, 68, 93});
AddFrame({0, 0, 70, 96});
Play();
}
};
auto game = std::make_unique<Game>();
auto main(int argc, char *argv[]) -> int
{
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
game->SetScreenSize(500, 300);
game->AddChildObject(std::move(std::make_unique<GreenAlien>()));
return game->Run();
}
Animations
#include "Handcrank/Animator.hpp"
#include "Handcrank/Handcrank.hpp"
#include "Handcrank/RectRenderObject.hpp"
using namespace Handcrank;
auto game = std::make_unique<Game>();
auto main(int argc, char *argv[]) -> int
{
game->SetScreenSize(500, 300);
auto cube = std::make_shared<RectRenderObject>(
game->GetWidth() / 2, game->GetHeight() / 2, 250, 250);
cube->SetAnchor(RectAnchor::HCENTER | RectAnchor::VCENTER);
cube->SetFillColor(MAX_R, 0, 0, 0);
game->AddChildObject(cube);
auto animator = std::make_shared<Animator>(Animator::Mode::SEQUENCE, true);
float internalAlpha = 0;
// Fade in
animator->AddAnimation(std::make_shared<Animation>(
[&](const double deltaTime, const double elapsedTime)
{
auto color = cube->GetFillColor();
internalAlpha += 500 * deltaTime;
auto alpha = std::clamp(internalAlpha, 0.0F, (float)MAX_ALPHA);
cube->SetFillColor(color.r, color.g, color.b, alpha);
return alpha == (float)MAX_R ? 0 : 1;
}));
// Pause
animator->AddAnimation(std::make_shared<Animation>(
[&](const double deltaTime, const double elapsedTime)
{ return elapsedTime > 0.1 ? 0 : 1; }));
// Fade out
animator->AddAnimation(std::make_shared<Animation>(
[&](const double deltaTime, const double elapsedTime)
{
auto color = cube->GetFillColor();
internalAlpha -= 500 * deltaTime;
auto alpha = std::clamp(internalAlpha, 0.0F, (float)MAX_ALPHA);
cube->SetFillColor(color.r, color.g, color.b, alpha);
return alpha == 0 ? 0 : 1;
}));
// Pause
animator->AddAnimation(std::make_shared<Animation>(
[&](const double deltaTime, const double elapsedTime)
{ return elapsedTime > 0.1 ? 0 : 1; }));
game->AddChildObject(animator);
return game->Run();
}
Tiled Map Support
Currently Tiled map support isn't native, but utilizing the VertexRenderObject component you can easily setup a single texture to render in your game.
#include "tilemap_packed.h"
#include "Handcrank/Handcrank.hpp"
#include "Handcrank/Utilities.hpp"
#include "Handcrank/VertexRenderObject.hpp"
using namespace Handcrank;
class TileMap : public VertexRenderObject
{
private:
int textureWidth;
int textureHeight;
const int scale = 3;
const int tileWidth = 16;
const int tileHeight = 16;
public:
void Start() override
{
texture =
LoadCachedTexture(game->GetRenderer(), images_tilemap_packed_png,
images_tilemap_packed_png_len);
SDL_QueryTexture(texture, nullptr, nullptr, &textureWidth,
&textureHeight);
int terrain[][30] = {
{2, 2, 2, 1, 58, 1, 2, 2, 1, 1, 1, 1, 3, 3, 1,
1, 1, 3, 2, 2, 58, 2, 2, 3, 1, 2, 1, 1, 1, 1},
{3, 3, 3, 2, 58, 3, 1, 1, 1, 1, 2, 1, 2, 1, 2,
3, 1, 1, 1, 1, 58, 1, 2, 2, 1, 1, 1, 1, 2, 1},
{1, 2, 4, 74, 23, 1, 1, 1, 2, 2, 2, 2, 1, 3, 1,
3, 1, 1, 1, 3, 58, 1, 1, 3, 2, 1, 2, 1, 1, 1},
{2, 1, 58, 1, 1, 1, 1, 1, 3, 2, 2, 2, 2, 1, 1,
1, 1, 2, 1, 1, 58, 2, 1, 1, 1, 1, 1, 3, 2, 1},
{2, 1, 58, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3,
1, 1, 19, 20, 20, 76, 20, 21, 1, 1, 3, 1, 1, 3, 2},
{1, 1, 58, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1,
1, 1, 37, 38, 38, 38, 38, 92, 20, 21, 1, 1, 1, 1, 1},
{1, 2, 22, 74, 74, 74, 5, 2, 3, 2, 1, 1, 1, 1, 1,
2, 1, 55, 56, 94, 38, 38, 38, 38, 39, 2, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 58, 1, 2, 1, 2, 2, 1, 2, 1,
1, 2, 1, 1, 37, 38, 38, 38, 38, 39, 1, 1, 3, 2, 1},
{2, 1, 1, 2, 1, 1, 58, 2, 1, 2, 1, 1, 1, 1, 1,
1, 1, 1, 1, 55, 56, 56, 56, 56, 57, 2, 1, 1, 1, 2},
{3, 1, 1, 2, 1, 2, 58, 1, 2, 1, 1, 3, 2, 1, 1,
1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1},
{20, 20, 20, 20, 20, 20, 76, 20, 21, 1, 3, 2, 2, 1, 1,
1, 1, 1, 2, 3, 1, 3, 1, 1, 1, 1, 1, 1, 2, 1},
{38, 38, 38, 38, 38, 38, 38, 38, 39, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2},
{38, 38, 38, 38, 38, 38, 38, 38, 92, 20, 20, 20, 20, 20, 21,
1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 2, 3, 1, 3},
{38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39,
2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2},
{56, 56, 56, 56, 56, 94, 38, 38, 38, 38, 38, 38, 38, 38, 92,
20, 20, 21, 1, 1, 3, 2, 2, 1, 1, 2, 1, 3, 1, 2},
{1, 2, 2, 1, 1, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38,
38, 38, 39, 1, 3, 2, 2, 1, 1, 3, 3, 1, 1, 2, 1},
{2, 1, 2, 1, 2, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38,
38, 38, 39, 3, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1}};
int objects[][30] = {
{0, 0, 6, 0, 0, 0, 145, 0, 0, 0, 145, 95, 95, 113, 113,
95, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 145, 95, 0, 0, 145, 0, 95, 113, 95,
113, 113, 0, 95, 0, 0, 0, 0, 0, 0, 0, 6, 0, 95, 0},
{0, 0, 0, 0, 0, 0, 145, 0, 0, 113, 145, 113, 113, 113, 113,
95, 0, 0, 0, 0, 0, 0, 0, 95, 6, 0, 0, 6, 0, 95},
{0, 0, 0, 0, 0, 0, 146, 111, 111, 111, 148, 0, 113, 95, 0,
50, 0, 0, 0, 6, 0, 0, 0, 0, 95, 95, 0, 95, 0, 0},
{111, 111, 131, 111, 111, 111, 166, 0, 0, 0, 145, 0, 0, 0, 0,
0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 113, 113, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 0, 48, 0, 12,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 113, 95, 0, 0},
{95, 0, 0, 0, 0, 0, 0, 0, 110, 111, 165, 111, 129, 111, 112,
0, 113, 113, 0, 0, 0, 0, 0, 0, 0, 0, 113, 113, 95, 113},
{113, 95, 95, 0, 95, 0, 0, 0, 0, 0, 0, 0, 145, 0, 0,
0, 0, 95, 95, 0, 0, 0, 0, 0, 0, 113, 95, 113, 95, 113},
{0, 113, 113, 113, 113, 0, 0, 0, 0, 0, 0, 0, 145, 0, 0,
113, 113, 113, 82, 0, 0, 0, 0, 0, 0, 95, 95, 113, 113, 0},
{0, 95, 113, 0, 0, 0, 0, 13, 0, 0, 128, 111, 148, 0, 0,
0, 95, 0, 0, 0, 0, 0, 0, 0, 95, 113, 113, 95, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
145, 0, 164, 111, 111, 129, 111, 111, 111, 130,
0, 113, 95, 113, 113, 113, 95, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 0, 0, 0, 0,
145, 0, 0, 0, 145, 0, 113, 95, 89, 0, 113, 113, 0, 95, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0,
145, 0, 81, 0, 164, 111, 111, 129, 111, 112, 95, 0, 113, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 131, 131, 131, 131,
166, 6, 0, 81, 0, 0, 113, 145, 89, 0, 0, 0, 113, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 149, 149, 149,
0, 0, 0, 0, 0, 0, 0, 145, 113, 0, 6, 0, 0, 0, 95},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 145, 0, 6, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 145, 0, 0, 0, 0, 113, 0, 0}};
AddTileSet(terrain, std::size(terrain), std::size(terrain[0]));
AddTileSet(objects, std::size(objects), std::size(objects[0]));
}
private:
void AddTile(int x, int y, int offsetX, int offsetY)
{
GenerateTextureQuad(vertices, indices,
{(float)x * scale, (float)y * scale,
(float)tileWidth * scale,
(float)tileHeight * scale},
{(float)offsetX, (float)offsetY, (float)tileWidth,
(float)tileHeight},
{255, 255, 255, 255}, textureWidth, textureHeight);
}
void AddTileSet(const int tiles[][30], int cols, int rows)
{
int tilesPerCol = textureWidth / tileWidth;
int tilesPerRow = textureHeight / tileHeight;
for (auto y = 0; y < cols; y += 1)
{
for (auto x = 0; x < rows; x += 1)
{
if (tiles[y][x] == 0)
{
continue;
}
int tileIndex = tiles[y][x] - 1;
int srcX = (tileIndex % tilesPerCol) * tileWidth;
int srcY = (tileIndex / tilesPerCol) * tileHeight;
AddTile(x * tileWidth, y * tileHeight, srcX, srcY);
}
}
}
};
auto game = std::make_unique<Game>();
auto main(int argc, char *argv[]) -> int
{
game->SetScreenSize(500, 300);
game->AddChildObject(std::move(std::make_unique<TileMap>()));
return game->Run();
}