Clicky

  • Journey or Destination

    Everybody in India was seemingly learning to be a software engineer in the 2000’s. They were super expensive, made a lot of money and commanded respect. Then the recession hit. Now, software engineers were a dime a dozen and begging for jobs.

    Ring a bell?

    I remember having mixed feelings when coding bootcamps were springing up everywhere. People were paying thousands to go on a six week bootcamp to learn how to code. I was happy that coding was becoming accessible, but I was not happy that people were picking this career path purely as a way of making money.

    The same thing had happened in India - the popularity of software engineering had stemmed from the massive amounts of outsourcing that was happening. It was a lucrative career option. There was no love for the job, no curiosity about learning. It was a job.

    Trying to hire a software engineer in 2021/2022 was an absolute nightmare. Salaries were skyrocketing and engineers were rare. Good engineers were rarer. The bootcamps were going hell for leather at this time, and then the layoffs hit. Tens of thousands of jobs lost, along with all the new graduates who entered the market. On top of that, add the promise of A.I being able to write code. It can probably write code as well as some of the bootcamp graduates.

    There was a time that the majority of software was written by people who loved the work. It was treated more as a craft and we were all figuring out how to do it better. This craft then got commercialised, industrialised.

    The focus is now on productivity, and how quickly we can get code out. The love of the work has been lost. Let A.I have that joyless (thankless) job! I (We?) don’t want it.

    I’d rather be the last carpenter who still enjoys the job! What about you?

  • Basic Gameplay

    The first goal was to get it working as far as the coding challenge itself. There is a companion vlog for this post

    The Ship

    Spawning the ship itself was relatively straightforward. I only needed three points and drawTriangle from raylib.

    In the coding challenge, the ship was rotated with the keyboard, but I wanted the ship to point to the mouse so that aiming was straightforward. Rotating to the mouse was trickier — it involved atan2 and ChatGPT got me started.

    The coding challenge video did not worry about time lapsed between each frame, but I wanted triangle to be framerate independent. That involved a bit of jiggery pokery to get working, including determining how much the ship can move within a time frame.

    Moving the ship was a bit easier, but based on the many videos of the Coding Train that implemented force, velocity and dampening, it was pretty straightforward. Dampening took a bit of trial and error. ChatGPT sent me down the garden path initially with an over-complicated formula, but I was able to simplify it later.

    Integrating the physics for linear momentum with the one for rotational momentum was also quite nice — it meant that the ship could overshoot the aim when rotating and will correct back.

    The Asteroids

    The coding challenge worked with a fixed number of on screen asteroids and they wrapped around. I needed to expand this to:

    • A potentially infinite vertical scroller.
    • Collisions between the asteroids (currently the same as from the challenge, only checks full circle collision)
    • Figure out how to handle asteroids moving out of the screen

    The above will be covered in a bit more depth in the next devlog.

    The Camera

    I also needed a follow camera. Unlike the original asteroids, the ship can move up/down and the camera should follow it.

    The current code looks something like this:

    /// ship_y: ship's current y position
    /// ship_vy: ship's y velocity
    /// dt: time elapsed since last update
    /// margin: the zone out of which camera is moved
    /// speed: maximum camera movement speed
    fn updateCameraY(self: *Self, ship_y: f32, ship_vy: f32, dt: f32, margin: f32, speed: f32) void {
        const cam_y = self.camera.target.y;
        const inner_margin = margin * 0.8;
    
        // Predict future ship position
        // TODO: at high speeds, when the clamp goes off, the camera does a little shuffle
        // It would be nice to fix that
        const screen_height: f32 = @floatFromInt(self.screen.height);
        const predicted_y = ship_y + std.math.clamp(ship_vy * 30.0, -screen_height, screen_height);
    
        var target_y = self.camera.target.y;
        var target_clr: rl.Color = .red;
    
        const tmargin = cam_y - inner_margin;
        const bmargin = cam_y + inner_margin;
        if (predicted_y < tmargin) {
            target_y = predicted_y + margin;
            target_clr = .blue;
        } else if (predicted_y > bmargin) {
            target_y = predicted_y - margin;
            target_clr = .green;
        }
    
        if (target_y > screen_height / 2) target_y = screen_height / 2;
        const t = std.math.clamp(dt * speed, 0.0, 1.0);
        self.camera.target.y = lerp(cam_y, target_y, t);
    }
    

    It predicts the position of the ship, based on its y velocity. If that’s outside the middle zone, it’ll move the camera at up to the maximum speed. There is some clamping to prevent things flying off at high speeds. It also prevents the camera from overtaking the ship.

    This code is mostly hacked together with some help from ChatGPT. It’ll be cleaned up later, once I have a better indication of possible ship speeds, and how/if we’ll zoom in and out as well.

    Another important facet to consider with the camera was what to do with the edges. Out of the four edges, only one is infinite.

    The current solution is that the camera stops tracking when it gets to the “bottom.” It also does not move to the left or right. The ship, on the other hand is free to move out of the range of the camera. Currently, nothing changes except that the camera does not follow.

    Since the thrust works in the direction of the mouse, it’s pretty easy to bring the ship back on to the screen. This feels like the world is big and still out there, we just don’t track what happens out there.

    I considered wrapping around on the left and right, but that felt more like the ship was trapped in that zone. I want the feeling of being trapped in vengeance to be more subtle ;)

    Combat

    Combat is pretty much a mirror image of what happens in the coding challenge, though I didn’t have some of the helper functions. I learned some math :)

    Initially, the update loop only handled asteroid collisions. I added bullets as a separate field in the update loop. It then checks each bullet with every asteroid in the active chunks (covered in devlog #2).

    If there is collision, the asteroid is split into two, moved apart a bit, and given opposite linear momentum. The bullet also takes “damage” at this point, and can be removed. The damage system is designed to support penetration, which is currently not active.

    If the spawned asteroids would be too small, nothing is spawned. Later on, this would be the trigger to spawn a material drop.

    Zig / Raylib

    Learning zig and raylib was a big part of this. Fortunately, both were a lot of fun to learn and work with.

    One thing I found annoying was that the vector functionality in raylib was scattered around as individual functions instead of on the vector struct. While this was understandable, with raylib being written in C, I found it a bit frustrating.

    I ended up writing my own Vector struct in zig and the functions that I used as methods on that struct. It was an opportunity for me to learn some vector math as well.

    I also encapsulated raylib inside a Canvas struct. I probably still have some rl. calls other places in the code, but the idea is that a canvas is passed into any bits of code that needs it. The main help is that it’ll convert our version of Vector2 to the one that raylib wants.

    I am also thinking about how I want input handling to work. I would like to encapsulate that into a separate struct. Right now, I mainly access raylib directly.

    Manual testing

    While I am a big proponent and fan of Test Driven Development, I was happy enough with manual testing for triangle. I found joy in seeing it work each time I manage to get something working.

    I do end up writing tests later, particularly when I got to bits of code that was harder to test manually.

    Feel

    triangle already feels fun and light. There are some clunky elements to be ironed out, but so far, it feels good :)

    You can watch the video devlog on youtube

    Other posts

  • A lone triangle vs the universe

    The Context

    I’ve been unwell for a few months which has left me with a great deal of fatigue and limited ability to be productive.

    One of the activities to pass time while healing was playing a lot of ARPG’s recently, particularly PoE 2 and the Last Epoch. I’d even had a few ideas about how I might do things a little differently. Since my brain capacity was pretty low, these games with their repetitive nature and easy dopamine hits were really enjoyable.

    I found the gameplay a bit fragmented though, and having to follow guides was annoying. I had been mulling over the idea of making something which flows, and supports your decision making. It’s not about giving the player answers, but about providing the player the most relevant information in an easy to digest format. They still get to make the choices. I struggled to identify what mattered - guides were easier.

    I did wonder if there was an easy way to just say - “use this build from this guide” so that it would save me from all the tedious clicking :/

    All this left me wondering if I could build something that captured the fun while keeping you in the flow. I even looked at how complicated it would be to make an isometric game. Too complicated for me was the answer.

    Factory games, one of my other favourite genres felt a little too mentally taxing. Otherwise, I would have dived into Space Age.

    I have also been bingeing on youtube videos and discovered a fantastic channel - the Coding Train.

    I’ve been almost religiously watching the Coding Challenges playlist and I have been loving it. I love how he makes 2d graphics programming seems so easy and approachable. I used to be so intimidated by all the math.

    There was a slow confidence building in me that maybe - just maybe I could build a game.

    Coding Challenge 46

    Last night, I was watching the one about remaking Asteroids. He made it look so easy and fun. How he puts those asteroids together felt so clever - neat! I could do that. Actually, I could make that really interesting.

    For a little while now, I’ve been wanting to try out one of the challenges, tied in with playing with zig, which has also been on my radar. I’ve largely worked with higher level languages like Java (and golang more recently). I’ve always been fascinated by C, and C++, but never quite got into it. Zig represented a more fun and modern version of C that I could enjoy. I also really liked how explicit and simple zig feels.

    It feels like a perfect opportunity to feed two birds with one scone. I could try and replicate what he did in that video in zig.

    A little bit of research on graphics/game libraries in zig revealed raylib to be the best candidate for my needs. While a C library, it has zig bindings, there is plenty of documentation, albeit not necessarily in zig - but that was ok.

    I could see it in my mind - a field of asteroids, pew pew pew, asteroids break apart. Destroyed asteroids would drop resources. Oh you could have factories on the ship and build your armoury - weapons, armour, scanners - everything. That would be an interesting twist - you don’t pick up loot - you build it.

    Each item would still have randomised mods, but you could extract them like in Last Epoch and use them in other ones - I have a lot of ideas.

    Story

    In terms of the story, I’ve been pondering the destructive nature of capitalism a lot lately and that felt like a good metaphor to bring in here - it just connected.

    The planet where the triangle belongs has been destroyed by a corporation - not necessarily maliciously, but in a bid to extract it of all valuable resource.

    I have this image in my mind of something called a “Mill” that the planet is put through. The remnants create an asteroid field in which you find yourself.

    The game essentially starts off with seeking vengeance. Victory - at least over the corporation - is not possible. You can get stronger, much stronger, and you can get to a place where you can beat the waves with relative ease - but the waves never end!

    I have been pondering a softer ending, a choice that the player can make, when they’ve had enough - but I’m reluctant to give that away.

    On pondering this. I realised this could also be a quiet metaphor for depression. Having been through it in my life, I could feel a connection. What if the triangle is indestructible?

    Actually, that would improve the flow - no game over screen, and no respawn. All the machinery and installations on the ship could be destroyed, but the core of the ship - that always remains. You can always keep fighting.

    Gameplay

    Scavenge

    Starting off in an asteroid field, you’re able to scavenge materials. The ship starts off with a smelter, a factory, and some basic power. You can refine materials and construct buildings to get stronger.

    Each sector will get more difficult. I thought about being able to travel vertically and horizontally, but in the end, I decided to make it a vertical scroller. A two dimensional map might be more interesting, but it also brings annoying choices, particularly in terms of wondering whether you should have gone another way.

    By putting the game on some form of rails, you have a smaller context to think about - and you can focus on fun. I am reminded of Raptor: Call of the Shadows, a game that I loved in the 90’s.

    Raptor had shops, but I want to do a super simplified factory aspect. Every upgrade should feel earned — constructed, not found. I want to feel that progress is made entirely by the actions of the player.

    There will still be randomisation, but it will be limited to the mods that are spawned on the items that are constructed. The player gets to decide what items to build.

    I want the crafting to be as deterministic as possible, but while also keeping it interesting.

    Figuring it out

    There are no (traditional) tutorials or help sections. This decision will put a lot of pressure on getting the UI/UX intuitive and easy to use. However, it will also help maintain flow and keep every bit of progress feeling earned.

    Items will have descriptions and details so that the user can make choices, but it is about experimentation and exploration. I would also like a component, something like a computer that can be installed. The idea is that this can do analysis of items and mods and give you details of clear and easy to understand benefits and disadvantages. Not just what attributes change, but how it will impact gameplay. I can’t be perfect, but it must be accurate, and be able to provide enough information to be able to decide whether it’s worth a try.

    The intention is that this computer can act a bit like the guide that you would otherwise google.

    Each sector increases difficulty, but is infinite in itself. After traveling for a bit, you will be offered an exit. If you keep going, exits will be offered at regular intervals - but you can just keep going.

    You are dropped into a universe and every step is up to you to figure it out.

    Aesthetic

    I can hear the synthwave tracks that play in the background already (it could be because I’ve been listening to a lot of synthwave recently). It feels right and thematic to the original Asteroids.

    The graphics, at least initially is going to be simple and shape based, again thematic. I’ve been pondering whether and how the graphics would get updated as you get through the sectors. The start of it is going to be black and white with little hints of colour. As you progress, you’ll get more and more colour and the vibrancy increases, sometimes in leaps and bounds. It could be a metaphor for the journey of recovery.

    The sound effects should also have a strong 80s vibe. I want it to feel like you’ve been transported to the 80s but if it happened in 2025.

    triangle

    As a name, at least for the time being, I’ve settled on triangle. It is a passion project - I have a vision and a strong idea of how I want to feel playing it.

    At its heart, triangle is about (re)building - a game where every piece of progress feels personal.

    I would like to share it with the world and am looking for feedback, thoughts ideas and supporters.

    If the idea or any of the concepts resonate with you, I’d love to hear from you.

    Other Posts

    Updates

    2025-05-31: Added devlog #0 on youtube
    2025-05-21: Added an itch.io page

  • Going fast vs Going far

    Aesop wrote us wonderful and valuable fables. Almost all of us know the one about the tortoise and the hare - that slow and steady wins the race.

    There is a quote by Mario Andretti:

    If everything seems under control, you’re not going fast enough.

    This one clearly embodies the hare, but is it a good attitude to have in all work, or in general life?

    We all seem to place so much emphasis on speed, it seems like we are trying race through life. Aren’t we all going to the same place?

    I think the quote (of unclear origin)

    If you want to go fast, go alone; if you want to go far, go together?

    is a good comparison of our options, and while it might be nice(r) to go far, I think there is a lot more value in traveling together.

    We are born alone, and we die alone. We do not have choice in those, but why do so many of us go alone, just to go fast?

  • Starting Again

    Most of you know that I ran my own company for fifteen years, and at one point it had around 30 people in five different teams. It was without a doubt the most difficult job I’ve ever done by a long long way. It was also the most rewarding and fulfilling.

    Truth be told - it also very nearly killed me! I had a breakdown which left me an emotional and mental wreck, as if I’d been hit by a bus (10 points if you tell me which one!)

    It has taken me the better part of 15 years to recover from that, and in the meantime I tried working for others and I enjoyed it. In the end, though, I think once you have run your own company for a while, it is incredibly difficult to do anything else. It was inevitable that if I ever got well enough, that I would want to start something of my own again.

    It was a long and hard journey, but I am finally better, and I am ready to start again. I am taking all the hard earned lessons into this venture, and I want to do it even more differently this time.

    I promised myself that this time around, I would:

    • Surround myself with people who will challenge me and expand my perspective.
    • Accept the fact that I do not have all the answers
    • Do things the way that we are meant to:
      • Bring in expertise we do not have
      • Have someone in the team right from the start focused on marketing
      • Technically, BDD, TDD, Agile, Lean, ““The only way to go fast is to go well””
    • Focus on people first
    • Focus on the journey, not the destination

    One thing I realised as part of this journey is that doing things differently means that all the tools, technologies, and processes out there don’t quite fit.

    With #muster, we are starting with one of the easier parts, and streamlining the developer experience. We are ending up building a lot of other things in the process, but they all take longer.

    With choosing to do things in a very different way, we don’t know exactly how it’ll all pan out, but it will an interesting journey