Skip to content

Commit

Permalink
lib: reduce fread calls when loading BMP
Browse files Browse the repository at this point in the history
main: add comments to asteroids
  • Loading branch information
and3rson committed Mar 2, 2024
1 parent fc6c04a commit 01bab9f
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 36 deletions.
111 changes: 91 additions & 20 deletions firmware/main/sdcard/asteroids/asteroids.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
-- Character sprites by https://laredgames.itch.io/coins-free
--[[
mm mm mm mm
#### ##
#### m#####m ###### m####m ##m###m m####m #### ####### ## m## ##
## ## ## ## ##mmmm## ## ## ## ## ## ## ## ## #### ##
###### ## ## ## ## ## ## ## ## ## ## ##m# ##
m## ##m ##mmmm# ## ##mmmm# ###mm## ##mm## mmm##mmm m##mmm## ### ## mm
## ## #
##
Автор: Андрій "and3rson" Дунай
Шрифти, використані в графіці: https://ukrfonts.com/info/index.php?v=20&id=5730
--]]

WHITE = display.color565(255, 255, 255)
BLACK = display.color565(0, 0, 0)
Expand All @@ -8,6 +21,9 @@ MAGENTA = display.color565(255, 0, 255)
-- ROOT = 'asteroids/'
ROOT = ''

-------------------------------------------------------------------------------
-- Загальні константи
-------------------------------------------------------------------------------

ANGLE_COUNT = 60
ANGLE_STEP = 360 / ANGLE_COUNT
Expand All @@ -17,6 +33,10 @@ display.set_cursor(8, display.height / 2 - 8)
display.print("Завантаження...")
display.render()

-------------------------------------------------------------------------------
-- Завантаження ресурсів
-------------------------------------------------------------------------------

SHIP_SPRITES = { resources.load_image(ROOT .. "ship.bmp", MAGENTA) }
SHIP_FORWARD_SPRITES = { resources.load_image(ROOT .. "ship_forward.bmp", MAGENTA) }
SHIP_BACKWARD_SPRITES = { resources.load_image(ROOT .. "ship_backward.bmp", MAGENTA) }
Expand Down Expand Up @@ -45,6 +65,14 @@ end
PRESS_START = resources.load_image(ROOT .. "press_start.bmp")
YOU_ARE_DEAD = resources.load_image(ROOT .. "game_over.bmp")

-------------------------------------------------------------------------------
-- Ігрові класи
-------------------------------------------------------------------------------

--
-- Корабель, яким керує гравець
--

