#f0ge - game engine for flipper zero

121 messages Β· Page 1 of 1 (latest)

visual nymph
#

Hi all!
Not so long ago I started working on a minimalistic game engine for Flipper. The original goal was only just a PoC to prove it's possible but lately I have changed my mind and want to make this a reality. Currently it is not ready to use but I plan to release a usable version sometime this spring. If you are interested, please follow this thread. I'll do my best to share what I'm currently working on and give sneak peeks.

What features this project will include at first?
[Mostly finished]

  • Entity and component hierarchy (not the same as ECS)
  • Custom rendering solution with sprite rotation and scale support that runs on a separate thread and works on a back buffer to not lock up the main thread

[FINISHED]

  • It can clean up after itself, no need to keep in mind your pointers, if you gave it to the engine it will destroy it when not needed anymore. Nested pointers are still your duty to clean.
  • memory management helpers. Using these you can print out where you are creating pointers and where it is destroyed
  • The engine checks for the heap size before start and after finish and will notify you in console if there is something that was not cleaned up

[Early stage]

  • 2D physics implementation that with the help of the custom renderer will be able to rotate sprites

What is a possible update for later?

  • I'm thinking to incorporate or re-invent a multi channel midi synth engine so the games can have effects and sounds
  • Maybe a full 3D rasterizer can be added to the renderer or if that turns out to be too much for flipper then at least a raycaster

If you have any other suggestion please don't keep it to yourself πŸ˜„

#

What I want to achieve is a library that will do most of the things for you, all you have to do is just set up what goes where and add custom logic to them. If you want to check out an example that I'm updating in the same time as the engine https://github.com/teeebor/flipper-engine-sample/blob/master/jumper.c
This api will stay mostly the same. Also, do not put this on your flipper unless you want to crash it πŸ˜„

The source of the engine can be found here: https://github.com/teeebor/flipper-game-engine

serene finch
#

First of all Woah

#

Second good job this looks great and useful

full kernel
#

i wonder, could you use a tv remote as an input device for games?

#

also, is what about flipper-to-flipper multiplayer?

visual nymph
#

I think both should be possible, but I don't have neither a remote nor a second flipper to test it out

opal sigil
#

Dude flipper to flipper multiplayer would be amazing

visual nymph
surreal parcel
#

@visual nymph any updates?

visual nymph
#

still fighting with physics πŸ˜‚

#

but other updates were made:

  • custom renderer now can draw circles and lines, still have to add a new type of graphics object so you can define the shape for the entity
  • you don't have to enable manually the rendering, if you add a graphics object it will set that flag
visual nymph
#

not the most stable simulation, but finally it collides with the lines

visual nymph
#

project not abandoned, just taking a break. I will continue working on it in a few weeks

full kernel
#
  • famous last words before abandoning a project
visual nymph
#

never gonna happen πŸ˜…

visual nymph
visual nymph
#

and here is the new interface

class RotationComponent : public Component {
    float speed=1;
public:
    explicit RotationComponent(float s): speed(s){}
    RotationComponent(): speed(1){}

    void Update(const float &delta) override {
        entity->get_transform()->rotate(speed * (float)delta);
    }

    void OnInput(InputKey key, InputState type) override {
        if(key == InputKeyOk && type == InputPress)
            speed*=-1;
    }

};

#ifdef __cplusplus
extern "C"
{
#endif

int32_t jumper_app(void *p) {
    UNUSED(p);
    Engine *engine_instance = new Engine("SampleGame", 30, true);

    auto *current_scene = new Scene("play");

    auto *ball = new Entity("Ball");
    ball->AddComponent<RotationComponent>();
    ball->set_sprite(&I_platforms);
    ball->get_transform()->set_position({20,32});
    current_scene->Add(ball);

    ball = new Entity("Ball 2");
    ball->AddComponent<RotationComponent>(0.5f);
    ball->set_sprite(&I_platforms);
    ball->get_transform()->set_position({100,32});
    current_scene->Add(ball);

    engine_instance->SetScene(current_scene);

    engine_instance->Start();
    delete engine_instance;

    return 1;
}

#ifdef __cplusplus
}
#endif
visual nymph
tardy ravine
#

Do you know PICO-8?

visual nymph
visual nymph
#

Status report:

  • collisions are working
  • physics got an auto-sleep solution so only moving objects gonna be processed
  • physics based movement is somewhat working, better solution on the way
plucky herald
#

I wish u could help you but I'm new in flipper world πŸ™‚

frigid vessel
plucky herald
#

Thanks, I know a little about dev, so Im gonna check your project so I can learn more about flipper dev

visual nymph
#

if you have any questions feel free to ask

visual nymph
hardy panther
#

Looks smooth af

full kernel
#

nice, love that you did a JavaScript port

visual nymph
solemn salmon
#

I'm interested in this project. Currently working on beej's guide to learn enough C to be useful. About halfway through it, then I'm gonna try and build a fap

visual nymph
solemn salmon
feral mortar
#

Imagine ovo on the flipper

lofty dome
#

imagine having an imu on a flipper so you could tilt the device and make the balls move aroudn

visual nymph
fossil monolith
#

