I'm making a small game with SDL and Lua #4
Replies: 6 comments 7 replies
-
Day 1-2. Window, sprite moving aroundStarting... The current plan is to port Ebiten Breakout and see how much code it'll take. Then I'll make something new. Dependenciesthird_party dir looks like this: third_party/
├── imgui
├── lua
├── SDL
├── SDL_image
├── sol2
└── CMakeLists.txt Things I need to add:
Current progressThe game looks like this: Lua script looks like this: local sprite
local x = 100
local y = 100
local speed = 600
function init(texture)
sprite = Sprite.new(texture)
sprite:setTextureRect(0, 0, 16, 16)
end
function update(dt)
local dx = 0
local dy = 0
if isKeyPressed(UP) then
dy = dy - 1
end
if isKeyPressed(DOWN) then
dy = dy + 1
end
if isKeyPressed(LEFT) then
dx = dx - 1
end
if isKeyPressed(RIGHT) then
dx = dx + 1
end
x = x + speed * dx * dt
y = y + speed * dy * dt
sprite:setPosition(x, y)
end
function draw()
drawSprite(sprite)
end So, I got the basic "sprite" class and can check if a keyboard key was pressed or not. Cool! GraphicsI'm using SDL_Renderer for rendering for now. I want shader and 3D graphics support, so I'm thinking of trying out something from this set:
I have great hopes for this - hope I'll be able to use it in the future to not think about OpenGL/Vulkan/Metal etc. ever again. I wish software rendering was a viable option since I don't want to make a AAA game - the PCs of today are powerful enough to handle it, but I'll have to do quite a lot of things that GPU does for you manually, so it might be worse than using OpenGL. Stats
|
Beta Was this translation helpful? Give feedback.
-
Day 3-4 Working game!This game is almost ported. Stuff to port:
I'm reusing "Go themed" sprites for now, but will redraw them to be "Lua, C++ themed" soon. The ball will be Lua's symbol, of course. But what should the paddle and blocks look like? Hmm... Stats:
OK, it was easy. I'm porting this game to be sure that I can make something "complete" and not get stuck worrying about the small stuff. As your can see, I now have more Lua code than C++ code. Isn't it great? For now, the C++ parts that Lua code calls is pretty minimal:
I think I might simplify local s = sprite.New("some_texture.png")
s:setTextureRect(32, 32, 16, 16) I know about techniques like batching textures into one mega-texture (like Ebitengine does), but for now I don't want to go that route and focus on other things - I can optimize it later if I see the need to do so. And if I decide to go with software rendering for fun, I can even replace I think that software rendering can work out pretty well for the 2D games I want to make:
The only shader I want to have is a CRT shader - but I can do it in OpenGL after I've rendered everything I want to be present on the screen to a texture. So, full screen effects will be optional and won't make "the base* rendering engine harder than it needs to be. If I decide to do 3D (and I might!), I'll totally reuse my OpenGL rendering code for now. I've found g3d to be inspiring and might steal some API ideas from it. I also learned that SDL's Switch port works even if you write stuff with OpenGl (source), so that's pretty awesome. But for the present time, I want to focus on the fun parts - tile maps, NPCs and dialogues, cutscenes, etc. So, gameplay/stuff that every game needs first, graphics and niceties second. :) |
Beta Was this translation helpful? Give feedback.
-
Off-topic. Writing code in Lua vs GoFor now, I'm suffering a bit, to be honest. Writing This stuff is also frustrating: -- script.lua
local function f()
g(5) -- error: g is nil
local function g(x)
print(x)
end Lua is a one-pass compiler, so if it doesn't see "g" at first, it assumes that it's a global variable. To fix it, you need to write this: -- script.lua
local g
local function f()
g(5) -- error: g is nil
g = function(x)
print(x)
end ... not as good as Go, where you can define functions everywhere you want... Plus, it breaks the pattern which I've grown to love: func f() {
f1()
f2()
f3()
}
func f1() { ... }
func f2() { ... }
func f3() { ... } So, you read the source from top to bottom. First, you read the higher level functions, then you scroll down and read more "low level" functions. Lua makes it a bit harder to write. Of course, you can "fix" it by using "OOP-like" approach, e.g.: local Game = {}
function Game:init()
self:spawnObjects()
end
function Game:spawnObjects()
end ... and it sort of works. But it makes writing free functions a bit harder. Oh well. Then, you have other problems of dynamic languages
local pos = { x = 20, y = 30 }
local newPos = pos
newPos.x = 30 -- oops, "pos" changed too! With Go, it wasn't like this - every struct is allocated on stack by default and so when you do I think I'll rant more in the coming days, but I hope that I forget the "Go" stuff (since I won't be writing much of it anymore) and rewire my brain to Lua. It's not all gloom, of course. Not writing types and being able to easily "extend" anything by just doing I'm also reading "Programming in Lua" again and it's so great. No filler, just the stuff you need to know. |
Beta Was this translation helpful? Give feedback.
-
Day 5-8 First game - doneAnd here it is! Here it is: https://github.com/eliasdaler/sdl_breakout This is a port of my old test game, Ebiten Breakout. It might be small, but I'm sure it might be useful to some people. :) Final stats (excluding CMake)
The similar game which I wrote in Ebitengine and Go is 834 lines of code. Considering that Ebitengine is much higher level than SDL, that's not too bad. A lot of interesting things have happened since I last posted. Engine / game separationThe game is now fully separated from the engine (I call it "edge"). The game is fully written in Lua. The engine's C++ API is pretty simple. Here it is: -- graphics
edge.loadTexture(path) -- loads a texture
edge.loadFont(path, size) -- loads a TTF font
drawableString = edge.newDrawableString(font, text) -- creates a text string which can be drawn
drawableString:draw(x,y,r,g,b) -- draws a string with given color
sprite = edge.newSprite(texture) -- creates a sprite
sprite:setTextureRect(x,y,w,h) -- sets a part of texture to be displayed
sprite:getTextureRect() -- returns a texture rect
sprite:draw(x,y)
-- sound
sound = edge.loadSound(path) -- loads a sound
sound:play() -- plays a sound
music = edge.loadMusic(path) -- loads music (in .ogg)
music:play() -- plays music
music:stop() -- stop music
-- input
edge.isKeyPressed(key) -- checks is keyboard key is pressed Here are some thoughts on this API design.
Speaking of Love2D... I wonder why they chose to separate things by modules like The engine also has some parts writte in in Lua. They're stored in "edge/src/Lua" - I feel like it's nice to write "middle level" stuff like this. This is where FSMs, event manager, ECS and other things will go in the future. LauncherTheoretically, each game doesn't need to have a specific launcher. It can just execute "scripts/game.lua" and that's where all the game code will start. However, I found it difficult to make a CMake target which will work like this and properly link to stuff. I will work on it later. Love2D also has this interesting feature, which allows you to "zip" all your resources and scripts, and then just "cat" them with a Love2D's binary to get a runnable executable. This is great and simple, I might look into it. Being able to make a "single file" binary for your entire game sounds pretty neat (not that great for modding, though!) CMakeYou will notice that this game has tons of CMake code. This is caused by the following:
You will notice that I use symlinks during build step. I've found this much better than copying scripts/assets to build directory, because you'll never accidentally modify a script inside the build dir (instead of source dir, as you probably want). I still didn't manage dynamic linking to work on Windows. You also need to copy DLLs to install/build dir there. I'll work on it later. Asserts are greatAnother great thing I've figured out is this pattern: self.font = assert(edge.loadFont("res/font.ttf", 8))
This means, I don't need to write code like this: local msg
self.font, msg = edge.loadFont("res/font.ttf", 8)
if not self.font then
error(msg)
end Doing this in one of code is beatiful. And I can also catch this error on Lua side if I don't want it to "escape" to C++.
As you can see, the error message also shows me the line on which the error has occured. Nice! C?Theoretically, all the C++ code could be ported to C. I'll need to get rid of sol2 binding code and write a lot of Lua C API code. I don't want to do it, so I'll use it for now. But it's interesting how the breakout itself can now even be ported to Love2D's code without rewriting much of it (you only need to write "glue" code) Future workThe next thing will be much bigger. I want to make a small top-down adventure game with tiles, NPCs, dialogue etc. I'll also make a small level editor. This time in Lua (I had a 3k+ LoC of C++ code for level editor in my previous engine). I'll also stop using See you later! |
Beta Was this translation helpful? Give feedback.
-
Next game: small tile-based top-down game + level editorSorry for the lack of updates recently. I recently ported my graphics code from SDL_Renderer to OpenGL. I used my old code from my previous games (from 2019-2020) and turns out it was pretty good, because I was able to quickly adapt it. Mainly I just used it to do simple sprites/quad buffers. Here's what I have now (notice the nice CRT shader, can't do that with SDL_Renderer!): Watch windows (wow, you can even embed videos here!) watch_window.mp4Things I learned about Lua recently.
local M = {}
function M:doSomething()
...
end
function M:doAnother()
M.doSomething()
end
return M This solves the "locals" problem I was talking about previously.
What I'm doing nowMostly boring stuff. Need to rewrite other "basic" things - level editor stuff, proper input management (screaming internally), basic collision detection, etc.-etc. I might go a bit quite for a while since it's mostly nothing unusual or ground breaking. If you want to hear about some of this, though, let me know! :) |
Beta Was this translation helpful? Give feedback.
-
As you can tell, the experiment is over and I've been making a bigger 3D game in C++ for a few months now: |
Beta Was this translation helpful? Give feedback.
-
Okay, trying something new. It'll be easy for me to write "throw away" posts here and not pollute my main website.
Then I might compile it into a proper article, but it will take some time. You can subscribe to this discussion to get new updates! Neat.
My previous Lua/C++ games had something like this:
Now I want to make something closer to LOVE2D - it'll be a wrapper around SDL/OpenGL and most of the code will be written in Lua to not deal with C++ nonsense.
For example, in my previous games ECS was implemented on C++ side, so you had to do a lot of bindings to create new components/systems in Lua. Now I want to implement the whole ECS thing in Lua:
I think I'll open-source it, if something good comes out of it.
At least I'll document my findings in a "blog" format.
Please post comments to each post as a reply, not as a new post in this thread. :)
P.S. Making stuff from "almost" scratch is supposed to be easy. But it isn't, and we need to think why it is so and how this can be changed without using full-blown game engines.
Beta Was this translation helpful? Give feedback.
All reactions