From 9f287a18f6e1deaa9d67b1336a775733e3950b28 Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Mon, 4 Mar 2024 18:00:31 +0200 Subject: [PATCH] lib: add image flipping lib: fix Doom tempo --- firmware/main/sdcard/runner/runner.lua | 75 +++++++++++++++----- sdk/addons/lualilka/library/resources.lua | 10 +++ sdk/lib/lilka/src/lilka/buzzer.cpp | 2 +- sdk/lib/lilka/src/lilka/display.cpp | 16 +++++ sdk/lib/lilka/src/lilka/display.h | 6 +- sdk/lib/lilka/src/lua/lualilka_resources.cpp | 70 ++++++++++++++++++ 6 files changed, 158 insertions(+), 21 deletions(-) diff --git a/firmware/main/sdcard/runner/runner.lua b/firmware/main/sdcard/runner/runner.lua index 18bbc999..b51a7ffa 100644 --- a/firmware/main/sdcard/runner/runner.lua +++ b/firmware/main/sdcard/runner/runner.lua @@ -5,11 +5,13 @@ BLACK = display.color565(0, 0, 0) ROOT = 'runner/' -PlayerState = { - STAND = "stand", - RUN = "run", - JUMP = "jump", +STAND_SPRITE = resources.load_image(ROOT .. "boy_stand.bmp", BLACK) +RUN_SPRITES = { + resources.load_image(ROOT .. "boy_run_1.bmp", BLACK), + resources.load_image(ROOT .. "boy_run_2.bmp", BLACK), + resources.load_image(ROOT .. "boy_run_3.bmp", BLACK), } +JUMP_SPRITE = resources.load_image(ROOT .. "boy_jump.bmp", BLACK) Player = { x = 32, @@ -17,15 +19,33 @@ Player = { width = 32, -- Розмір спрайту - 32x32 height = 32, sprites = { - stand = { resources.load_image(ROOT .. "boy_stand.bmp") }, - run = { - resources.load_image(ROOT .. "boy_run_1.bmp", BLACK), - resources.load_image(ROOT .. "boy_run_2.bmp", BLACK), - resources.load_image(ROOT .. "boy_run_3.bmp", BLACK), + left = { + stand = resources.flip_image_x(STAND_SPRITE), + run = { + resources.flip_image_x(RUN_SPRITES[1]), + resources.flip_image_x(RUN_SPRITES[2]), + resources.flip_image_x(RUN_SPRITES[3]), + resources.flip_image_x(RUN_SPRITES[2]), + }, + jump = resources.flip_image_x(JUMP_SPRITE), + }, + right = { + stand = STAND_SPRITE, + run = { + RUN_SPRITES[1], + RUN_SPRITES[2], + RUN_SPRITES[3], + RUN_SPRITES[2], + }, + jump = JUMP_SPRITE, }, - jump = { resources.load_image(ROOT .. "boy_jump.bmp", BLACK) }, }, - state = PlayerState.RUN, + speed_x = 0, + speed_y = 0, + look_direction = 1, + + is_airborne = true, + gravity = 200, } function Player:new(o) @@ -36,18 +56,30 @@ function Player:new(o) end function Player:update(delta) - self.x = self.x + 50 * delta + -- self.x = self.x + 50 * delta + self.speed_y = self.speed_y + self.gravity * delta + + self.x = self.x + self.speed_x * delta + self.y = self.y + self.speed_y * delta + + -- TODO: Calculcate look direction based on speed_x and/or inputs end function Player:draw() local image - if self.state == PlayerState.STAND then - image = self.sprites.stand[1] - elseif self.state == PlayerState.RUN then - -- Перемикаємо спрайти бігу кожні 0.25 секунди - image = self.sprites.run[math.floor(util.time() * 4) % #self.sprites.run + 1] - elseif self.state == PlayerState.JUMP then - image = self.sprites.jump[1] + local sprites + if self.look_direction == 1 then + sprites = self.sprites.right + else + sprites = self.sprites.left + end + + if self.is_airborne then + image = sprites.jump + elseif math.abs(self.speed_x) < 1 then + image = sprites.stand[1] + else + image = sprites.run[math.floor(util.time() * 15) % #sprites.run + 1] end -- Малюємо гравця на екрані так, щоб середина нижнього краю спрайту була в координатах (x, y) display.draw_image(image, self.x - self.width / 2, self.y - self.height) @@ -60,8 +92,13 @@ display.render() function lilka.update(delta) player:update(delta) + local state = controller.get_state() + if state.start.pressed then + util.exit() + end end function lilka.draw() + display.fill_screen(BLACK) player:draw() end diff --git a/sdk/addons/lualilka/library/resources.lua b/sdk/addons/lualilka/library/resources.lua index 9a23135f..02f27642 100644 --- a/sdk/addons/lualilka/library/resources.lua +++ b/sdk/addons/lualilka/library/resources.lua @@ -27,6 +27,16 @@ function resources.load_image(filename, transparent_color) end ---@param blank_color integer колір для пікселів, які залишаться незаповненими function resources.rotate_image(image, angle, blank_color) end +---Відображає зображення горизонтально. Зручно використовувати для платформерів, де герой може рухатись вліво та вправо. +--- +---@param image table ідентифікатор зображення +function resources.flip_image_x(image) end + +---Відображає зображення вертикально. +--- +---@param image table ідентифікатор зображення +function resources.flip_image_y(image) end + ---Читає вміст файлу і повертає його як текст. ---@param filename string шлях до файлу (відносно місця знаходження скрипта, що виконується) ---@return string diff --git a/sdk/lib/lilka/src/lilka/buzzer.cpp b/sdk/lib/lilka/src/lilka/buzzer.cpp index 784b7d18..da9f5b8c 100644 --- a/sdk/lib/lilka/src/lilka/buzzer.cpp +++ b/sdk/lib/lilka/src/lilka/buzzer.cpp @@ -73,7 +73,7 @@ void Buzzer::playDoom() { {NOTE_F3, -16}, {NOTE_D3, -16}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_AS2, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_B2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_AS2, -2}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_AS2, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_B2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_AS2, -2}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_AS2, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_B2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_AS2, -2}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_AS2, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_B2, 8}, {NOTE_C3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_E3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_D3, 8}, {NOTE_E2, 8}, {NOTE_E2, 8}, {NOTE_B3, -16}, {NOTE_G3, -16}, {NOTE_E3, -16}, {NOTE_B2, -16}, {NOTE_E3, -16}, {NOTE_G3, -16}, {NOTE_C4, -16}, {NOTE_B3, -16}, {NOTE_G3, -16}, {NOTE_B3, -16}, {NOTE_G3, -16}, {NOTE_E3, -16}, }; - playMelody(doom_e1m1, sizeof(doom_e1m1) / sizeof(doom_e1m1[0]), 160); + playMelody(doom_e1m1, sizeof(doom_e1m1) / sizeof(doom_e1m1[0]), 60); // TODO - Should be 160... But note durations seem to be off } Buzzer buzzer; diff --git a/sdk/lib/lilka/src/lilka/display.cpp b/sdk/lib/lilka/src/lilka/display.cpp index fc26d41b..84cb9900 100644 --- a/sdk/lib/lilka/src/lilka/display.cpp +++ b/sdk/lib/lilka/src/lilka/display.cpp @@ -135,6 +135,22 @@ void Image::rotate(int16_t angle, Image *dest, int32_t blankColor) { } } +void Image::flipX(Image *dest) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dest->pixels[x + y * width] = pixels[(width - 1 - x) + y * width]; + } + } +} + +void Image::flipY(Image *dest) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dest->pixels[x + y * width] = pixels[x + (height - 1 - y) * width]; + } + } +} + Display display; } // namespace lilka diff --git a/sdk/lib/lilka/src/lilka/display.h b/sdk/lib/lilka/src/lilka/display.h index 571c0edc..607b1e77 100644 --- a/sdk/lib/lilka/src/lilka/display.h +++ b/sdk/lib/lilka/src/lilka/display.h @@ -284,7 +284,7 @@ class Image { public: Image(uint32_t width, uint32_t height, int32_t transparentColor = -1); ~Image(); - /// Повертає зображення, яке отримане обертанням поточного зображення на заданий кут (в градусах), і записує його в `dest`. + /// Обернути зображення на заданий кут (в градусах) і записати результат в `dest`. /// /// @param angle Кут обертання в градусах. /// @param dest Вказівник на Image, в яке буде записано обернуте зображення. @@ -307,6 +307,10 @@ class Image { /// delete rotatedImage; /// @endcode void rotate(int16_t angle, Image *dest, int32_t blankColor); + /// Віддзеркалити зображення по горизонталі і записати результат в `dest`. + void flipX(Image *dest); + /// Віддзеркалити зображення по вертикалі і записати результат в `dest`. + void flipY(Image *dest); uint32_t width; uint32_t height; /// 16-бітний колір (5-6-5), який буде прозорим. За замовчуванням -1 (прозорість відсутня). diff --git a/sdk/lib/lilka/src/lua/lualilka_resources.cpp b/sdk/lib/lilka/src/lua/lualilka_resources.cpp index ac4a9926..704c4cb1 100644 --- a/sdk/lib/lilka/src/lua/lualilka_resources.cpp +++ b/sdk/lib/lilka/src/lua/lualilka_resources.cpp @@ -82,6 +82,74 @@ int lualilka_resources_rotateImage(lua_State* L) { return 1; } +int lualilka_resources_flipImageX(lua_State* L) { + // Arg is image table + // First argument is table that contains image width, height and pointer. We need all of them. + lua_getfield(L, 1, "pointer"); + // Check if value is a valid pointer + if (!lua_islightuserdata(L, -1)) { + return luaL_error(L, "Невірне зображення"); + } + Image* image = (Image*)lua_touserdata(L, -1); + lua_pop(L, 1); + + // Instantiate a new image + lilka::Image* flippedImage = new lilka::Image(image->width, image->height, image->transparentColor); + // Rotate the image + image->flipX(flippedImage); + + // Append rotatedImage to images table in registry + lua_getfield(L, LUA_REGISTRYINDEX, "images"); + lua_pushlightuserdata(L, flippedImage); + lua_setfield(L, -2, (String("xFlippedImage-") + random(100000)).c_str()); + lua_pop(L, 1); + + // Create and return table that contains image width, height and pointer + lua_newtable(L); + lua_pushinteger(L, flippedImage->width); + lua_setfield(L, -2, "width"); + lua_pushinteger(L, flippedImage->height); + lua_setfield(L, -2, "height"); + lua_pushlightuserdata(L, flippedImage); + lua_setfield(L, -2, "pointer"); + + return 1; +} + +int lualilka_resources_flipImageY(lua_State* L) { + // Arg is image table + // First argument is table that contains image width, height and pointer. We need all of them. + lua_getfield(L, 1, "pointer"); + // Check if value is a valid pointer + if (!lua_islightuserdata(L, -1)) { + return luaL_error(L, "Невірне зображення"); + } + Image* image = (Image*)lua_touserdata(L, -1); + lua_pop(L, 1); + + // Instantiate a new image + lilka::Image* flippedImage = new lilka::Image(image->width, image->height, image->transparentColor); + // Rotate the image + image->flipY(flippedImage); + + // Append rotatedImage to images table in registry + lua_getfield(L, LUA_REGISTRYINDEX, "images"); + lua_pushlightuserdata(L, flippedImage); + lua_setfield(L, -2, (String("yFlippedImage-") + random(100000)).c_str()); + lua_pop(L, 1); + + // Create and return table that contains image width, height and pointer + lua_newtable(L); + lua_pushinteger(L, flippedImage->width); + lua_setfield(L, -2, "width"); + lua_pushinteger(L, flippedImage->height); + lua_setfield(L, -2, "height"); + lua_pushlightuserdata(L, flippedImage); + lua_setfield(L, -2, "pointer"); + + return 1; +} + int lualilka_resources_readFile(lua_State* L) { const char* path = luaL_checkstring(L, 1); // Get dir from registry @@ -120,6 +188,8 @@ int lualilka_resources_writeFile(lua_State* L) { static const luaL_Reg lualilka_resources[] = { {"load_image", lualilka_resources_loadImage}, {"rotate_image", lualilka_resources_rotateImage}, + {"flip_image_x", lualilka_resources_flipImageX}, + {"flip_image_y", lualilka_resources_flipImageY}, {"read_file", lualilka_resources_readFile}, {"write_file", lualilka_resources_writeFile}, {NULL, NULL},