I've been taking a stab at refactoring this engine - the original C version, not the C++ branch - and am getting close to having it working. just thought I'd start mentioning it now. it's been an interesting exercise in teaching myself C (only really worked with Java, C++, C# before)

#

was strange to see Icons being rendered into a temporary buffer and then reading in a copy of that drawn portion just to get the image data, but I suppose that was from working around buffer access or taking advantage of something else given you can get_icon_data currently, and cannot access the Canvas buffer as that's currently an anonymous struct for apps afaict

fossil monolith
#

for the latter I took what Icon/Canvas are doing internally since the codec library included is exposed via compress.h

// Decode compressed XMP data that Icons use to get a copy of uncompressed 1-bit data
uint8_t* decode_icon(const Icon* icon) {
    uint8_t* p_icon_data;
    uint8_t width = icon_get_width(icon);
    uint8_t height = icon_get_height(icon);
    uint16_t size = (width/8)*height;

    // Create decoder instance and grab decoded data pointer
    // - Ideally re-use the CompressIcon and free on app exit
    CompressIcon* compress_icon = compress_icon_alloc();
    compress_icon_decode(compress_icon, icon_get_data(icon), &p_icon_data);

    // Copy the data since the decoder only holds on temporarily
    uint8_t* icon_data = malloc(size);
    memcpy(icon_data, p_icon_data, size);

    // Free decoder instance as we're done here
    compress_icon_free(compress_icon);

    return icon_data;
}

Obviously memory is limited so this shouldn't always unpack for every asset all the time, but figured I'd share this pathway in case someone else could use it.

#

and without direct canvas buffer access I'm actually using double buffers with the front buffer being direct-copied to the canvas using

