Rust gamedev: bringing it all together
kellswhere GameDev DK30 Fall 2023 3 3
Description
I’m coming in with rudimentary game logic implemented and a graphics tutorial under my belt. Time to bring it all together into a prototype!
Recent Updates
Bonus week, second and final update! I’ve figured out how to raycast against the map. I spent a day and a half troubleshooting my screen-pixel-to-ray code - turns out there was an issue with the wgpu rendering tutorial that messed up my field of view. I brought in the rapier physics engine to do the actual raycast.
I was hoping to let the user build some buildings, but we didn’t quite make it. Looks like I have another borrow checker issue to deal with; hopefully this one takes less time than the last one!
Going to mark this one as completed and take some time off for the holidays.
Bonus week, first update! I’ve got a basic version of the hex map rendering:
Gotta say, it’s pretty cool to see something shaped like actual video game geometry instead of just abstract cubes and spheres. It looks a little rough without textures, shadows, or ambient occlusion, but it’s good enough for now!
Next stop is raycasting; I’ve started implementing screen-point-to-ray, and hope to have a clickable map by tomorrow. I’ve got some technical debt around propagating screen resolution info around, so this is a great excuse to spend a little time on that as well.
Ok, we made it to the end! I was planning to do some more work today, but what actually happened is that I slept a lot. Past me might have tried to spend tonight struggling to hit another goal at the last minute, but present me is going to call it a sign, relax a bit with my friends, and write this recap.
I’d call this DK30 successful. I seriously underestimated the work required to pull together even a basic graphics framework on top of the tutorial I’d done, so I didn’t even make it past my initial week 2 goal. I worked relatively consistently, though, and learned a whole lot about rendering and Rust in general. I’ve got a lot of the foundation of my game down - at least, a working version zero of it! - and I’m in a good place to start integrating the game logic.
I think what I’m going to do now is keep posting updates here for another week and see if I can actually get game logic going. I see four things to do:
- draw the map
- work out raycasts so the player can click on the map
- construct a basic UI so the player can see resources and build buildings
- set up a system to manage graphics for buildings
After that I should be able to hook in my preexisting game logic and hit my original week 2 goal of basic gameplay!
Lots of progress today!
With the help of the Rust gamedev Discord server, I figured out the tricky borrow checker issue. It all makes sense in retrospect - I had a fundamental misunderstanding that I’ve now corrected. Feels good to learn stuff!
With that knowledge I was able to fix and debug the egui rendering. I also took a stab at the code that sends window events to egui; I had to downgrade the windowing library to be compatible, but I was able to get everything working there as well.
Here’s my UI test:
Decided to skip the map for the moment and work on integrating egui into my game so I can start developing the user interface. I’ve kind of been dreading it, so, you know, time to tackle it head-on! There are two parts to handle: integrating it with windowing and with rendering. Windowing needs to pass down events, and rendering needs to display the resulting geometry.
I’ve been having a very rough time with it, though. I worked my way through some examples and source code and got something integrated, but it turns out there’s a really thorny Rust issue that I need to solve. I’ve learned a lot about Rust, but I’m still pretty new with its borrow checker, and there’s a nasty knot of lifetime stuff to disentangle for this issue. Evidently it’s a known problem.
Messed up my update schedule! Partner’s birthday celebration and the new Path of Exile league launch - should have posted something here, but at least it was time well spent. I did manage to work on this project three days of the last six, which I’m pretty happy with.
Still gonna try for it, but it seems like I might not be able to get basic gameplay by the end of the month. If I don’t make it, I’ll try to at least get UI working by then.
I’ve started work on the game proper, which is very exciting! I’ve done some work on the input systems, and I have a simple RTS camera working with zoom and pan. Super cool to have a complete loop going with rendering, user input, and game response.
Turns out there’s a simple screen recorder built into the Xbox game bar and included in Windows 10. Here’s a video:
Dunno how to get rid of the mouse pointer - apparently it needs to be on screen for the window to receive mouse scroll events.
Next stop, rendering hexes for the game map!
I have finally completed my week one goal of completing a basic rendering framework. I refactored the camera functionality and added lighting and depth testing. Lighting is still very basic: ambient and a single directional light. Enough to proceed to gameplay, though! I also added sphere-drawing code:
Looks a little sketchy without shadowing or ambient occlusion, but one thing at a time. Lighting was pretty easy to add, but I actually spent half a day trying to convince myself the results were accurate. Turns out it was correct the first time, and color spaces are just weird!
Next goals are to make a movable camera, render the game map, and learn about UI.
For this update I have successfully doubled my cube count!
I got instancing working, so I can efficiently draw multiple copies of a mesh. I’ve also made great progress with the refactor from last update, centralizing GPU resources under the Renderer
struct. The interface is sparse but clean; I can feed mesh and instance data into Renderer
and get handles back so I can reference that data later. This should be a solid foundation for building more complex features to render scenes.
I still need to handle textures. (No depth testing yet!) I also need to refactor the camera functionality to be centralized into Renderer
like meshes and instancing, and add basic lighting. Once those things are done, I’ll finally be finished with my very ambitious week 1 goal!
Motivation-wise, I’d say I was solidly mid since I returned. I got a lot done today, but my first two full days back I mostly played Factorio and messed around online. I’d like to cut myself a little slack for relaxing back into my familiar living space, but maybe not that much! I’ll treat tomorrow onward as an opportunity to improve.
Back from visiting family! I got frame limits working; my solution was pretty clunky, though, so I’d like to take another look at that sometime in the future.
I managed to make some progress on the trip home as well. I’ve started tackling instancing so I can efficiently draw multiple copies of the same mesh. I’ve also begun a small refactor, moving all GPU resources into the Renderer
struct. This gives me a centralized database of them so I can easily do things like calculate VRAM usage and avoid memory leaks on the GPU. It also allows me to perform buffer manipulations with member variables in Renderer
that I’d prefer not to expose.
I’m pleased to have made nontrivial progress over my Thanksgiving visit, and eager to get back to work in earnest. Time to take stock. We’re two days into week 3, with our week 1 goal still ahead of us. I’m going to recalibrate my goals to move my old week 2 goal to week 4: implement basic gameplay.
I ironed out some of my windowing / game loop issues; game logic updates are now happening at a silky-smooth, customizable 16 updates per second. Right now my cube is rendering at thousands of times per second, which I’d like to limit to 60. Looks like I’ll have to change how the windowing library does its thing to accomplish that, but I think I have an idea how to approach it. I’d also like to figure out more about how vsync works with the library in question to see if that offers a better approach to frame limiting.
Progress is still slow, especially given my partner was in town for the past several days to spend Thanksgiving with my family, but for the moment I’ll count it as a win to have gotten any work done.
Behold, triangles!
I’m cheating, kind of - there’s no depth buffer yet. My example only works because it’s a cube and any faces that would get depth-overwritten just get backface-culled instead. I’m saving that for later because I need to figure out an abstraction over textures before I can make a depth texture to write to. Also need to figure out some more windowing stuff, I think; resizing is not smooth, and I need to propagate the aspect ratio to the camera.
I’m with family this week so I haven’t been working too much on this project, but I can’t feel too guilty about that. I’ve made the executive decision to cool it a bit on updates while I’m here so I won’t have several in a row that are just “not much done, having fun with family”.
Partner’s family is in town for an early Thanksgiving celebration, so I’ve had less time to work. I’m also flying out for a week to visit my own family, starting Monday. I’m going to try to make a bit of progress over that visit, but we’ll see!
No triangles yet, but I’ve gotten a lot done in preparation. I’ve prototyped a simple Scene
struct with a camera, a directional light, and a single mesh. Once I get that working, I’ll add support for more complexity, with instancing and different mesh types. I’ve also hammered out part of a basic render pipeline that uses vertex color, normal, and position. Fingers crossed for a triangle next update!
I’ve decided to try doing morning updates. This avoids the pitfall where I spend the whole day thinking I’ll do just a bit more work so the update is more impressive, then push the update to the next day because it’s already too late.
Since my first update, I’ve ironed out some lingering windowing issues, implemented a game loop, and made a Mesh
class to store and manipulate mesh data. I’ve also added some basic rendering code to clear to a dark screen.
Next steps are to make Mesh
export to some GPU buffer struct, implement camera, lighting, and texture structs, and make some actual draw calls! Hopefully next update will contain an image with some triangles; I’m going to set the stage now by showcasing my empty window:
And before I could finish this to post, I got sucked into a whirlwind of other tasks for the day! Looks like it won’t be a morning post. Got vaccinated again, though, and got in an 8k walk! I’ll take it.
I’m going to kick this off with an expanded description of my project and goals.
I’m building a colony defense game in the tradition of They Are Billions. Since my last gamedev DK30, I’ve moved from an irregular grid with several buildings per tile to a hex grid where most buildings are several tiles. I’ve also started learning Rust so I can move out of the Unity ecosystem.
I had basic income and building mechanics working in Unity before I switched; my primary goal this DK30 is to recover that level of progress in Rust. I’m coming into this with the game logic part already written and a tutorial for the wgpu low-level rendering library completed. To get the prototype up and running, I’ll need to build a rendering system on top of wgpu, learn physics and UI libraries in Rust, and wrap it all together to interface with my game logic systems.
I’ve set an ambitious goal of completing the basic prototype during the first two weeks. It would be great to hit that deadline, but I won’t sweat it if I have to go longer! If I do have time after the prototype, I’ve set out some fun things to explore in my goals for weeks 3 and 4.
Finally, I’d like to be intentional about how I approach this from a motivation perspective. Using what I’ve learned in my past DK30s, I’m setting these additional goals for myself:
- I will try to make at least three updates per week.
- If I find myself avoiding this project because I’ve encountered a difficult part, I’ll try to work on a different part for a little while instead.
Can’t wait to get started and see everyone else’s projects. Good luck everyone!
Estimated Timeframe
Nov 14th - Dec 14th
Week 1 Goal
Refactor the rendering code from the wgpu tutorial into a graphics framework.
Week 2 Goal
Learn physics and UI libraries and implement basic gameplay.
Week 3 Goal
Add a feature. Possible choices:
- add gameplay: roads and services
- procedural plants
- procedural buildings
- Wasm support
Week 4 Goal
Add another feature. Possible choices:
- add gameplay: roads and services
- procedural plants
- procedural buildings
- Wasm support