Ship = {
x = display.width / 2,
y = display.height / 2,
Expand All @@ -64,7 +92,6 @@ Ship = {
dead = false,
-- accel_y = 0,
}

function Ship:new(o)
o = o or {}
setmetatable(o, self)
Expand Down Expand Up @@ -142,6 +169,10 @@ function Ship:draw()
end
end

--
-- Куля, якою стріляє корабель
--

Bullet = {
x = 0,
y = 0,
Expand Down Expand Up @@ -188,6 +219,10 @@ function Bullet:draw()
display.fill_circle(self.x, self.y, self.radius, WHITE)
end

--
-- Астероїд, який рухається по екрану
--

Asteroid = {
x = 0,
y = 0,
Expand Down Expand Up @@ -272,53 +307,70 @@ function Asteroid:draw()
end
end

--- @type table | nil
-------------------------------------------------------------------------------
-- Змінні, які зберігають стан гри та всіх ігрових об'єктів.
-------------------------------------------------------------------------------

--- @type table
local ship = nil -- Ship:new()
local bullets = {}
local asteroids = {}

-- Час, коли наступний астероїд з'явиться на екрані
local next_asteroid_spawn_time = 0

GAME_STATE = {
HELLO = 0,
IN_GAME = 1,
GAME_OVER = 2,
-- Перелік станів гри
STATES = {
HELLO = 0, -- Початковий екран
IN_GAME = 1, -- Гра
GAME_OVER = 2, -- Кінець гри
}
local game_state = STATES.HELLO

local game_state = GAME_STATE.HELLO
-------------------------------------------------------------------------------
-- Головні цикли гри
-------------------------------------------------------------------------------

--
-- Цикл обробки введення та оновлення стану гри
--
function lilka.update(delta)
if game_state == GAME_STATE.HELLO then
if game_state == STATES.HELLO then
-- Якщо гравець натиснув кнопку "Старт", то починаємо гру
if controller.get_state().start.just_pressed then
game_state = GAME_STATE.IN_GAME
game_state = STATES.IN_GAME
ship = Ship:new()
bullets = {}
asteroids = {}
end
else
-- Оновлюємо стан корабля, куль та астероїдів
ship:update(delta)
for _, bullet in ipairs(bullets) do
bullet:update(delta)
end
for _, asteroid in ipairs(asteroids) do
asteroid:update(delta)
end
-- Remove dead bullets
-- Видаляємо мертві кулі
for i = #bullets, 1, -1 do
if bullets[i].dead then
table.remove(bullets, i)
end
end
-- Remove dead asteroids
-- Видаляємо мертві астероїди
for i = #asteroids, 1, -1 do
if asteroids[i].dead and util.time() - asteroids[i].killed_at > 1 then
table.remove(asteroids, i)
end
end

-- Отримуємо стан контролера та обробляємо введення
local state = controller.get_state()

-- Керуємо кораблем, якщо він не мертвий
if not ship.dead then
-- Рух вперед та назад
if state.up.pressed then
ship:set_forward_acceleration(100)
elseif state.down.pressed then
Expand All @@ -327,6 +379,7 @@ function lilka.update(delta)
ship:set_forward_acceleration(0)
end

-- Поворот корабля
if state.left.pressed then
ship:set_angular_speed(180)
elseif state.right.pressed then
Expand All @@ -335,6 +388,7 @@ function lilka.update(delta)
ship:set_angular_speed(0)
end

-- Стрільба
if state.a.just_pressed then
local dir_x = math.cos(math.rad(-ship.rotation))
local dir_y = math.sin(math.rad(-ship.rotation))
Expand All @@ -344,15 +398,17 @@ function lilka.update(delta)
table.insert(bullets, bullet)
end

-- Вихід з гри
if state.start.just_pressed then
util.exit()
end
end

-- Перевіряємо, чи потрібно додати новий астероїд
-- Якщо минув визначений час і якщо на екрані менше 8 астероїдів, то додаємо новий
if next_asteroid_spawn_time < util.time() and #asteroids < 8 then
local x, y
-- Spawn asteroid outside of screen bounds
-- Вибираємо випадкову точку за межами екрану
if math.random() < 0.5 then
-- Лівий або правий край екрану
x = math.random() > 0.5 and -100 or display.width + 100
Expand All @@ -362,45 +418,58 @@ function lilka.update(delta)
x = math.random(0, display.width)
y = math.random() > 0.5 and -100 or display.height + 100
end
-- Set direction towards the random point on the screen
-- Вибираємо випадкову точку в межах екрану, куди буде рухатися астероїд
local target_x = math.random(0, display.width)
local target_y = math.random(0, display.height)

-- Обчислюємо вектор швидкості астероїда
local dir_x = target_x - x
local dir_y = target_y - y
dir_x, dir_y = math.norm(dir_x, dir_y)

-- Додаємо астероїд
table.insert(asteroids, Asteroid:new(x, y, dir_x * 50, dir_y * 50))
next_asteroid_spawn_time = util.time() + math.random(1, 3)
end

-- Check for collisions
-- Перевіряємо колізії: чи корабель зіштовхнувся з астероїдом або чи куля влучила в астероїд
for _, asteroid in ipairs(asteroids) do
-- Пропускаємо мертві астероїди
if not asteroid.dead then
-- Перевіряємо кулі
for _, bullet in ipairs(bullets) do
if math.dist(asteroid.x, asteroid.y, bullet.x, bullet.y) < asteroid.sprite.width / 2 then
-- Якщо куля влучила в астероїд, то вони обидвоє вмирають
asteroid:kill()
bullet:kill()
end
end
if math.dist(asteroid.x, asteroid.y, ship.x, ship.y) < (asteroid.sprite.width / 3 + ship.width / 2) then
-- Коли корабель зіштовхується з астероїдом. Якщо корабель не мертвий, то вони обидвоє вмирають
-- Якщо гравець не мертвий, то він вмирає і гра переходить в стан GAME_OVER
if not ship.dead then
asteroid:kill()
ship:kill()
game_state = GAME_STATE.GAME_OVER
game_state = STATES.GAME_OVER
end
end
end
end
if game_state == GAME_STATE.GAME_OVER then
if game_state == STATES.GAME_OVER then
-- Якщо гравець мертвий, то він може натиснути кнопку "Старт", щоб почати гру знову
if state.start.just_pressed then
game_state = GAME_STATE.HELLO
game_state = STATES.HELLO
end
end
end
end

--
-- Цикл малювання гри
--
function lilka.draw()
if game_state == GAME_STATE.HELLO then
if game_state == STATES.HELLO then
-- Виводимо початковий екран
local banner = BANNER[math.random(1, 4)]
display.fill_screen(BLACK)
display.draw_image(
Expand All @@ -414,6 +483,7 @@ function lilka.draw()
display.height - PRESS_START.height - 32
)
else
-- Малюємо корабель, кулі та астероїди
display.fill_screen(BLACK)
ship:draw()
for _, bullet in ipairs(bullets) do
Expand All @@ -422,7 +492,8 @@ function lilka.draw()
for _, asteroid in ipairs(asteroids) do
asteroid:draw()
end
if game_state == GAME_STATE.GAME_OVER then
if game_state == STATES.GAME_OVER then
-- Якщо гравець мертвий, то виводимо повідомлення про кінець гри
display.draw_image(
YOU_ARE_DEAD,
display.width / 2 - YOU_ARE_DEAD.width / 2,
Expand Down
1 change: 0 additions & 1 deletion firmware/main/sdcard/asteroids/credits.txt

This file was deleted.

27 changes: 12 additions & 15 deletions sdk/lib/lilka/src/lilka/resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,33 @@ Image* Resources::loadImage(String filename, int32_t transparentColor) {
fread(fileinfo, 1, 40, file);
// Get number of bits per pixel (offset from start of file: 0x1C)
int bitsPerPixel = fileinfo[14] + (fileinfo[15] << 8);
uint8_t bytesPerPixel = bitsPerPixel / 8;
uint32_t width = fileinfo[4] + (fileinfo[5] << 8) + (fileinfo[6] << 16) + (fileinfo[7] << 24);
uint32_t height = fileinfo[8] + (fileinfo[9] << 8) + (fileinfo[10] << 16) + (fileinfo[11] << 24);
// int bitsPerPixel = fileinfo[14] + (fileinfo[15] << 8);
// uint16_t *pixels = new uint16_t[width * height];

if (width > 1024 || height > 1024) {
// Are you trying to load a 1 megapixel image? Are you insane? :)
// serial_err("Image too large: %s (%d x %d)\n", filename.c_str(), width, height);
serial_err("Image too large: %s (%d x %d)\n", filename.c_str(), width, height);
fclose(file);
return 0;
}

Image* image = new Image(width, height, transparentColor);
fseek(file, dataOffset, SEEK_SET);
uint8_t row[width * bytesPerPixel];
for (int y = height - 1; y >= 0; y--) {
// Read row data
fread(row, 1, width * bytesPerPixel, file);
for (int x = 0; x < width; x++) {
uint32_t color;
if (bitsPerPixel == 24) {
fread(&color, 1, 3, file);
} else if (bitsPerPixel == 32) {
fread(&color, 1, 4, file);
} else {
// TODO
serial_err("Unsupported bits per pixel: %d\n", bitsPerPixel);
fclose(file);
delete image;
return 0;
}

image->pixels[x + y * width] = display.color565((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF);
uint8_t* pixel = &row[x * bytesPerPixel];
uint8_t r = pixel[2];
uint8_t g = pixel[1];
uint8_t b = pixel[0];
display.color565();
uint16_t color = (r >> 3) | ((g >> 2) << 5) | ((b >> 3) << 11);
image->pixels[y * width + x] = color;
}
}
fclose(file);
Expand Down

0 comments on commit 01bab9f

Please sign in to comment.