void buffer_write_canvas(Canvas* canvas, ScreenBuffer* buffer) {
    canvas_draw_xbm(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, buffer->buffer);
}```
visual nymph
# fossil monolith for the latter I took what Icon/Canvas are doing internally since the codec libr...

Didn't had time to work on this for a long time sadly (and discord for some reason failed to notify me about your post so sorry for the late response).
That compress header is a new thing? Don't remember that.
When I started this canvas memory manipulation code back in solitaire, the only thing I found that used minimal memory and computation was to directly manipulate the canvas buffer, and if needed keep a back buffer.
Basically this one thing is the reason solitaire is not in the app catalog yet.

The icon rendering part in the old C code was because I wanted a fast way to decompress them and have in the same format as the canvas buffer uses, don't remember having exposed api for that. I even introduced a new format for the c++ version to get rid of that icon renderer, but it only made things worse.

This solution could really make things easier for sure, thanks for the info!

#

now the thing that I really have to figure out is physics πŸ˜„

fossil monolith
#

I cloned your engine and ended up rewriting a bunch of it but some of the fundamental stuff is pretty much untouched like your ECS objects

#

I didn't use any of the physics though as I just wanted to focus on having a more generic ECS tile-based sprite system with later 3D support, and was mostly messing around

visual nymph
#

I think the main reason for the c++ rewrite was because of the physics, it is hard enough as is, wanted to utilize operator overloads so at least the equations are somewhat readable

fossil monolith
# fossil monolith for the latter I took what Icon/Canvas are doing internally since the codec libr...

that is a bit dated since I mentioned it; this is what I have now and had to do type-punning to get the compressed size to pass to the decoder so I could handle images that are larger than 1024 bytes (a bug/limitation in the current design on flipper's internal API):

// Decode compressed XMP data that Icons use to get uncompressed 1-bit image
uint8_t* decode_icon(const Icon* icon) {
    FURI_LOG_D(ENGINE_TITLE, "Decoding icon");
    uint8_t* p_icon_compressed = (uint8_t*)icon_get_data(icon);
    
    if (compress_container == NULL) {
        FURI_LOG_W(ENGINE_TITLE, "Compression decoder not allocated - allocating now [%i]", DECODER_SIZE_BYTES);
        compress_container = compress_alloc(DECODER_SIZE_BYTES);
    }

    uint8_t width = icon_get_width(icon);
    uint8_t height = icon_get_height(icon);

    size_t size = (width/8)*height; // Expected size
    size_t actual_size = 0; // Actual decompressed size returned by decoder

    uint8_t* p_icon_decompressed = malloc(size);

    Mirror__CompressHeader* header = (Mirror__CompressHeader*)p_icon_compressed;
    uint16_t compressed_size = header->compressed_buff_size;

    FURI_LOG_D(ENGINE_TITLE, " - compress_decode width/height = %i,%i", width, height);
    FURI_LOG_D(ENGINE_TITLE, " - compress_decode(container, p_compressed, %i, p_decompressed, %i, %i)", compressed_size, size, actual_size);
    
    bool success = compress_decode(compress_container, p_icon_compressed, compressed_size, p_icon_decompressed, size, &actual_size);
    if (success) {
        FURI_LOG_D(ENGINE_TITLE, " - Decompression completed with actual size of %i", actual_size);
    }
    else {
        FURI_LOG_E(ENGINE_TITLE, "FAILED TO DECODE IMAGE");
    }

    FURI_LOG_D(ENGINE_TITLE, "Returning final decompressed data");
    return p_icon_decompressed;
}
visual nymph
#

really hate add(x, y) πŸ˜‚

fossil monolith
#

I hadn't worked in anything except C#, Python, and (back in my mobile dev days) Java for around the past 10 years, so diving into C while learning flipper app dev and poking around with your engine has been quite the experience πŸ™‚

#

most recent thing I got working was a tilemapper but I need to redo the sprite code I began working on as it doesn't output right (bitwise math is tricky to get right on multi-byte 1bit images)

visual nymph
#

wow

fossil monolith
#

but I've been bogged down with impending 1.0 launch of work project so I haven't had free time or mental energy to do extra on the side for the moment

#

once I get more of the 2D stuff done I was going to port my old c++ rasterizer code from like 15 years ago and figure out some dithering algo, or chase after syncing the refresh rate to simulate 4-shade grayscale like the old TI calculator days πŸ˜†

visual nymph
#

there were experiments about that by someone when I started this engine

#

iirc it worked by utilizing the slow response time of the display, it drawed the pixels every second frame using direct draw

fossil monolith
#

ya, just lower priority until other fundamentals are in place

#

not a matter of if but when 😁

glad island
#

Unfortunately with VGM, grayscale via flicker is not going to be viable anymore, as it'll just show as flicker on the TV

#

Unless you end up not doing that if VGM is connected

#

Right now I only know how to detect the IMU, I don't know if screen casting can be detected - should probably ask the devs about that

visual nymph
#

maybe with modified firmware we could simulate the slow pixel response times. Or maybe even add the feature for the main firmware and allow apps to enable it via gpio

glad island
#

Yeah maybe

glad island
#

I don't know if a pattern like this means "you're going too fast" or "too slow"

visual nymph
#

ignore the refresh rate text in the output, used the wrong word, it means flip every x tickπŸ˜…

glad island
#

Maybe if it can explicitly sync on the display refresh but I don't think this is exposed

visual nymph
#

saw that there is a mutex now inside canvas, tried that one out maybe it gets locked until there is a refresh, but it behaves the same as before :/

#

this one makes me really happy πŸ˜„

glad island
#

indeed, same

glad island
#

instead of the normal "game logic"

visual nymph
#

there is a callback too, but that can't be accessed as easily as the mutex, wasn't able to test that

glad island
#

yeah I think these callbacks are internal, anyway it wouldn't be any different to doing it directly before the canvas commit

white trench
#

Apparently I need to comment here for discord to let me actually follow the thread. Sorry.

glad island
white trench
pale barn
#

How to put it on my flipper

#

And how to install ufbt

#

On pc

#

To make apps

pale barn
#

@visual nymph how do I install it and I have questions

visual nymph
#

just to clarify, do you want the game engine that is made by the flipper devs? This is not that engine

pale barn
#

I mean sure I want the game engine

#

I saw the github

visual nymph
#

physics is not working properly

pale barn
visual nymph
#

then you can use it already πŸ˜„
it has the update loop and rendering with rotation support working

visual nymph
#

it needs an update to make it compatible with the flipper app catalog

#

but for direct build I think it should still work

pale barn
#

I will beggin tomorrow

visual nymph
pale barn
#

Thank you

#

Byyy

dusk bridge
visual nymph
#

f0ge - game engine for flipper zero

#

reviving the project under a new name to avoid confusion with the other game engine

visual nymph
spiral sail
visual nymph
#

You can achieve pixel shader-like behaviors if you pass in a custom callback for the renderer

void render_circle(Buffer *screen, RenderData *data, Vector *scaling, Vector *pixel, Vector *uv) {
    UNUSED(data);
    UNUSED(scaling);
    
    Vector center = {0.5f, 0.5f};

    if (vector_distance(uv, &center) < 0.5f) {
        set_pixel(screen, pixel);
    }
}

static RenderData car_render = (RenderData){
    .poly = RECTANGLE(-12, -18, 12, 18),
    .tile_mode = TILE_NONE,
    .color = Black,
    .callback = &render_circle
};
drifting rapids
visual nymph
# drifting rapids What is enitiy and component hierarcy could you make it basic I feel so much di...

it is basically what you have inside unity. You have GameObjects that can have other GameObjects as children in the tree, this means the children will have relative movement, meaning when you move the parent object it will move the child too.
About the components, those are similar to unity's MonoBehaviour classes. You make a component for example to move a car. You add it to one or more car objects, then during play all those car object will act the same way

visual nymph
#

A visual example.
The outer "Hi" circle is a child of the one in the center. It has been moved to the side a bit.
Both circles have a rotation component added, the outer one has an extra component that changes scale.

The result is that the center one spins dragging the outer one with it and the outer one spins on its own while also having a scale effect

dire vale
#

dunno if this is completely usable for flipworld for example but @golden mica would think this is really cool lol

#

awesome project man looks damn awesome

drifting rapids
#

Yes got it the object is the parent and child is child

#

The object is for example car

#

Child is the movment of thr car

#

Right?

glad island
#

no the child would be the door that has independent movement but also moves together with the parent (car)