In case you don’t know, back in 2021 travelers in China would sometimes get quarantined for possible contact with COVID carriers. During the quarantine we need to stay in a designated hotel or something for two weeks.
Harujion is a small 2d engine I made during quarantine (because I got nothing else to do). It’s heavily inspired by Love2D.
I’m working on a similar project with one of my friend called amore2d. It’s not ready for release yet, but now that I’m much more familiar with Lua APIs, I made a much better Lua-C++ interop layer.
Features
- Modern OpenGL 2D Renderer (4.5 Core Profile, Direct State Access)
- Uses LuaJIT instead of vanilla Lua
- Lua Scripting Support
- Window
- Keyboard
- Mouse
- Renderer
- Audio
- Built-in common 2D graphics objects
Demo video: Flappy Bird
Assets are from this repo.
Sample Lua Code: Player script for the Flappy Bird game
This is a simple piece of code I used in the Flappy Bird game. It’s mainly for demo purpose so there’s nothing fancy in it, but it works well.
-- load classic.lua for Lua OOP
local Object = require("classic.lua")
-- declare the Player class
local Player = Object:extend()
-- Player constructor
function Player:new()
-- load the animation sprites
self.sprites = {
haru.Sprite.new("flappy/yellowbird-midflap.png", 32),
haru.Sprite.new("flappy/yellowbird-downflap.png", 32),
haru.Sprite.new("flappy/yellowbird-midflap.png", 32),
haru.Sprite.new("flappy/yellowbird-upflap.png", 32)
}
self.v = 0.0 -- vertical velocity
self.y = 0.0 -- vertical position
self.sprite_timer = 0.0 -- animation timer
self.sprite_idx = 1 -- current animation frame
end
function Player:jump()
-- jumping is simply changing the vertical velocity
self.v = 5.0
end
function Player:update(deltaTime)
-- update velocity and position
self.v = self.v - 10 * deltaTime
self.y = self.y + self.v * deltaTime
-- update sprite frame animation
self.sprite_timer = self.sprite_timer + deltaTime
if self.sprite_timer > 0.1 then -- 0.1s per animation frame
self.sprite_timer = self.sprite_timer - 0.1
self.sprite_idx = self.sprite_idx + 1 -- show next frame
if self.sprite_idx == 5 then -- wrap back to the first frame
self.sprite_idx = 1
end
end
end
function Player:draw()
-- draw the sprite and rotate it based on velocity
self.sprites[self.sprite_idx]:draw(0.0, self.y, self.v * 0.1)
end
return Player
Lua APIs
I loosely followed the Love2D API (which is basically a wrapper around SDL and SDL_Renderer).
Window
haru.window.close(): Close the windowharu.window.getFramebufferSize(): Get the framebuffer sizeharu.window.setTitle(title): Change the window title
Keyboard
haru.keyboard.pressed(key): Check if a key is being pressedharu.keyboard.justPressed(key): Check if a key is pressed in this frameharu.keyboard.justReleased(key): Check if a key is released in this frame
Mouse
haru.mouse.pressed(button): Check if a mouse button is being pressedharu.mouse.justPressed(button): Check if a mouse button is pressed in this frameharu.mouse.justReleased(button): Check if a mouse button is released in this frameharu.mouse.normalizedPosition(): Get mouse normalized position (0.0 ~ 1.0)haru.mouse.setCursor(enable): Enable or hide the cursor
Renderer
haru.renderer.setMatrixOrtho(left, right, bottom, top): Set the camera matrixharu.renderer.setClearColor(r, g, b[, a]): Change the renderer clear colorharu.renderer.setDrawColor(r, g, b[, a]): Change the renderer draw colorharu.renderer.drawPoint(x, y[, size]): Draw a pointharu.renderer.drawLine(x0, y0, x1, y1[, width]): Draw a lineharu.renderer.drawRect(x0, y0, x1, y1[, width]): Draw a rect (only the outline)haru.renderer.fillRect(x0, y0, x1, y1): Fill a rect with draw color
Audio
haru.audio.loadBank(filename): Load a FMOD audio bankharu.audio.setVolume(volume): Change the global volumeharu.audio.getEventDescription(eventPath): Get the event description for a FMOD eventharu.audio.fireOneShotEvent(event): Play a one-shot FMOD event
Built-in Lua Objects (Texture, Sprite, SpriteFont, Tileset)
Texture
haru.Texture.load(filename, filter, clamp, mipmap): Load a texture (returns a texture object)haru.Texture:getSize(): Get the texture size
Sprite
haru.Sprite.new(texture): Create a sprite from a textureharu.Sprite:setPixelRect(x, y, w, h): Set the sprite to use a rect on a texture atlas (in pixels)haru.Sprite:setPixelPivot(x, y): Set the sprite pivot (in pixels)haru.Sprite:setFlip(x, y): Set the sprite to flip horizontally or verticallyharu.Sprite:draw(x, y, rotation, scaleX, scaleY): Draw the sprite
SpriteFont
haru.SpriteFont.new(fontFilename): Load a sprite font (bitmap font)haru.SpriteFont:getGlyphPixelSize(): Get the size of one glyph in the bitmap fontharu.SpriteFont:draw(x, y, text, size): Draw text using the sprite font
Tileset
haru.Tileset.new(texture, tileWidth, tileHeight, spacing): Load a tilesetharu.Tileset:getTileSize(): Get the size of one tile in the tilesetharu.Tileset:draw(index, x, y): Draw theindexth tile in the tileset