<![CDATA[Carrot Games]]> http://community.carrot-games.com Smartfeed extension for phpBB <![CDATA[General :: RSS feed now available :: Author celes]]> 2026-03-10T14:44:25+00:00 2026-03-10T14:44:25+00:00 http://community.carrot-games.com/viewtopic.php?f=2&t=27&p=55#p55 Attention everyone! :blobcatrss:

I've installed an extension so that anyone can subscribe to forums or subforums in a fine-grained way using your RSS reader of choice! You can find it at the top of the page, under the Smartfeed menu next to quick links
image.png
RSS is about as out of fashion as a phpbb forum is, which makes this the perfect fit! :blobfoxthinksmart:

The extension has tons of options and not all the defaults suited me, so I'd say take a moment and read through the options before you generate the feed!

Attachments


image.png (47.85 KiB)

]]>
Attention everyone! :blobcatrss:

I've installed an extension so that anyone can subscribe to forums or subforums in a fine-grained way using your RSS reader of choice! You can find it at the top of the page, under the Smartfeed menu next to quick links
image.png
RSS is about as out of fashion as a phpbb forum is, which makes this the perfect fit! :blobfoxthinksmart:

The extension has tons of options and not all the defaults suited me, so I'd say take a moment and read through the options before you generate the feed!

Attachments


image.png (47.85 KiB)

]]>
<![CDATA[Iris Blade :: New round of playtest :: Author celes]]> 2026-02-28T09:07:59+00:00 2026-02-28T09:07:59+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=21&p=41#p41

After the latest improvements, I'm opening up a new small round of Iris Blade playtest. I'm looking for about 2-3 more people who can tell me about their first-time experience of the game.

As a general reminder, the game is currently a "vertical slice" of the full experience. The goal is to make sure the core mechanics are solid and the balancing is good before we move on and add a ton more content. In the final version you can expect more of everything: Items, map events, bosses, enemies, metaprogression... As wel as a bit of story and dialogues, Carrot Survivors style.

But for now, the main things I need y'all to look at are:
  • User experience: Anything that's uncomfortable or not obvious from playing the game alone should be reported as an issue.
  • Balancing: The game aims to be approachable and not overly challenging. But we need to know how you feel about it.
  • Map design: The map design is on its first iteration. We want to learn as much as possible about it.
As always, any other thoughts are welcome but feedback in the form of: "all three bosses are the same" or "there's lots of enemy recolors" are not needed, as we're well aware of it. :blobcatnodfast:

With that said! Anyone who wants a first row seat, please get in touch with me: Either through here, the fediverse, discord or anywhere you know about

After you've played one or two rounds of the game, I'd very much appreciate it if you could fill up the feedback survey. You can find a link to it in the game's pause menu. Sending your feedback through other channels is also okay.]]>


After the latest improvements, I'm opening up a new small round of Iris Blade playtest. I'm looking for about 2-3 more people who can tell me about their first-time experience of the game.

As a general reminder, the game is currently a "vertical slice" of the full experience. The goal is to make sure the core mechanics are solid and the balancing is good before we move on and add a ton more content. In the final version you can expect more of everything: Items, map events, bosses, enemies, metaprogression... As wel as a bit of story and dialogues, Carrot Survivors style.

But for now, the main things I need y'all to look at are:
  • User experience: Anything that's uncomfortable or not obvious from playing the game alone should be reported as an issue.
  • Balancing: The game aims to be approachable and not overly challenging. But we need to know how you feel about it.
  • Map design: The map design is on its first iteration. We want to learn as much as possible about it.
As always, any other thoughts are welcome but feedback in the form of: "all three bosses are the same" or "there's lots of enemy recolors" are not needed, as we're well aware of it. :blobcatnodfast:

With that said! Anyone who wants a first row seat, please get in touch with me: Either through here, the fediverse, discord or anywhere you know about

After you've played one or two rounds of the game, I'd very much appreciate it if you could fill up the feedback survey. You can find a link to it in the game's pause menu. Sending your feedback through other channels is also okay.]]>
<![CDATA[Iris Blade :: Capsule Art! :: Author celes]]> 2026-03-01T15:53:21+00:00 2026-03-01T15:53:21+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=22&p=42#p42 :neodeer_aww: :blobPikaAww:

This was made by Carrot Games' chief capsule artist, Kali (you can find her on instagram @Kalisdr_art)! She's worked art for other games and I've been told commissions are open! 👀
HeaderCapsule.png
VerticalCapsule.png
For those who don't know, the "capsule" is what steam calls the graphical pictures that appear attached to a game in the shop, the library and other various places. Other storefronts like itch have a similar concept, and it's the closest thing to the "box art" for digital releases.

Even though the game is pixel art, the laws of the biz demand games use illustrations for this kind of promotional material, and not in-game art. This has been a thing since the old days, and I guess it's become part of the public subconscious by now. But I think it's a great opportunity for a small studio like ours to display the game characters in more detail! I personally love the beautifully unique style we ended up with in this one!

Attachments


HeaderCapsule.png (503.49 KiB)


VerticalCapsule.png (790.77 KiB)

]]>
:neodeer_aww: :blobPikaAww:

This was made by Carrot Games' chief capsule artist, Kali (you can find her on instagram @Kalisdr_art)! She's worked art for other games and I've been told commissions are open! 👀
HeaderCapsule.png
VerticalCapsule.png
For those who don't know, the "capsule" is what steam calls the graphical pictures that appear attached to a game in the shop, the library and other various places. Other storefronts like itch have a similar concept, and it's the closest thing to the "box art" for digital releases.

Even though the game is pixel art, the laws of the biz demand games use illustrations for this kind of promotional material, and not in-game art. This has been a thing since the old days, and I guess it's become part of the public subconscious by now. But I think it's a great opportunity for a small studio like ours to display the game characters in more detail! I personally love the beautifully unique style we ended up with in this one!

Attachments


HeaderCapsule.png (503.49 KiB)


VerticalCapsule.png (790.77 KiB)

]]>
<![CDATA[Iris Blade :: [Devlog] A new enemy spawn system :: Author celes]]> 2026-03-10T17:50:08+00:00 2026-03-10T17:50:08+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=28&p=56#p56

There was something a very insightful playtester (thanks zivi!! :blobcatheart:) brought up, and it's the kind of thing that, once I heard, it made everything click: The current enemy spawn system tries to find spots near the player to put enemies in. This worked relatively well, but while it lead to fine gameplay, the issue that had been bugging me and I couldn't point out is that it did not really matter where you went: Enemies would keep spawning near you, so you could stay in place and keep farming forever. Sure, the game was incentivising you to move around in other ways (chests, raising difficulty, having to find an exit...), but at the bottommost core gameplay loop the game was incentivising people to stay put.

Now this is fine for a "survivors" style game, a genre in which I have some experience! But Iris Blade is supposed to be the opposite from a survivors game.

So it was time to redo enemy spawn logic!

Mulling it over, and the notebook

I'll go off on a bit of a tangent. I like to avoid "the topic" in my writing because we're all very tired, especially me, but there is one thing that keeps coming up when discussing with the cultists. They all bring up the usefulness of bouncing off ideas against the proverbial deus ex wall and how effective that is at organizing one's thoughts.

And you know what, I gotta give it to the cultists. :blobcatthink: There is something truly special about being put in front of with my own thoughts, in writing form, that other forms of thinking don't quite let me reach. This will be different for different people. But I think organizing one's thoughts is truly the key to tackling larger complexity.

So, I devised a contraption that lets me do the same thing with a heavily reduced carbon footprint: The notebook. Now, I didn't invent the notebook. As far as I can tell, this was invented by YouTuber ThinMatrix, of Equilinox fame and Java gamedev extraodinaire. If only he had told us how key the notebook was to his success during the devlogs. Instead, he just hinted at us by showing quick montages of his pages upon pages of doodles every now and then.

Anyway, seriously now: The next time you're struggling with some problem and feel the urge to consult with a talking pile of tensors, try talking to your notebook instead! Oh and drink that glass of water instead, you've earned it! :akko_smile:

Alright, It seems I am incapable of seriousness, so goofiness will have to suffice for now. Now, to better illustrate my point, I'm about to let you take very blurry peek into my mind
image.png
Those are the first two pages, to fully converge on a design for this I wrote about six pages. If you've tried reading a bit of this, you'll notice it's just me rambling. I'm literally thinking and writing it down as I go, stream-of-consciousness style. I find the act of writing helps me connect the dots and steers me away from tangents in a way that's almost impossible to do with freestyle thinking.

You'll also notice my writing style is a bit wasteful, we're lucky paper literally grows on trees, eh?

And the notebook nerds in the room may also have noticed how I like my notebooks clear: no grids or lines. That's just my style and I don't really have strong arguments for it, other than the fact that no lines gives me complete freedom to doodle things, even if this page ended up being a bit more text-y. Maybe you're also having strong opinions about the fact a grown-up is using a pencil. You can even see it in the picture, a 0.7mm Bic Velocity mechanical pencil (to save you the digging). The reason? I find it useful, and even cathartic to be able to conveniently "erase" a thought once I realize something didn't make sense.

The economics of spawning a bunch of slimes

Back to the topic, if there ever was one: The Game Director, which is what I've been calling the bit of code responsible for spawning enemies and making some other interesting gameplay decisions, has now acquired two tasks: (1) Fill up the world with enemies at the start of the level and (2) Respawn enemies as they die so you get to catch a break, but never really run out of them.

In retrospective, this was much simpler than I thought. Anytime you need to do something "procedural", the answer is always chunks and noise functions. What I did is split the game world into chunks (yet again). Each chunk stores two new properties:
  • Enemy count
  • Heat
Now, I follow the honorable art in computer science to use the word "heat" to describe metrics I don't have a better metaphor for. By "heat" I mean, how interesting is that chunk for the purposes of spawning enemies. The higher the heat, the more enemies there will be.

With this, we can define what I called the density of a chunk. Now, hopefully you'll excuse phpbb's lack of LaTeX formulas:

Code: Select all

density = enemyCount / heat
The Game Director's goal will be to keep enemy density stable across all chunks. But what does stability mean here? Well, that took a while to figure out, it's on page 4 of the notebook! At each point in time, we have a target density we want for the chunks. At the start of the run, that target density will be low. As the game progresses, we will slowly turn up the density dial, and things will gradually get crazier as the world is filled with more and more enemies, but keeping the original balance: Hotter areas get more enemies than colder ones.

So, by stability, what we mean is that we want to achieve a constant value for... some metric? Let's call it tension, even though it's a bit of a misnomer, like all the other terms here:

Code: Select all

tension = targetDensity / density
The tension formula has some interesting properties:
  • If the chunk is at the target enemy density, it has a value of 1.
  • If the chunk has a lot more enemies than it should, its value gradually go towards zero.
  • If the chunk has less enemies than it should, its value will quickly raise

    The mathematicians are screaming into the monitor as I am explaining division to the crowd. "Look at what gamedevs need to mimick a fraction", or something :blobcatgiggle:
    image.png
    But tension was the main ingredient here: When it's time to spawn an enemy, the Game Director will rank chunks by tension, and favor those with higher tension. Picking an element from a list at random with weighted probabilities was a recent topic of discussion, so I won't delve into that.

    There's some twists I added to the idea, for extra chaos. Heat is typically computed by checking things in that chunk, like chests and whatnot. Each chest adds some heat, barrels add some heat too, and so on. But then I added a noise function on top, so even if two chunks have the same amount of stuff in it, some chunks will tend to be a lot more densely populated than others, just like life itself: And to you, the player, the real reason behind this variance is that I'm sure those slimes have a plan which makes them have some areas more heavily guarded than others.

    I mentioned at the start of the section the game director had two goals: It turns out, the two goals are actually one and the same. A nice property of this system is that Initial spawn and respawning are two sides of the same coin. You start with an empty map, and you keep spawning enemies until you're satisfied!

    It's hard to show this in action since the point is to feel it over the course of the run, but this was my best attempt in a short looping gif:
    image.png
    And then the FPS dropped

    But no tale is complete without some performance testing. :akko_fistup: What's this I see? 45 FPS in my debug build? No, that won't do.

    Spawning enemies near the player has some advantages. You get to play smoke and mirrors with them and despawn enemies once they venture too far off. When it's you against the horde, nobody will notice a missing bat. :badbat: But now that there's enemies all over, I couldn't pull off this trick anymore. It only took a quick glance at the profiler to notice the issue. Hear me out now: Invest some time into having a good profiling system!

    The solution? Simple! Every performance issue can be solved in one of two ways: (1) Do it faster, (2) Do less work. Since my enemy logic is a port from Carrot Survivors, I know it was pretty optimized already: zero-allocation, uses a fast path for the spatial partitioning logic that's specific to enemies... So (2) was the obvious choice.

    The next best thing you can do when you can't despawn faraway enemies is to freeze them. And that turned out reasonably well! I also took the chance to add culling logic to my Sprite component, which I hadn't had the need to until now (because most things in the game are tiles or grass and those are culled!).

    Closing

    Anyway, I hope you enjoyed the devlog! :blobcatheart: I tried to go for a longer one this time, but I think it was time well spent. If I get to be a little selfish, please do let me know if you enjoy these. Gamedev can feel a bit lonely sometimes and despite what it might seem, morale among the carrot ranks hasn't been the highest.

Attachments


image.png (873.68 KiB)


enemyspawn.gif (9747.26 KiB)


image.png (20.24 KiB)

]]>


There was something a very insightful playtester (thanks zivi!! :blobcatheart:) brought up, and it's the kind of thing that, once I heard, it made everything click: The current enemy spawn system tries to find spots near the player to put enemies in. This worked relatively well, but while it lead to fine gameplay, the issue that had been bugging me and I couldn't point out is that it did not really matter where you went: Enemies would keep spawning near you, so you could stay in place and keep farming forever. Sure, the game was incentivising you to move around in other ways (chests, raising difficulty, having to find an exit...), but at the bottommost core gameplay loop the game was incentivising people to stay put.

Now this is fine for a "survivors" style game, a genre in which I have some experience! But Iris Blade is supposed to be the opposite from a survivors game.

So it was time to redo enemy spawn logic!

Mulling it over, and the notebook

I'll go off on a bit of a tangent. I like to avoid "the topic" in my writing because we're all very tired, especially me, but there is one thing that keeps coming up when discussing with the cultists. They all bring up the usefulness of bouncing off ideas against the proverbial deus ex wall and how effective that is at organizing one's thoughts.

And you know what, I gotta give it to the cultists. :blobcatthink: There is something truly special about being put in front of with my own thoughts, in writing form, that other forms of thinking don't quite let me reach. This will be different for different people. But I think organizing one's thoughts is truly the key to tackling larger complexity.

So, I devised a contraption that lets me do the same thing with a heavily reduced carbon footprint: The notebook. Now, I didn't invent the notebook. As far as I can tell, this was invented by YouTuber ThinMatrix, of Equilinox fame and Java gamedev extraodinaire. If only he had told us how key the notebook was to his success during the devlogs. Instead, he just hinted at us by showing quick montages of his pages upon pages of doodles every now and then.

Anyway, seriously now: The next time you're struggling with some problem and feel the urge to consult with a talking pile of tensors, try talking to your notebook instead! Oh and drink that glass of water instead, you've earned it! :akko_smile:

Alright, It seems I am incapable of seriousness, so goofiness will have to suffice for now. Now, to better illustrate my point, I'm about to let you take very blurry peek into my mind
image.png
Those are the first two pages, to fully converge on a design for this I wrote about six pages. If you've tried reading a bit of this, you'll notice it's just me rambling. I'm literally thinking and writing it down as I go, stream-of-consciousness style. I find the act of writing helps me connect the dots and steers me away from tangents in a way that's almost impossible to do with freestyle thinking.

You'll also notice my writing style is a bit wasteful, we're lucky paper literally grows on trees, eh?

And the notebook nerds in the room may also have noticed how I like my notebooks clear: no grids or lines. That's just my style and I don't really have strong arguments for it, other than the fact that no lines gives me complete freedom to doodle things, even if this page ended up being a bit more text-y. Maybe you're also having strong opinions about the fact a grown-up is using a pencil. You can even see it in the picture, a 0.7mm Bic Velocity mechanical pencil (to save you the digging). The reason? I find it useful, and even cathartic to be able to conveniently "erase" a thought once I realize something didn't make sense.

The economics of spawning a bunch of slimes

Back to the topic, if there ever was one: The Game Director, which is what I've been calling the bit of code responsible for spawning enemies and making some other interesting gameplay decisions, has now acquired two tasks: (1) Fill up the world with enemies at the start of the level and (2) Respawn enemies as they die so you get to catch a break, but never really run out of them.

In retrospective, this was much simpler than I thought. Anytime you need to do something "procedural", the answer is always chunks and noise functions. What I did is split the game world into chunks (yet again). Each chunk stores two new properties:
  • Enemy count
  • Heat
Now, I follow the honorable art in computer science to use the word "heat" to describe metrics I don't have a better metaphor for. By "heat" I mean, how interesting is that chunk for the purposes of spawning enemies. The higher the heat, the more enemies there will be.

With this, we can define what I called the density of a chunk. Now, hopefully you'll excuse phpbb's lack of LaTeX formulas:

Code: Select all

density = enemyCount / heat
The Game Director's goal will be to keep enemy density stable across all chunks. But what does stability mean here? Well, that took a while to figure out, it's on page 4 of the notebook! At each point in time, we have a target density we want for the chunks. At the start of the run, that target density will be low. As the game progresses, we will slowly turn up the density dial, and things will gradually get crazier as the world is filled with more and more enemies, but keeping the original balance: Hotter areas get more enemies than colder ones.

So, by stability, what we mean is that we want to achieve a constant value for... some metric? Let's call it tension, even though it's a bit of a misnomer, like all the other terms here:

Code: Select all

tension = targetDensity / density
The tension formula has some interesting properties:
  • If the chunk is at the target enemy density, it has a value of 1.
  • If the chunk has a lot more enemies than it should, its value gradually go towards zero.
  • If the chunk has less enemies than it should, its value will quickly raise

    The mathematicians are screaming into the monitor as I am explaining division to the crowd. "Look at what gamedevs need to mimick a fraction", or something :blobcatgiggle:
    image.png
    But tension was the main ingredient here: When it's time to spawn an enemy, the Game Director will rank chunks by tension, and favor those with higher tension. Picking an element from a list at random with weighted probabilities was a recent topic of discussion, so I won't delve into that.

    There's some twists I added to the idea, for extra chaos. Heat is typically computed by checking things in that chunk, like chests and whatnot. Each chest adds some heat, barrels add some heat too, and so on. But then I added a noise function on top, so even if two chunks have the same amount of stuff in it, some chunks will tend to be a lot more densely populated than others, just like life itself: And to you, the player, the real reason behind this variance is that I'm sure those slimes have a plan which makes them have some areas more heavily guarded than others.

    I mentioned at the start of the section the game director had two goals: It turns out, the two goals are actually one and the same. A nice property of this system is that Initial spawn and respawning are two sides of the same coin. You start with an empty map, and you keep spawning enemies until you're satisfied!

    It's hard to show this in action since the point is to feel it over the course of the run, but this was my best attempt in a short looping gif:
    image.png
    And then the FPS dropped

    But no tale is complete without some performance testing. :akko_fistup: What's this I see? 45 FPS in my debug build? No, that won't do.

    Spawning enemies near the player has some advantages. You get to play smoke and mirrors with them and despawn enemies once they venture too far off. When it's you against the horde, nobody will notice a missing bat. :badbat: But now that there's enemies all over, I couldn't pull off this trick anymore. It only took a quick glance at the profiler to notice the issue. Hear me out now: Invest some time into having a good profiling system!

    The solution? Simple! Every performance issue can be solved in one of two ways: (1) Do it faster, (2) Do less work. Since my enemy logic is a port from Carrot Survivors, I know it was pretty optimized already: zero-allocation, uses a fast path for the spatial partitioning logic that's specific to enemies... So (2) was the obvious choice.

    The next best thing you can do when you can't despawn faraway enemies is to freeze them. And that turned out reasonably well! I also took the chance to add culling logic to my Sprite component, which I hadn't had the need to until now (because most things in the game are tiles or grass and those are culled!).

    Closing

    Anyway, I hope you enjoyed the devlog! :blobcatheart: I tried to go for a longer one this time, but I think it was time well spent. If I get to be a little selfish, please do let me know if you enjoy these. Gamedev can feel a bit lonely sometimes and despite what it might seem, morale among the carrot ranks hasn't been the highest.

Attachments


image.png (873.68 KiB)


enemyspawn.gif (9747.26 KiB)


image.png (20.24 KiB)

]]>
<![CDATA[Iris Blade :: Re: [Devlog] A new enemy spawn system :: Reply by Sugui]]> 2026-03-10T19:33:00+00:00 2026-03-10T19:33:00+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=28&p=58#p58 . It makes me remember those days when I was bored in classroom at the highschool, and I started to draw abstract patterns and figures. All my attention went towards there, and for some reason, I felt creativity peaked while doing that.

Sure, every person can be different, but I think having a white notebook where you don't have distractions or restrictions about how you can express yourself, helps a lot for activating creativity. And also, using a paper notebook fits so well here as it can be seen "old school" as phpbb too :blobcatgiggle:. Anyways I think the term "old-school" is becoming more and more a synonym of "better" than "bleeding edge" is, sadly. :pensivecat:

About the algorithm, it seems cool and something that would make players more prone to explore in general. Also, I'm impressed how you got to think about it using your notebook, it really seems like magic a bit. :blobPikaAww:

Also, about being selfish *proceeds to headbutt her*. This is literally your space and you can talk about everything you want. This devlog is a new topic in the forum that is not conflicting with others. I think it's important that you talk about in the way you want about the gamedev, so you don't limit your creativity or expresivity. I'm sure there will always be people that read and listen to what you want to say. :blobcatheart:

In my case, I found this devlog very interesting, I'm always happy to read your gamedev ramblings. :blobpika:

(I'm now thinking about bringing in my notebook for a trip I'll have to do soon so I can think game ideas :blobcatthink: )]]>
. It makes me remember those days when I was bored in classroom at the highschool, and I started to draw abstract patterns and figures. All my attention went towards there, and for some reason, I felt creativity peaked while doing that.

Sure, every person can be different, but I think having a white notebook where you don't have distractions or restrictions about how you can express yourself, helps a lot for activating creativity. And also, using a paper notebook fits so well here as it can be seen "old school" as phpbb too :blobcatgiggle:. Anyways I think the term "old-school" is becoming more and more a synonym of "better" than "bleeding edge" is, sadly. :pensivecat:

About the algorithm, it seems cool and something that would make players more prone to explore in general. Also, I'm impressed how you got to think about it using your notebook, it really seems like magic a bit. :blobPikaAww:

Also, about being selfish *proceeds to headbutt her*. This is literally your space and you can talk about everything you want. This devlog is a new topic in the forum that is not conflicting with others. I think it's important that you talk about in the way you want about the gamedev, so you don't limit your creativity or expresivity. I'm sure there will always be people that read and listen to what you want to say. :blobcatheart:

In my case, I found this devlog very interesting, I'm always happy to read your gamedev ramblings. :blobpika:

(I'm now thinking about bringing in my notebook for a trip I'll have to do soon so I can think game ideas :blobcatthink: )]]>
<![CDATA[Iris Blade :: Re: [Devlog] A new enemy spawn system :: Reply by Kampffrosch]]> 2026-03-11T06:48:17+00:00 2026-03-11T06:48:17+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=28&p=59#p59
There are so many forms of them, all of them useful.
I try to always carry a cheap a6 notebook in my pocket, so I can just jot down some thoughts or tasks for later and free up my mind in the moment.

I also draft up my ideas on printer paper for more complex projects, but I throw those away once I implemented them. I don't want to use that as a reference since it "bitrots" instantly.

Notes that I actually want to retrieve in the future I store digitally.]]>

There are so many forms of them, all of them useful.
I try to always carry a cheap a6 notebook in my pocket, so I can just jot down some thoughts or tasks for later and free up my mind in the moment.

I also draft up my ideas on printer paper for more complex projects, but I throw those away once I implemented them. I don't want to use that as a reference since it "bitrots" instantly.

Notes that I actually want to retrieve in the future I store digitally.]]>
<![CDATA[Iris Blade :: Re: [Devlog] A new enemy spawn system :: Reply by celes]]> 2026-03-11T12:09:46+00:00 2026-03-11T12:09:46+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=28&p=60#p60
Sugui wrote: Tue Mar 10, 2026 7:33 pm I'm now thinking about bringing in my notebook for a trip I'll have to do soon so I can think game ideas
Yay! Glad to have inspired this!! :heart_purple:
Kampffrosch wrote: Wed Mar 11, 2026 6:48 am I try to always carry a cheap a6 notebook in my pocket, so I can just jot down some thoughts or tasks for later and free up my mind in the moment.
I should do this too! I rely on my phone for that when there's no need, besides writing on my comically small 3.5" phone screen is uncomfortable (that's the point of having such a tiny phone, so that I don't use it more than it's necessary). What do you use for writing? I'm a bit wary of keeping pens in my pocket as they easily break.

That reminds me, another very useful thing I've rescued for my notebook sessions is a scientific calculator. I have one I kept all the way through my high school and uni years and it's been a very refreshing to bring it back into my daily workflow. Come to think of it I've never even replaced the battery to this thing?

Anyway I realized having a physical calculator is a lot more effective than anything on my phone or my computer. When coming up with formulas like the above, quickly plugging in some numbers without the distraction of having to go back to the computer really helps.]]>
Sugui wrote: Tue Mar 10, 2026 7:33 pm I'm now thinking about bringing in my notebook for a trip I'll have to do soon so I can think game ideas
Yay! Glad to have inspired this!! :heart_purple:
Kampffrosch wrote: Wed Mar 11, 2026 6:48 am I try to always carry a cheap a6 notebook in my pocket, so I can just jot down some thoughts or tasks for later and free up my mind in the moment.
I should do this too! I rely on my phone for that when there's no need, besides writing on my comically small 3.5" phone screen is uncomfortable (that's the point of having such a tiny phone, so that I don't use it more than it's necessary). What do you use for writing? I'm a bit wary of keeping pens in my pocket as they easily break.

That reminds me, another very useful thing I've rescued for my notebook sessions is a scientific calculator. I have one I kept all the way through my high school and uni years and it's been a very refreshing to bring it back into my daily workflow. Come to think of it I've never even replaced the battery to this thing?

Anyway I realized having a physical calculator is a lot more effective than anything on my phone or my computer. When coming up with formulas like the above, quickly plugging in some numbers without the distraction of having to go back to the computer really helps.]]>
<![CDATA[Iris Blade :: Re: [Devlog] A new enemy spawn system :: Reply by Kampffrosch]]> 2026-03-11T17:01:39+00:00 2026-03-11T17:01:39+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=28&p=61#p61
celes wrote: Wed Mar 11, 2026 12:09 pm What do you use for writing? I'm a bit wary of keeping pens in my pocket as they easily break.
I use gel pens. (uniball signo dx 0.38)
I like them because I can write pretty small and readable with them and the gel doesn't smear.
celes wrote: Wed Mar 11, 2026 12:09 pm physical calculator
Anything I would do with a calculator I rather do in my head or on paper.
It helps me build/maintain that mathematical intuition.
That's just me though, I don't think calculators are bad I just never warmed up to them.]]>
celes wrote: Wed Mar 11, 2026 12:09 pm What do you use for writing? I'm a bit wary of keeping pens in my pocket as they easily break.
I use gel pens. (uniball signo dx 0.38)
I like them because I can write pretty small and readable with them and the gel doesn't smear.
celes wrote: Wed Mar 11, 2026 12:09 pm physical calculator
Anything I would do with a calculator I rather do in my head or on paper.
It helps me build/maintain that mathematical intuition.
That's just me though, I don't think calculators are bad I just never warmed up to them.]]>
<![CDATA[Iris Blade :: Scrollbars! :: Author celes]]> 2026-03-18T19:27:43+00:00 2026-03-18T19:27:43+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=33&p=86#p86
We take so much for granted from our UIs... I didn't even realize I'd need scrollbars! I keep going back and forth thinking whether rolling my own UI system was a good idea, but seeing how I was able to a add scrollbar container in about an hour, I'm on team "good idea" today! :akko_smile:
scrollbars.gif
The biggest ux hurdle here was making sure this is both keyboard navigable and mouse navigable and to make no compromises on either of the navigation modes. For mouse mode, that means being able to use the scrollbar and hovering widgets freely, and for keyboard it means the scrollbar should follow the focused element so that you always see what you're selecting.

Coming up with a system to handle both was easy once I gave this some proper thought and made everything click. My UI interaction "manager" thing already keeps track of the current interaction mode, so we know whether we're on key mode (keyboard or gamepad) or pointer (i.e. mouse) mode. There are some actions that let you switch between the two.

The interesting challenge was in keyboard mode, where I had to detect if the currently focused element changed. I added a "justFocused " property just for that which gives me the UiRect and sense id for the widget that was focused. If the sense id for the widget is one that is inside the scrollbar container and the rectangle is inside thethe visible range, we adjust the scroll position doing some math. One thing I kept struggling with is how do I know what widgets are inside the scrollbar container. That is achieved with code that looks like this inside the scrollbar's draw code:

Code: Select all

uiInteraction.BeginRecording("some-unique-key");
foreach (var child in children) {
    child.Draw(...);
}
var childInScrollbar = uiInteraction.EndRecording("some-unique-key");
Another detail worth mentioning is how I didn't do a modular "ScrollBar" node that you can put anything under but instead a "ScrollbarColumn", which acts as a VBox *with* a scrollbar. I do that because when adjusting the offset, I make sure to snap to one of the rows. This is very situational but I find it improves usability when you have a row with lots of tiny widgets, since otherwise you might end up scrolling halfway through the section to make one of the child widgets visible.

One day I should write about my UI system in a more comprehensive way. Not sure if that made any sense :nkoThink:

Anyway that's all for today!

Attachments


scrollbars.gif (1189.88 KiB)

]]>

We take so much for granted from our UIs... I didn't even realize I'd need scrollbars! I keep going back and forth thinking whether rolling my own UI system was a good idea, but seeing how I was able to a add scrollbar container in about an hour, I'm on team "good idea" today! :akko_smile:
scrollbars.gif
The biggest ux hurdle here was making sure this is both keyboard navigable and mouse navigable and to make no compromises on either of the navigation modes. For mouse mode, that means being able to use the scrollbar and hovering widgets freely, and for keyboard it means the scrollbar should follow the focused element so that you always see what you're selecting.

Coming up with a system to handle both was easy once I gave this some proper thought and made everything click. My UI interaction "manager" thing already keeps track of the current interaction mode, so we know whether we're on key mode (keyboard or gamepad) or pointer (i.e. mouse) mode. There are some actions that let you switch between the two.

The interesting challenge was in keyboard mode, where I had to detect if the currently focused element changed. I added a "justFocused " property just for that which gives me the UiRect and sense id for the widget that was focused. If the sense id for the widget is one that is inside the scrollbar container and the rectangle is inside thethe visible range, we adjust the scroll position doing some math. One thing I kept struggling with is how do I know what widgets are inside the scrollbar container. That is achieved with code that looks like this inside the scrollbar's draw code:

Code: Select all

uiInteraction.BeginRecording("some-unique-key");
foreach (var child in children) {
    child.Draw(...);
}
var childInScrollbar = uiInteraction.EndRecording("some-unique-key");
Another detail worth mentioning is how I didn't do a modular "ScrollBar" node that you can put anything under but instead a "ScrollbarColumn", which acts as a VBox *with* a scrollbar. I do that because when adjusting the offset, I make sure to snap to one of the rows. This is very situational but I find it improves usability when you have a row with lots of tiny widgets, since otherwise you might end up scrolling halfway through the section to make one of the child widgets visible.

One day I should write about my UI system in a more comprehensive way. Not sure if that made any sense :nkoThink:

Anyway that's all for today!

Attachments


scrollbars.gif (1189.88 KiB)

]]>
<![CDATA[Iris Blade :: Re: Scrollbars! :: Reply by Sugui]]> 2026-03-18T21:23:07+00:00 2026-03-18T21:23:07+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=33&p=87#p87 I liked a lot what you did to record the buttons that are actually in the scrolbar!

So I guess that the .Draw() function adds them to the current recording, if there is one. But do elements call .Draw() also when they aren't visible on the screen? Because it can be elements not visible due to the scrollbar progress, but its .Draw() function being called anyways :nkoThink:]]>
I liked a lot what you did to record the buttons that are actually in the scrolbar!

So I guess that the .Draw() function adds them to the current recording, if there is one. But do elements call .Draw() also when they aren't visible on the screen? Because it can be elements not visible due to the scrollbar progress, but its .Draw() function being called anyways :nkoThink:]]>
<![CDATA[Iris Blade :: Re: Scrollbars! :: Reply by celes]]> 2026-03-19T10:29:39+00:00 2026-03-19T10:29:39+00:00 http://community.carrot-games.com/viewtopic.php?f=7&t=33&p=88#p88
Sugui wrote: Wed Mar 18, 2026 9:23 pm So I guess that the .Draw() function adds them to the current recording, if there is one. But do elements call .Draw() also when they aren't visible on the screen? Because it can be elements not visible due to the scrollbar progress, but its .Draw() function being called anyways
I should take the time to properly blog about this at some point, but the way UI interaction works here is inspired by how dear imgui (and other similar imgui libraries) do it. During its draw method, widgets "sense" a portion of the screen. This call to .Sense() returns information about UI interaction on that location of the screen for the previous frame[1], so whether the mouse was hovering, clicking or dragging over that screen portion (a rectangle). In my case it's not just the mouse, but also whether key focus was on that widget, and whether the accept button was pressed over that widget.

So what the recording functionality does is registering all widgets that have called Sense() between the start and the end, and with that, you know all the things that are actually interested in user interaction.

[1] This frame of lag is part of what makes the whole thing work in immediate mode. When you draw a button, you immediately know if it was pressed during the previous frame (or rather, at the start of the current frame), and this enables the common immediate-mode pattern of "if draw_button() { /* do things ... */ }" even though in my framework it looks slightly different.]]>
Sugui wrote: Wed Mar 18, 2026 9:23 pm So I guess that the .Draw() function adds them to the current recording, if there is one. But do elements call .Draw() also when they aren't visible on the screen? Because it can be elements not visible due to the scrollbar progress, but its .Draw() function being called anyways
I should take the time to properly blog about this at some point, but the way UI interaction works here is inspired by how dear imgui (and other similar imgui libraries) do it. During its draw method, widgets "sense" a portion of the screen. This call to .Sense() returns information about UI interaction on that location of the screen for the previous frame[1], so whether the mouse was hovering, clicking or dragging over that screen portion (a rectangle). In my case it's not just the mouse, but also whether key focus was on that widget, and whether the accept button was pressed over that widget.

So what the recording functionality does is registering all widgets that have called Sense() between the start and the end, and with that, you know all the things that are actually interested in user interaction.

[1] This frame of lag is part of what makes the whole thing work in immediate mode. When you draw a button, you immediately know if it was pressed during the previous frame (or rather, at the start of the current frame), and this enables the common immediate-mode pattern of "if draw_button() { /* do things ... */ }" even though in my framework it looks slightly different.]]>
<![CDATA[Show your stuff :: Magic Dungeon. A small puzzle RPG! :: Author Sugui]]> 2026-03-06T18:58:16+00:00 2026-03-06T18:58:16+00:00 http://community.carrot-games.com/viewtopic.php?f=15&t=24&p=46#p46 https://godot-rust.github.io).
image.png
I wanted to do a small project with the library, so I got inspiration by the game "Tower of the Sorcerer". This is a game that at first glance seems like a traditional roguelike. But the difference is that it's all deterministic, and you need to careful use your resources and optimize your HP to get as far as you can in your adventure!

This is a really simplified version of the game, with less things to have in account, and a single floor. Your objective is to kill the devil... and make a soup with them :blobcatgiggle: . No, really, this last part is just funny because for the assets pack that I used for the sprites, it happened to be a character that looks so similar to Marcille, from Dungeon Meshi, so I used it for the game.

I warn you that the game is really difficult! The steps I took to do a good level design were basically, place things a bit randomly (at my own criteria), and then balance:
  • Hmmm the game is too easy, let's improve the stats of this monster.
  • Hmmm now the game get's unbeatable because there aren't enough sword items so I can't hit the serpent, let's add another sword item to the level
  • ...
And just iterating like this is how I get a more-or-less balanced experience! But I made it difficult enough so I think there is only one way to beat it. If I get inspired I could do another post with the solution, for those who want to play the game but can't beat it.
image.png

The link to the game is here, is only playable in browser!: https://siesta-cat.itch.io/magic-dungeon

Attachments


image.png (345.63 KiB)


image.png (38.02 KiB)

]]>
https://godot-rust.github.io).
image.png
I wanted to do a small project with the library, so I got inspiration by the game "Tower of the Sorcerer". This is a game that at first glance seems like a traditional roguelike. But the difference is that it's all deterministic, and you need to careful use your resources and optimize your HP to get as far as you can in your adventure!

This is a really simplified version of the game, with less things to have in account, and a single floor. Your objective is to kill the devil... and make a soup with them :blobcatgiggle: . No, really, this last part is just funny because for the assets pack that I used for the sprites, it happened to be a character that looks so similar to Marcille, from Dungeon Meshi, so I used it for the game.

I warn you that the game is really difficult! The steps I took to do a good level design were basically, place things a bit randomly (at my own criteria), and then balance:
  • Hmmm the game is too easy, let's improve the stats of this monster.
  • Hmmm now the game get's unbeatable because there aren't enough sword items so I can't hit the serpent, let's add another sword item to the level
  • ...
And just iterating like this is how I get a more-or-less balanced experience! But I made it difficult enough so I think there is only one way to beat it. If I get inspired I could do another post with the solution, for those who want to play the game but can't beat it.
image.png

The link to the game is here, is only playable in browser!: https://siesta-cat.itch.io/magic-dungeon

Attachments


image.png (345.63 KiB)


image.png (38.02 KiB)

]]>
<![CDATA[Show your stuff :: Re: Magic Dungeon. A small puzzle RPG! :: Reply by Sugui]]> 2026-03-07T18:15:48+00:00 2026-03-07T18:15:48+00:00 http://community.carrot-games.com/viewtopic.php?f=15&t=24&p=49#p49 https://sugui.bearblog.dev/creating-a-s ... odot-rust/]]> https://sugui.bearblog.dev/creating-a-s ... odot-rust/]]> <![CDATA[Show your stuff :: Re: Magic Dungeon. A small puzzle RPG! :: Reply by Kampffrosch]]> 2026-03-12T09:48:01+00:00 2026-03-12T09:48:01+00:00 http://community.carrot-games.com/viewtopic.php?f=15&t=24&p=63#p63

> With GDScript, I got so disgusted every time I had to use the language.
I know that feeling.
I run into a new weirdness every time I do a jam.

I really liked your game btw. It reminded me of desktop dungeons, never heard about Tower of the Sorcerer before.]]>


> With GDScript, I got so disgusted every time I had to use the language.
I know that feeling.
I run into a new weirdness every time I do a jam.

I really liked your game btw. It reminded me of desktop dungeons, never heard about Tower of the Sorcerer before.]]>
<![CDATA[Show your stuff :: Re: Magic Dungeon. A small puzzle RPG! :: Reply by Sugui]]> 2026-03-13T21:49:29+00:00 2026-03-13T21:49:29+00:00 http://community.carrot-games.com/viewtopic.php?f=15&t=24&p=71#p71 . I appreciate that you like the game, I wasn't sure that people would enjoy it.

This genre is really interesting and not much explored imo. It could be fun to think about another idea within this genre :nkoThink:

It's very funny that I had Desktop Dungeons in my Steam library from a day that I could get the game free, but never played it. Until this last Sunday, where I
found that this game was of the magic tower genre too. It was quite fun :bunhappy:]]>
. I appreciate that you like the game, I wasn't sure that people would enjoy it.

This genre is really interesting and not much explored imo. It could be fun to think about another idea within this genre :nkoThink:

It's very funny that I had Desktop Dungeons in my Steam library from a day that I could get the game free, but never played it. Until this last Sunday, where I
found that this game was of the magic tower genre too. It was quite fun :bunhappy:]]>
<![CDATA[The Lounge :: Kiwix: A cool offline internet viewer :: Author celes]]> 2026-03-13T10:26:04+00:00 2026-03-13T10:26:04+00:00 http://community.carrot-games.com/viewtopic.php?f=16&t=29&p=67#p67

There's this project called kiwix. Their goal is to provide access to knowledge when there's no internet, so they provide access to lots of trusted knowledge sources: There's books, talks, wikipedia and your favorite software's documentation is probably there too :hammyeyes:

The first place to check is the library where you can search through the archives. If you click the cards you can open the libraries and browse through the website archives. They even have a search feature that isn't half bad.

It took me a while to find out how to read these archives locally, but here's a good comprehensive list. It seems they have plenty of options, from a simple offline app you can run locally to a little frontend you can self-host that uses the same interface as the library I shared above.

I'm gonna be putting one of these in my cluster to toy with it!]]>


There's this project called kiwix. Their goal is to provide access to knowledge when there's no internet, so they provide access to lots of trusted knowledge sources: There's books, talks, wikipedia and your favorite software's documentation is probably there too :hammyeyes:

The first place to check is the library where you can search through the archives. If you click the cards you can open the libraries and browse through the website archives. They even have a search feature that isn't half bad.

It took me a while to find out how to read these archives locally, but here's a good comprehensive list. It seems they have plenty of options, from a simple offline app you can run locally to a little frontend you can self-host that uses the same interface as the library I shared above.

I'm gonna be putting one of these in my cluster to toy with it!]]>
<![CDATA[The Lounge :: Re: Kiwix: A cool offline internet viewer :: Reply by Sugui]]> 2026-03-13T21:33:19+00:00 2026-03-13T21:33:19+00:00 http://community.carrot-games.com/viewtopic.php?f=16&t=29&p=70#p70
I'm also thinking about piracy now, non-centrally distributed information is one of the best and durable ways to preserve shows, music, software, books from be lost forever... Think about the old shows that streaming services stopped to host u_u

So, I see you have received the call of Hari Seldon! :nkoPat:]]>

I'm also thinking about piracy now, non-centrally distributed information is one of the best and durable ways to preserve shows, music, software, books from be lost forever... Think about the old shows that streaming services stopped to host u_u

So, I see you have received the call of Hari Seldon! :nkoPat:]]>
<![CDATA[The Lounge :: Re: Kiwix: A cool offline internet viewer :: Reply by celes]]> 2026-03-14T09:56:40+00:00 2026-03-14T09:56:40+00:00 http://community.carrot-games.com/viewtopic.php?f=16&t=29&p=74#p74
Sugui wrote: Fri Mar 13, 2026 9:33 pm So, I see you have received the call of Hari Seldon!
Indeed! We'd all do well to start preserving knowledge. I don't think any sort of catastrophic events are necessary for preservation stuff to come in handy. You never know when some asshole is gonna decide to block a whole country's internet to protect a bunch of fragile-ego'd clowns and their right to chase after a ball.]]>
Sugui wrote: Fri Mar 13, 2026 9:33 pm So, I see you have received the call of Hari Seldon!
Indeed! We'd all do well to start preserving knowledge. I don't think any sort of catastrophic events are necessary for preservation stuff to come in handy. You never know when some asshole is gonna decide to block a whole country's internet to protect a bunch of fragile-ego'd clowns and their right to chase after a ball.]]>
<![CDATA[Game Programming :: Screenshots and profiling :: Author celes]]> 2026-03-02T19:59:23+00:00 2026-03-02T19:59:23+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=23&p=43#p43
Here you have it in all its glory, and as a bonus you get to see some unsafe C# in action! I gotta say, SDL makes this stuff easy and hard at the same time: Easy because the documentation is amazing and everything has examples. Hard, because there were a couple nasty surprises to figure out, especially figuring out when asking OpenGL for "RGBA" pixels you have to tell SDL to save in ABGR, plus the mandatory OpenGL vertical flip, of course :blobcatnodfast:
image.png
But then, I tried it, and the thing froze my game... not by much, only a split second, but enough for me to be annoyed. Because I want this thing running in the background taking screenshots while I play, and I don't wanna get a hiccup every few seconds when playing!

At first glance, it seemed the culprit was that glGetTexImage. Everyone will tell you that transferring pixel data from the GPU to the CPU is super slow and will block everything, stall rendering, and probably will make you late for dinner too. Because suddenly we're all hardware people and everyone knows "pci express is super slow", or something.

Anyway, after a quick detour into Pixel Buffer Objects, which apparently can speed this up, but not as much as one would hope, I had an epiphany: What if I actually profile this?

Well, turns out 100% of the time is taken by that SavePNG function in SDL_Image and the actual data transfer from the GPU to CPU is negligible... Why does SavePNG take so long (about 250ms)? Don't know don't care, but now we have a solution. We run it in a separate thread and let the magic of parallelism do the rest. Since we have the pixel data in an array that nobody else knows about (something something *ownership*), we can do all the SDL stuff on the side while the game continues running.
image.png
I'll also take the opportunity to appreciate how easy it was to do all this "low level" manipulation mostly-safely (no formal proof for you :pensivepumpkin:) and quite idiomatically. Especially spawning that thread which took almost no effort!

Remember kids: Always profile first :blobfoxthinksmart:

*EDIT*: Oops! And there was a tiny memory leak in there. I forgor to destroy the SDL surface. Guess I'm not used to cleaning up after myself ^^' Serves me right for boasting about how (un)safe this stuff was.

Attachments


image.png (78.93 KiB)


image.png (55.39 KiB)

]]>

Here you have it in all its glory, and as a bonus you get to see some unsafe C# in action! I gotta say, SDL makes this stuff easy and hard at the same time: Easy because the documentation is amazing and everything has examples. Hard, because there were a couple nasty surprises to figure out, especially figuring out when asking OpenGL for "RGBA" pixels you have to tell SDL to save in ABGR, plus the mandatory OpenGL vertical flip, of course :blobcatnodfast:
image.png
But then, I tried it, and the thing froze my game... not by much, only a split second, but enough for me to be annoyed. Because I want this thing running in the background taking screenshots while I play, and I don't wanna get a hiccup every few seconds when playing!

At first glance, it seemed the culprit was that glGetTexImage. Everyone will tell you that transferring pixel data from the GPU to the CPU is super slow and will block everything, stall rendering, and probably will make you late for dinner too. Because suddenly we're all hardware people and everyone knows "pci express is super slow", or something.

Anyway, after a quick detour into Pixel Buffer Objects, which apparently can speed this up, but not as much as one would hope, I had an epiphany: What if I actually profile this?

Well, turns out 100% of the time is taken by that SavePNG function in SDL_Image and the actual data transfer from the GPU to CPU is negligible... Why does SavePNG take so long (about 250ms)? Don't know don't care, but now we have a solution. We run it in a separate thread and let the magic of parallelism do the rest. Since we have the pixel data in an array that nobody else knows about (something something *ownership*), we can do all the SDL stuff on the side while the game continues running.
image.png
I'll also take the opportunity to appreciate how easy it was to do all this "low level" manipulation mostly-safely (no formal proof for you :pensivepumpkin:) and quite idiomatically. Especially spawning that thread which took almost no effort!

Remember kids: Always profile first :blobfoxthinksmart:

*EDIT*: Oops! And there was a tiny memory leak in there. I forgor to destroy the SDL surface. Guess I'm not used to cleaning up after myself ^^' Serves me right for boasting about how (un)safe this stuff was.

Attachments


image.png (78.93 KiB)


image.png (55.39 KiB)

]]>
<![CDATA[Game Programming :: Re: Screenshots and profiling :: Reply by Sugui]]> 2026-03-02T21:05:55+00:00 2026-03-02T21:05:55+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=23&p=44#p44
Also, why is everything of the code "unsafe". Is it because you are creating arrays in C# and passing them to C as pointers?

I remembering you saying in the Discord server I think, that you game was single threaded, guess that's not exactly true anymore :blobcatgiggle:
no formal proof for you
Just where it hurts :akko_nope:]]>

Also, why is everything of the code "unsafe". Is it because you are creating arrays in C# and passing them to C as pointers?

I remembering you saying in the Discord server I think, that you game was single threaded, guess that's not exactly true anymore :blobcatgiggle:
no formal proof for you
Just where it hurts :akko_nope:]]>
<![CDATA[Game Programming :: Re: Screenshots and profiling :: Reply by celes]]> 2026-03-03T08:17:47+00:00 2026-03-03T08:17:47+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=23&p=45#p45
I'd need to profile, but if I had to take a guess it's probably the code compressing the pixels into PNG, I guess it takes some time to do that for a 1920x1080 image.
Sugui wrote:Just where it hurts :akko_nope:
ehehehe :blobcatgiggle:]]>

I'd need to profile, but if I had to take a guess it's probably the code compressing the pixels into PNG, I guess it takes some time to do that for a 1920x1080 image.
Sugui wrote:Just where it hurts :akko_nope:
ehehehe :blobcatgiggle:]]>
<![CDATA[Game Programming :: The one Stack Overflow question I keep coming back to :: Author celes]]> 2026-03-07T12:21:15+00:00 2026-03-07T12:21:15+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=25&p=47#p47

https://stackoverflow.com/questions/172 ... babilities

Some of you may recognize the poster (it's a me!). Feel free to lurk, there's not much but there was a time when StackOverflow was good, and I think a naiver younger version of me asked some good questions.

Anyway, probabilities. There's a reason I keep coming back to this question: It shows up all the time in gamedev. For those of you who don't follow StackOverflow links (wouldn't blame you), or in case the link is no longer accessible: Here's the original question:
I have an array of N elements (representing the N letters of a given alphabet), and each cell of the array holds an integer value, that integer value meaning the number of occurrences in a given text of that letter. Now I want to randomly choose a letter from all of the letters in the alphabet, based on his number of appearances with the given constraints:
  • If the letter has a positive (nonzero) value, then it can be always chosen by the algorithm (with a bigger or smaller probability, of course).
  • If a letter A has a higher value than a letter B, then it has to be more likely to be chosen by the algorithm.
Now, taking that into account, I've come up with a simple algorithm that might do the job, but I was just wondering if there was a better thing to do. This seems to be quite fundamental, and I think there might be more clever things to do in order to accomplish this more efficiently. This is the algorithm i thought:
  • Add up all the frequencies in the array. Store it in SUM
  • Choosing up a random value from 0 to SUM. Store it in RAN
  • While RAN > 0, Starting from the first, visit each cell in the array (in order), and subtract the value of that cell from RAN
  • The last visited cell is the chosen one
So, is there a better thing to do than this? Am I missing something?

I'm aware most modern computers can compute this so fast I won't even notice if my algorithm is inefficient, so this is more of a theoretical question rather than a practical one.

I prefer an explained algorithm rather than just code for an answer, but If you're more comfortable providing your answer in code, I have no problem with that.
Let's take a moment to appreciate a time where StackOverflow was filled with good, insightful questions (such as mine :nkoCool:) and answers instead of burned-out mods closing questions they don't understand as duplicate of other questions they never bothered checking.

I don't even know what the original use case for this was, but if I had to take a guess, I was messing with the dark arts, trying to implement a markov chain generator. Either way I kept coming back to this problem and many times it has to do with designing gameplay systems, like having a boss pick its next attack, or picking where I'll spawn my next enemy where some areas are "hotter" than others, which was today's problem, actually!

Let's take a look at the accepted solution :blobpeek:
The idea:
  • Iterate through all the elements and set the value of each element as the cumulative frequency thus far.
  • Generate a random number between 1 and the sum of all frequencies
  • Do a binary search on the values for this number (finding the first value greater than or equal to the number).
Example:

Code: Select all

Element    A B C D
Frequency  1 4 3 2
Cumulative 1 5 8 10
Generate a random number in the range 1-10 (1+4+3+2 = 10, the same as the last value in the cumulative list), do a binary search, which will return values as follows:

Code: Select all

Number   Element returned
1        A
2        B
3        B
4        B
5        B
6        C
7        C
8        C
9        D
10       D
Now, I must admit, even today, this answer flies slightly over my head. I wouldn't blame you if putting that binary search in there makes you feel uneasy, especially when you still have to iterate through all the elements so the complexity of the whole thing would be linear anyway. I have implemented this version at least once in the past and it seems to work, and things like binary searches tickled my past self's brain in the right way, so I marked it as accepted.

But these days, the answer I've converged towards is actually the algorhm I initially suggested in my question, not the answer. I've implemented it many times across many programming languages. It's good enough, and I don't think we're getting anything from the binary search over a linear scan for the small lists I typically deal with.

This was the latest iteration of it, I wrote it today as a utility function because there were at least three inline instances of it in Iris Blade and while I appreciate ths fun kata I keep coming back to, I think it's time to encapsulate it in a tiny utility function:
image.png
So anyway, this is the StackOverflow *question*, not answer, I keep coming back to ^^

Attachments


image.png (89.86 KiB)

]]>


https://stackoverflow.com/questions/172 ... babilities

Some of you may recognize the poster (it's a me!). Feel free to lurk, there's not much but there was a time when StackOverflow was good, and I think a naiver younger version of me asked some good questions.

Anyway, probabilities. There's a reason I keep coming back to this question: It shows up all the time in gamedev. For those of you who don't follow StackOverflow links (wouldn't blame you), or in case the link is no longer accessible: Here's the original question:
I have an array of N elements (representing the N letters of a given alphabet), and each cell of the array holds an integer value, that integer value meaning the number of occurrences in a given text of that letter. Now I want to randomly choose a letter from all of the letters in the alphabet, based on his number of appearances with the given constraints:
  • If the letter has a positive (nonzero) value, then it can be always chosen by the algorithm (with a bigger or smaller probability, of course).
  • If a letter A has a higher value than a letter B, then it has to be more likely to be chosen by the algorithm.
Now, taking that into account, I've come up with a simple algorithm that might do the job, but I was just wondering if there was a better thing to do. This seems to be quite fundamental, and I think there might be more clever things to do in order to accomplish this more efficiently. This is the algorithm i thought:
  • Add up all the frequencies in the array. Store it in SUM
  • Choosing up a random value from 0 to SUM. Store it in RAN
  • While RAN > 0, Starting from the first, visit each cell in the array (in order), and subtract the value of that cell from RAN
  • The last visited cell is the chosen one
So, is there a better thing to do than this? Am I missing something?

I'm aware most modern computers can compute this so fast I won't even notice if my algorithm is inefficient, so this is more of a theoretical question rather than a practical one.

I prefer an explained algorithm rather than just code for an answer, but If you're more comfortable providing your answer in code, I have no problem with that.
Let's take a moment to appreciate a time where StackOverflow was filled with good, insightful questions (such as mine :nkoCool:) and answers instead of burned-out mods closing questions they don't understand as duplicate of other questions they never bothered checking.

I don't even know what the original use case for this was, but if I had to take a guess, I was messing with the dark arts, trying to implement a markov chain generator. Either way I kept coming back to this problem and many times it has to do with designing gameplay systems, like having a boss pick its next attack, or picking where I'll spawn my next enemy where some areas are "hotter" than others, which was today's problem, actually!

Let's take a look at the accepted solution :blobpeek:
The idea:
  • Iterate through all the elements and set the value of each element as the cumulative frequency thus far.
  • Generate a random number between 1 and the sum of all frequencies
  • Do a binary search on the values for this number (finding the first value greater than or equal to the number).
Example:

Code: Select all

Element    A B C D
Frequency  1 4 3 2
Cumulative 1 5 8 10
Generate a random number in the range 1-10 (1+4+3+2 = 10, the same as the last value in the cumulative list), do a binary search, which will return values as follows:

Code: Select all

Number   Element returned
1        A
2        B
3        B
4        B
5        B
6        C
7        C
8        C
9        D
10       D
Now, I must admit, even today, this answer flies slightly over my head. I wouldn't blame you if putting that binary search in there makes you feel uneasy, especially when you still have to iterate through all the elements so the complexity of the whole thing would be linear anyway. I have implemented this version at least once in the past and it seems to work, and things like binary searches tickled my past self's brain in the right way, so I marked it as accepted.

But these days, the answer I've converged towards is actually the algorhm I initially suggested in my question, not the answer. I've implemented it many times across many programming languages. It's good enough, and I don't think we're getting anything from the binary search over a linear scan for the small lists I typically deal with.

This was the latest iteration of it, I wrote it today as a utility function because there were at least three inline instances of it in Iris Blade and while I appreciate ths fun kata I keep coming back to, I think it's time to encapsulate it in a tiny utility function:
image.png
So anyway, this is the StackOverflow *question*, not answer, I keep coming back to ^^

Attachments


image.png (89.86 KiB)

]]>
<![CDATA[Game Programming :: Re: The one Stack Overflow question I keep coming back to :: Reply by Sugui]]> 2026-03-07T16:14:32+00:00 2026-03-07T16:14:32+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=25&p=48#p48
But I always think that, unless optimization is crucial, it's always better a code that is more easy to understand than another one that is 0.01% more efficient and requires more effort to read. You can always return to the answer if this somewhat becomes a bottleneck (a very large map, an aggresive spawn of enemies...).

Nice post, never thought I would find you in a StackOverflow question. :blobcatgiggle: :heart_purple:]]>

But I always think that, unless optimization is crucial, it's always better a code that is more easy to understand than another one that is 0.01% more efficient and requires more effort to read. You can always return to the answer if this somewhat becomes a bottleneck (a very large map, an aggresive spawn of enemies...).

Nice post, never thought I would find you in a StackOverflow question. :blobcatgiggle: :heart_purple:]]>
<![CDATA[Game Programming :: Re: The one Stack Overflow question I keep coming back to :: Reply by celes]]> 2026-03-07T20:57:04+00:00 2026-03-07T20:57:04+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=25&p=50#p50 ]]> ]]> <![CDATA[Game Programming :: Testing video games :: Author Kampffrosch]]> 2026-03-14T10:04:38+00:00 2026-03-14T10:04:38+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=31&p=75#p75 The simplest, most obvious form of testing is just turning the game on and playing a round.
This already ensures a couple things:
- the window system and graphics are initialized correctly (/on my machine/)
- controls work for my setup
- the fundamental gameplay loop works as I expect
- the game does not crash instantly because of something stupid
- ...

A big advantage here is that you can notice issues this way you could not predict.
You can get greater coverage of the state space just by playing more and adding more players (play testers).

But maybe there are some specific things you want to focus on.
A "gym" or a debug console for setting up a specific environment helps here.
As does quicksave/quickload.

And a list of requirements. A checklist if you will.
Fundamentally a test is nothing but a scenario with a desired outcome.
For really gnarly cases I'd recommend actually having a pre-release checklist that you go through, like a pilot before take-off.
I started doing that at work after a botched release :(

Doing all of this manually is a lot of work though, especially once a game grows.
It'd be nice if the computer could check things like "if I hit this small enemy in this specific angle, it does not fly through a wall".
Hell, I'd like to cover all angles while I am at it. And all enemies.
Replace above with whatever is a common problem in your game, there is always something.
For things like this some automatic testing is in order.

Automatic testing also follows the usual recipe for test: set up a scenario + check expected outcomes.
In a way it feels like nailing down the behavior of the game.
Ossification of APIs is a real concern.
Therefore I like to start writing tests from the most outer layer I can inject state into and read results out of.
I literally spawn the whole game state, set it up for a scenario and then let it progress a bit before doing asserts.
In the legacy simulation I maintain the only change I had to make to support this was putting a proxy call before the system clock and adding one function for injecting input data.

It is always a judgment call on what method of testing leads to the best results with the least amount of effort.
There are two areas where automatic testing especially shines which I want to mention: regressions and large scale refactors.
Regressions sneak in time and time again. If I notice a pattern there I build a test.
Refactors change the internals of the game, but are supposed to keep the behavior the same.
Pinning the behavior via tests is great for this.

How do you test your games? Have you tried automated testing (for games or simulations) before? Did it go well?]]>
The simplest, most obvious form of testing is just turning the game on and playing a round.
This already ensures a couple things:
- the window system and graphics are initialized correctly (/on my machine/)
- controls work for my setup
- the fundamental gameplay loop works as I expect
- the game does not crash instantly because of something stupid
- ...

A big advantage here is that you can notice issues this way you could not predict.
You can get greater coverage of the state space just by playing more and adding more players (play testers).

But maybe there are some specific things you want to focus on.
A "gym" or a debug console for setting up a specific environment helps here.
As does quicksave/quickload.

And a list of requirements. A checklist if you will.
Fundamentally a test is nothing but a scenario with a desired outcome.
For really gnarly cases I'd recommend actually having a pre-release checklist that you go through, like a pilot before take-off.
I started doing that at work after a botched release :(

Doing all of this manually is a lot of work though, especially once a game grows.
It'd be nice if the computer could check things like "if I hit this small enemy in this specific angle, it does not fly through a wall".
Hell, I'd like to cover all angles while I am at it. And all enemies.
Replace above with whatever is a common problem in your game, there is always something.
For things like this some automatic testing is in order.

Automatic testing also follows the usual recipe for test: set up a scenario + check expected outcomes.
In a way it feels like nailing down the behavior of the game.
Ossification of APIs is a real concern.
Therefore I like to start writing tests from the most outer layer I can inject state into and read results out of.
I literally spawn the whole game state, set it up for a scenario and then let it progress a bit before doing asserts.
In the legacy simulation I maintain the only change I had to make to support this was putting a proxy call before the system clock and adding one function for injecting input data.

It is always a judgment call on what method of testing leads to the best results with the least amount of effort.
There are two areas where automatic testing especially shines which I want to mention: regressions and large scale refactors.
Regressions sneak in time and time again. If I notice a pattern there I build a test.
Refactors change the internals of the game, but are supposed to keep the behavior the same.
Pinning the behavior via tests is great for this.

How do you test your games? Have you tried automated testing (for games or simulations) before? Did it go well?]]>
<![CDATA[Game Programming :: Re: Testing video games :: Reply by celes]]> 2026-03-14T16:20:42+00:00 2026-03-14T16:20:42+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=31&p=77#p77
Even if I'm not in the school of "games should have tests", there's stuff here I definitely agree with. For one, I should start writing the checklist because too often I do this sort of stuff too hastily / informally and it's such an easy change to do.

I 100% agree on the "gym"s I didn't know that was the word for it, I call it "playgrounds", but yes, little scripts that set up a scene with a bunch of relevant game systems and entities so that I can quickly test features.

The difference for me is that most of these tests are manual :blobcatthink: I'll automate the most mechanical part away (setting up the scene) but I never went as far as simulating player inputs.

It feels like our views on this are also probably influenced by the kind of games we usually make. For a tile-based game I can easily input "move three tiles right, one down, and then attack twice", and that leads to a fully deterministic scenario you can test. But for the style of games I usually develop testing something like this is never so simple. I see two options (1) Work with some sort of fake pid controller that will simulate player joystick input and make them navigate a list of waypoints, RTS-style or (2) use pre-recorded inputs.

Option (1) feels a bit awkward, because real players will not use the same movement patterns as the pid controller, which will always be more precise and robotic, so the test is not testing the exact same thing as players will be doing.

As for (2), i wouldn't even consider it as it sounds like it would lead to way more trouble than it's worth. Having to re-record inputs after I make a tiny tweak to my character controller in every test, even if those tests are testing systems I haven't touched in months.

That said, (1) sounds interesting and something worth exploring, but I don't know nearly enough control theory to make something that would be good at simulating player input to achieve outcomes. It sounds more like a fun research rabbit hole than a "tried and trusted gamedev pattern".]]>

Even if I'm not in the school of "games should have tests", there's stuff here I definitely agree with. For one, I should start writing the checklist because too often I do this sort of stuff too hastily / informally and it's such an easy change to do.

I 100% agree on the "gym"s I didn't know that was the word for it, I call it "playgrounds", but yes, little scripts that set up a scene with a bunch of relevant game systems and entities so that I can quickly test features.

The difference for me is that most of these tests are manual :blobcatthink: I'll automate the most mechanical part away (setting up the scene) but I never went as far as simulating player inputs.

It feels like our views on this are also probably influenced by the kind of games we usually make. For a tile-based game I can easily input "move three tiles right, one down, and then attack twice", and that leads to a fully deterministic scenario you can test. But for the style of games I usually develop testing something like this is never so simple. I see two options (1) Work with some sort of fake pid controller that will simulate player joystick input and make them navigate a list of waypoints, RTS-style or (2) use pre-recorded inputs.

Option (1) feels a bit awkward, because real players will not use the same movement patterns as the pid controller, which will always be more precise and robotic, so the test is not testing the exact same thing as players will be doing.

As for (2), i wouldn't even consider it as it sounds like it would lead to way more trouble than it's worth. Having to re-record inputs after I make a tiny tweak to my character controller in every test, even if those tests are testing systems I haven't touched in months.

That said, (1) sounds interesting and something worth exploring, but I don't know nearly enough control theory to make something that would be good at simulating player input to achieve outcomes. It sounds more like a fun research rabbit hole than a "tried and trusted gamedev pattern".]]>
<![CDATA[Game Programming :: When to global, and how global is a global :: Author celes]]> 2026-03-13T19:42:36+00:00 2026-03-13T19:42:36+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=68#p68

But you have to distinguish, because there's various levels of what one means by "global", but first let's discuss the obvious:

We like globals because they're always at reach

The nice thing about a global is that you can access it from any and everywhere with little effort. Programmers who say they never use globals are often lying, they just forgot about their beloved print. But we all tend to forget about printing unless we're pure of heart so I'm not gonna blame anyone.

Anyway, the print function is always there: It's effortless, we don't need to pass a "printer" around, we don't need to do "dependency injection" so that we get a printer to print our strings, print is just there. It is ineffable.

In gamedev, there's a bunch of things I often want to be as convenient as printing. I'm gonna list a few:
  • Get a hold of the "scene", "map", "world" or whatever it may be. This could be an ECS if you're one of those. You also want to query that scene. I don't use an ECS but I still want to get the "EnemySpawner" from the scene, or iterate all instances of Player. There's various ways to do this. My point? I need the ability to do this everywhere because it keeps coming up everywhere.
  • Play a fire-and-forget sound effect. Seriously, playing the sound effect goes next to the line of code that performs the action that produces the sound effect. You can't convince me otherwise. If you have opinions about this, don't show me your "clean code", show me your sound design.
  • Firing off tweens. Similarly, I want the ability to fire off a tween pretty much anywhere. I'll write about how I do tweens some other time but this is the kind of thing I want to do at a "print" level of convenience. I want my game to be juicy so animations are everywhere. I cannot work with the mindset that there's some "animator" that looks at the pure state of the game world and makes decisions based on that. But that might just be me.
  • Drawing debug and non-debug geometry, but especially debug geometry. If your "framework" doesn't let me call a bunch of draw_line and draw_rect to debug one of those tricky computational geometry snippets I am awful at, you've already lost me. No I don't want to spawn a "DebugRenderer" in the scene so I can get a hold of it. That's as dumb as passing a "printer" around to just print.
What color is your glob.. nah I'm just kidding. Unless...?

But there's flavors of globals. This was a lesson that took me a game and a half to figure out, so it's worth putting out there, because I've seen people make this mistake in actual engines and frameworks where they put singletons into static variables.

And you know, this sort of thing truly depends on the use case. I've found I want roughly two kinds of globals in my game:
  • Scene Globals: These live in the scene. Accessing them means getting them from the scene tree. If you change scenes, you get a different set of globals. So, every access goes through some logic to get the currently active scene, then pick the thing from the currently active scene. You can replace scene with ECS if that helps you sleep at night, but my point is these have to be accessible from anywhere: No passing around, no funny dependency injection shenanigans.
  • Game Globals: These are true globals, just one for the duration of the program. They use static variables, or whatever it is that your language calls them.
Functionally, they look exactly the same, but you gotta be careful which one you pick. For example, today I fixed a bug in my game where the Tween manager was a game global, when it should've been a scene global. This was causing crashes on stream the other day when I restarted the scene after doing some tasks (like breaking a barrel and before the barrel had finished its breaking animation). By keeping the tween manager as a scene local, all tweens are frozen in time when I switch to another scene. If I destroy the scene, all tweens will immediately stop playing too.

But not everything should be a scene global. Some things are truly global. In my game I have a few: The editor (there's just one editor and that's enough of a headache! I don't need two), the renderer, the input system, the audio system, the scene system (not scenes themselves but the thing that has the list of scenes and knows the active one)... I think audio is the best example of a system that is *truly* global. Of course you can come up with counterexamples to all these. My point is: In my game, these are global, because I'll never need multiple instances of them.

Despite all I'm saying, I think the lesson here, though, is that often when we want a "global" something what we need is something closer to scene local. :nkoThink: At least that's been the lesson for me. Things like a tween manager, cpu particles, "arenas" to hold various forms of gameplay objects... These are all scene-global! Once the foundation for a game is set (the "engine", if you may), pretty much everything you'll need are scene globals.

Oh and there's another use case where I'll go for statics: Storing the delta time. I don't like passing it around because it's pointless busywork, but I want access to it to be as quick as possible, so I always store the delta time and a bunch other time related variables as static. That way I can conjure them from thin air anywhere in my code, e.g. Time.deltaTime, and I know the access is as quick as it gets: Just a memory read (often cached), no hashmap lookups or any weird nonsense. I still dread the days in which I had to choose between passing something around for performance or locking an RwLock for convenience...]]>


But you have to distinguish, because there's various levels of what one means by "global", but first let's discuss the obvious:

We like globals because they're always at reach

The nice thing about a global is that you can access it from any and everywhere with little effort. Programmers who say they never use globals are often lying, they just forgot about their beloved print. But we all tend to forget about printing unless we're pure of heart so I'm not gonna blame anyone.

Anyway, the print function is always there: It's effortless, we don't need to pass a "printer" around, we don't need to do "dependency injection" so that we get a printer to print our strings, print is just there. It is ineffable.

In gamedev, there's a bunch of things I often want to be as convenient as printing. I'm gonna list a few:
  • Get a hold of the "scene", "map", "world" or whatever it may be. This could be an ECS if you're one of those. You also want to query that scene. I don't use an ECS but I still want to get the "EnemySpawner" from the scene, or iterate all instances of Player. There's various ways to do this. My point? I need the ability to do this everywhere because it keeps coming up everywhere.
  • Play a fire-and-forget sound effect. Seriously, playing the sound effect goes next to the line of code that performs the action that produces the sound effect. You can't convince me otherwise. If you have opinions about this, don't show me your "clean code", show me your sound design.
  • Firing off tweens. Similarly, I want the ability to fire off a tween pretty much anywhere. I'll write about how I do tweens some other time but this is the kind of thing I want to do at a "print" level of convenience. I want my game to be juicy so animations are everywhere. I cannot work with the mindset that there's some "animator" that looks at the pure state of the game world and makes decisions based on that. But that might just be me.
  • Drawing debug and non-debug geometry, but especially debug geometry. If your "framework" doesn't let me call a bunch of draw_line and draw_rect to debug one of those tricky computational geometry snippets I am awful at, you've already lost me. No I don't want to spawn a "DebugRenderer" in the scene so I can get a hold of it. That's as dumb as passing a "printer" around to just print.
What color is your glob.. nah I'm just kidding. Unless...?

But there's flavors of globals. This was a lesson that took me a game and a half to figure out, so it's worth putting out there, because I've seen people make this mistake in actual engines and frameworks where they put singletons into static variables.

And you know, this sort of thing truly depends on the use case. I've found I want roughly two kinds of globals in my game:
  • Scene Globals: These live in the scene. Accessing them means getting them from the scene tree. If you change scenes, you get a different set of globals. So, every access goes through some logic to get the currently active scene, then pick the thing from the currently active scene. You can replace scene with ECS if that helps you sleep at night, but my point is these have to be accessible from anywhere: No passing around, no funny dependency injection shenanigans.
  • Game Globals: These are true globals, just one for the duration of the program. They use static variables, or whatever it is that your language calls them.
Functionally, they look exactly the same, but you gotta be careful which one you pick. For example, today I fixed a bug in my game where the Tween manager was a game global, when it should've been a scene global. This was causing crashes on stream the other day when I restarted the scene after doing some tasks (like breaking a barrel and before the barrel had finished its breaking animation). By keeping the tween manager as a scene local, all tweens are frozen in time when I switch to another scene. If I destroy the scene, all tweens will immediately stop playing too.

But not everything should be a scene global. Some things are truly global. In my game I have a few: The editor (there's just one editor and that's enough of a headache! I don't need two), the renderer, the input system, the audio system, the scene system (not scenes themselves but the thing that has the list of scenes and knows the active one)... I think audio is the best example of a system that is *truly* global. Of course you can come up with counterexamples to all these. My point is: In my game, these are global, because I'll never need multiple instances of them.

Despite all I'm saying, I think the lesson here, though, is that often when we want a "global" something what we need is something closer to scene local. :nkoThink: At least that's been the lesson for me. Things like a tween manager, cpu particles, "arenas" to hold various forms of gameplay objects... These are all scene-global! Once the foundation for a game is set (the "engine", if you may), pretty much everything you'll need are scene globals.

Oh and there's another use case where I'll go for statics: Storing the delta time. I don't like passing it around because it's pointless busywork, but I want access to it to be as quick as possible, so I always store the delta time and a bunch other time related variables as static. That way I can conjure them from thin air anywhere in my code, e.g. Time.deltaTime, and I know the access is as quick as it gets: Just a memory read (often cached), no hashmap lookups or any weird nonsense. I still dread the days in which I had to choose between passing something around for performance or locking an RwLock for convenience...]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by Sugui]]> 2026-03-13T20:22:57+00:00 2026-03-13T20:22:57+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=69#p69
But we all tend to forget about printing unless we're pure of heart so I'm not gonna blame anyone.
Do you think we functional weirdos here don't use an

Code: Select all

unsafePerformIO $ print "help!!"
to debug? :blobcatgiggle:

Nice article ^^ globals simplify so much the gamedev process if you know how to use them well.

I wonder if you have a variable that stores what is the current scene. Would you consider that variable global too? Or local? Because it refers to the scene itself.

:blobcatrss: Wait, what's that sound? I'm hearing from the distance to the clean codersâ„¢ shouting that Singleton is an antipattern and that you should drill every value through dependency injection /j]]>
But we all tend to forget about printing unless we're pure of heart so I'm not gonna blame anyone.
Do you think we functional weirdos here don't use an

Code: Select all

unsafePerformIO $ print "help!!"
to debug? :blobcatgiggle:

Nice article ^^ globals simplify so much the gamedev process if you know how to use them well.

I wonder if you have a variable that stores what is the current scene. Would you consider that variable global too? Or local? Because it refers to the scene itself.

:blobcatrss: Wait, what's that sound? I'm hearing from the distance to the clean codersâ„¢ shouting that Singleton is an antipattern and that you should drill every value through dependency injection /j]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by palas]]> 2026-03-14T00:40:30+00:00 2026-03-14T00:40:30+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=72#p72
1. Read-only
Basically constants, though their value doesn't have to stay immutable forever. From your post, delta time would fit this description.

2. Write-only
The opposite, but just as harmless. I guess we can consider the audio system to be write-only? Or drawing to the screen, as it can't really affect other parts of the codebase.

3. Read-write
Most spicy ones, which are read and written to all over the place. If globals ever do any harm, it's these ones, so it's important to consider which parts of the code really need such power.

Types 1 and 2 are in the safe category, so spamming them won't hurt too much. When it comes to 3, they directly affect how well can one reason about the codebase locally.
This is still mostly unsolved for me. It's not about whether or not to use them (can't really gamedev much without at least some global scene/level manager), but how to design in a way to keep as much locality as possible. For example, if I'm storing references to game objects, they can still be deleted from anywhere else. What happens if they do get deleted? Idk :akko_shrug:]]>

1. Read-only
Basically constants, though their value doesn't have to stay immutable forever. From your post, delta time would fit this description.

2. Write-only
The opposite, but just as harmless. I guess we can consider the audio system to be write-only? Or drawing to the screen, as it can't really affect other parts of the codebase.

3. Read-write
Most spicy ones, which are read and written to all over the place. If globals ever do any harm, it's these ones, so it's important to consider which parts of the code really need such power.

Types 1 and 2 are in the safe category, so spamming them won't hurt too much. When it comes to 3, they directly affect how well can one reason about the codebase locally.
This is still mostly unsolved for me. It's not about whether or not to use them (can't really gamedev much without at least some global scene/level manager), but how to design in a way to keep as much locality as possible. For example, if I'm storing references to game objects, they can still be deleted from anywhere else. What happens if they do get deleted? Idk :akko_shrug:]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by celes]]> 2026-03-14T09:38:39+00:00 2026-03-14T09:38:39+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=73#p73
Sugui wrote: Fri Mar 13, 2026 8:22 pm I wonder if you have a variable that stores what is the current scene. Would you consider that variable global too? Or local? Because it refers to the scene itself.
I have that variable! And it's a game-global one: Even if it refers to the scene, the list of scenes and the handle to of the currently active scene must live somewhere in memory, and they can't live inside a scene :blobcatnodfast:

This is similar to how godot does it. And, half-relatedly, I suspect godot's choice of singletons, similar to my own, doesn't let them render / process input for two different scene trees at the same time, but maybe I'm wrong! Either way you're unlikely to need that for a game unless you have a very clear use case in mind.
palas wrote: Sat Mar 14, 2026 12:40 am Types 1 and 2 are in the safe category, so spamming them won't hurt too much. When it comes to 3, they directly affect how well can one reason about the codebase locally.
This is still mostly unsolved for me. It's not about whether or not to use them (can't really gamedev much without at least some global scene/level manager), but how to design in a way to keep as much locality as possible. For example, if I'm storing references to game objects, they can still be deleted from anywhere else. What happens if they do get deleted? Idk
I really like the idea behind "write-only" globals and you've made me realize a lot of the globals I use are like that. Print is definitely a write-only global, at least in spirit (I don't care if you can access the internal buffer, nobody will).

Similarly, one-off particles or one-off sfx are write only globals in the same way, you fire the thing and you forget about it!

But not all my globals are write-only, not by far :blobcatthink: There's at least a dozen systems that access the player to check various things, such as enemies or projectiles or coin drops... they all need to know where the player is. But I've realized most of the time, even if I get a read-write reference to the player I'm conceptually treating it as read-only. I'd never modify the player from enemy code because that feels intuitively wrong! Maybe it's years of borrow checker that hammered these ideas into my brain.

As for references to game objects that can be deleted, I think that is *the* question. The one that haunts every gamedev. The best way to achieve this imo is to work with two kinds of references: (1) short-lived references that you don't keep across frames and (2) weak references that you need to check before you dereference

You need to enforce that accessing game objects that may have died in a previous frame forces you to check that the object is still alive. This can take several forms. Most of my global accessors return a nullable type, so my code is filled with:

Code: Select all

if (SceneGraph.GetFirstEntityInGroup("Player") is not {} player) return;
This often is enough to protect from misuse. Though there's been cases in which I can shoot myself in the foot:

Code: Select all

if (SceneGraph.GetFirstEntityInGroup("Player") is not {} player) return;
TweenManager.Spawn(..., tick: (t) => {
    player.color = somethingSomething(t);
});
This is the #1 cause of "accessed a dead entity" kind of crashes I get in practice, because that player reference was meant to be short lived and I captured it inside a tween. I never really despawn the player except during level transitions or restarts, so these crashes are rare: Rarity makes them more dangerous, not less.

I could come up with various APIs that make this kind of error more difficult and I have some ideas. But this is the kind of thing I try to not obsess over from the get-go: I need to see the design space for what it is before I go about "designing" a solution! In this particular case, I found not stopping all running tweens before a scene transition *was* the actual issue, and fixing it that drastically reduced the amount of these errors.

For entities like enemies that regularly despawn, these issues (when they happen, which is not that often) lead to more frequent crashes so I catch them during regular development. Often the solution is just throwing an if statement checking if the entity is valid, which is only ensured by my own discipline. Something I'm sure makes the average crab very nervous :blobcatgiggle:

Oh and half-unrelatedly: The "is" operator in C# is such a powerful pattern, because it is a generalization of all those Rust specific syntaxes (if-let, let-else...).]]>
Sugui wrote: Fri Mar 13, 2026 8:22 pm I wonder if you have a variable that stores what is the current scene. Would you consider that variable global too? Or local? Because it refers to the scene itself.
I have that variable! And it's a game-global one: Even if it refers to the scene, the list of scenes and the handle to of the currently active scene must live somewhere in memory, and they can't live inside a scene :blobcatnodfast:

This is similar to how godot does it. And, half-relatedly, I suspect godot's choice of singletons, similar to my own, doesn't let them render / process input for two different scene trees at the same time, but maybe I'm wrong! Either way you're unlikely to need that for a game unless you have a very clear use case in mind.
palas wrote: Sat Mar 14, 2026 12:40 am Types 1 and 2 are in the safe category, so spamming them won't hurt too much. When it comes to 3, they directly affect how well can one reason about the codebase locally.
This is still mostly unsolved for me. It's not about whether or not to use them (can't really gamedev much without at least some global scene/level manager), but how to design in a way to keep as much locality as possible. For example, if I'm storing references to game objects, they can still be deleted from anywhere else. What happens if they do get deleted? Idk
I really like the idea behind "write-only" globals and you've made me realize a lot of the globals I use are like that. Print is definitely a write-only global, at least in spirit (I don't care if you can access the internal buffer, nobody will).

Similarly, one-off particles or one-off sfx are write only globals in the same way, you fire the thing and you forget about it!

But not all my globals are write-only, not by far :blobcatthink: There's at least a dozen systems that access the player to check various things, such as enemies or projectiles or coin drops... they all need to know where the player is. But I've realized most of the time, even if I get a read-write reference to the player I'm conceptually treating it as read-only. I'd never modify the player from enemy code because that feels intuitively wrong! Maybe it's years of borrow checker that hammered these ideas into my brain.

As for references to game objects that can be deleted, I think that is *the* question. The one that haunts every gamedev. The best way to achieve this imo is to work with two kinds of references: (1) short-lived references that you don't keep across frames and (2) weak references that you need to check before you dereference

You need to enforce that accessing game objects that may have died in a previous frame forces you to check that the object is still alive. This can take several forms. Most of my global accessors return a nullable type, so my code is filled with:

Code: Select all

if (SceneGraph.GetFirstEntityInGroup("Player") is not {} player) return;
This often is enough to protect from misuse. Though there's been cases in which I can shoot myself in the foot:

Code: Select all

if (SceneGraph.GetFirstEntityInGroup("Player") is not {} player) return;
TweenManager.Spawn(..., tick: (t) => {
    player.color = somethingSomething(t);
});
This is the #1 cause of "accessed a dead entity" kind of crashes I get in practice, because that player reference was meant to be short lived and I captured it inside a tween. I never really despawn the player except during level transitions or restarts, so these crashes are rare: Rarity makes them more dangerous, not less.

I could come up with various APIs that make this kind of error more difficult and I have some ideas. But this is the kind of thing I try to not obsess over from the get-go: I need to see the design space for what it is before I go about "designing" a solution! In this particular case, I found not stopping all running tweens before a scene transition *was* the actual issue, and fixing it that drastically reduced the amount of these errors.

For entities like enemies that regularly despawn, these issues (when they happen, which is not that often) lead to more frequent crashes so I catch them during regular development. Often the solution is just throwing an if statement checking if the entity is valid, which is only ensured by my own discipline. Something I'm sure makes the average crab very nervous :blobcatgiggle:

Oh and half-unrelatedly: The "is" operator in C# is such a powerful pattern, because it is a generalization of all those Rust specific syntaxes (if-let, let-else...).]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by palas]]> 2026-03-14T11:00:28+00:00 2026-03-14T11:00:28+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=76#p76
Oh and half-unrelatedly: The "is" operator in C# is such a powerful pattern, because it is a generalization of all those Rust specific syntaxes (if-let, let-else...).
It is indeed, though I'm really not a fan of the scope of locals introduced inside ifs being extended outside of the if { }. This combined with the absence of shadowing is so annoying! But at the time it's the only way to achieve let-else. And tbh, I'ld prefer let-else any day instead of what you have to do in C#.

As for the API to handle potentially missing entities... The first thing that comes to mind is Handle<T> with the following api:

Code: Select all

fn Entity::handle(self) -> Handle<Self>;
fn Handle<T>::get(self) -> T?;
]]>
Oh and half-unrelatedly: The "is" operator in C# is such a powerful pattern, because it is a generalization of all those Rust specific syntaxes (if-let, let-else...).
It is indeed, though I'm really not a fan of the scope of locals introduced inside ifs being extended outside of the if { }. This combined with the absence of shadowing is so annoying! But at the time it's the only way to achieve let-else. And tbh, I'ld prefer let-else any day instead of what you have to do in C#.

As for the API to handle potentially missing entities... The first thing that comes to mind is Handle<T> with the following api:

Code: Select all

fn Entity::handle(self) -> Handle<Self>;
fn Handle<T>::get(self) -> T?;
]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by EmiSocks]]> 2026-03-14T16:42:11+00:00 2026-03-14T16:42:11+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=78#p78
The uses I encounter are pretty different, since I'm primarily a web dev, not a game dev. But the idea I've arrived at is that I find the current way that we do variable scopes pretty annoying. Like, the languages I know, variables are scoped by where they physically (lexically?) are, like local to a function or a script or a try/catch block or whatever. Attaching a variable to a class as a member makes a BIT more sense to vee, logically, but it's still kind of limited!

Instead the way I THINK about variable scopes is more... in terms of their lifetime, I guess! Like, I think of a value as being in the scope of "the whole application", or in the scope of "a request". In practice you have to simulate that scope with like, a dependency injection framework, or an object that you pass to every request callback one by one, etc. For a game I guess the analogous thing would be thinking of a variable as scoped to a frame, or a scene, or something. But that is different from "variable scope" at the language level!

I wonder if it would make any sense to have a language where you can declare scopes explicitly, and then declare at compile time what scopes a function is meant to access. I'm thinking of something like how if you use a State monad in Haskell, you have to declare that in the return type of the function, but... ideally more composable and less annoying than that.]]>

The uses I encounter are pretty different, since I'm primarily a web dev, not a game dev. But the idea I've arrived at is that I find the current way that we do variable scopes pretty annoying. Like, the languages I know, variables are scoped by where they physically (lexically?) are, like local to a function or a script or a try/catch block or whatever. Attaching a variable to a class as a member makes a BIT more sense to vee, logically, but it's still kind of limited!

Instead the way I THINK about variable scopes is more... in terms of their lifetime, I guess! Like, I think of a value as being in the scope of "the whole application", or in the scope of "a request". In practice you have to simulate that scope with like, a dependency injection framework, or an object that you pass to every request callback one by one, etc. For a game I guess the analogous thing would be thinking of a variable as scoped to a frame, or a scene, or something. But that is different from "variable scope" at the language level!

I wonder if it would make any sense to have a language where you can declare scopes explicitly, and then declare at compile time what scopes a function is meant to access. I'm thinking of something like how if you use a State monad in Haskell, you have to declare that in the return type of the function, but... ideally more composable and less annoying than that.]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by celes]]> 2026-03-14T16:57:43+00:00 2026-03-14T16:57:43+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=80#p80
EmiSocks wrote: Sat Mar 14, 2026 4:42 pm Instead the way I THINK about variable scopes is more... in terms of their lifetime, I guess!
Ohh!! This reminds me a lot of dynamic scoping :hammyeyes: I've only ever encountered dynamic scoping in clojure, but I think it's a popular concept in other lisps and maybe some older languages. With those, you can do something like this (excuse the parentheses, this is probably wrong but should get the point across):

Code: Select all

(def ^:dynamic *user-session* nil)

(def auth-middleware [request, next-fn]
  (if-let [session (validate-user-session (-> request :cookies :user-session-cookie))]
     (binding [*user-session* session]
       (next-fn))
     #_else 
     (throw "somethingsomething unauthenticated")))
Here, we define a dynamic var *user-session*[1] and it is set in a middleware so that code reading the variable inside a request handlers will get the user session for that request, but code outside an authenticated handler reading the variable will see null. This is done by setting a value for the variable inside a "binding" block: Anything that happens within that block (not lexically in that function but, dynamically at runtime) will see the bound value, but code that is outside the block, even code that may be running at the same time (such as other threads spawned by other requests) will not see this binding and may instead see other bindings or none at all.

The system is very powerful and you can even have an arbitrary level of nested bindings, which to some people is too much power, but to me is the right amount of power!

Another great use case for dynamic variables is testing. It's customary in clojure to put things like your database connection in a dynamic variable. That way you can bind it to the actual connection in real requests handlers and bind it to something else in tests. So in some sense, it's kind of dependency-injection-like, but requiring far less magic.

And now I wonder why I don't use these more often in game development... :blobcatthink:

[1] For the curious, the *s surrounding the variable name are actually a lisp/clojure naming convention for this and mean nothing, it's just part of the name. In lisp, you can use most characters inside a symbol, including the common math operators like +, -, * and more often than not also /, tho / is special in some lisps.]]>
EmiSocks wrote: Sat Mar 14, 2026 4:42 pm Instead the way I THINK about variable scopes is more... in terms of their lifetime, I guess!
Ohh!! This reminds me a lot of dynamic scoping :hammyeyes: I've only ever encountered dynamic scoping in clojure, but I think it's a popular concept in other lisps and maybe some older languages. With those, you can do something like this (excuse the parentheses, this is probably wrong but should get the point across):

Code: Select all

(def ^:dynamic *user-session* nil)

(def auth-middleware [request, next-fn]
  (if-let [session (validate-user-session (-> request :cookies :user-session-cookie))]
     (binding [*user-session* session]
       (next-fn))
     #_else 
     (throw "somethingsomething unauthenticated")))
Here, we define a dynamic var *user-session*[1] and it is set in a middleware so that code reading the variable inside a request handlers will get the user session for that request, but code outside an authenticated handler reading the variable will see null. This is done by setting a value for the variable inside a "binding" block: Anything that happens within that block (not lexically in that function but, dynamically at runtime) will see the bound value, but code that is outside the block, even code that may be running at the same time (such as other threads spawned by other requests) will not see this binding and may instead see other bindings or none at all.

The system is very powerful and you can even have an arbitrary level of nested bindings, which to some people is too much power, but to me is the right amount of power!

Another great use case for dynamic variables is testing. It's customary in clojure to put things like your database connection in a dynamic variable. That way you can bind it to the actual connection in real requests handlers and bind it to something else in tests. So in some sense, it's kind of dependency-injection-like, but requiring far less magic.

And now I wonder why I don't use these more often in game development... :blobcatthink:

[1] For the curious, the *s surrounding the variable name are actually a lisp/clojure naming convention for this and mean nothing, it's just part of the name. In lisp, you can use most characters inside a symbol, including the common math operators like +, -, * and more often than not also /, tho / is special in some lisps.]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by celes]]> 2026-03-14T19:25:03+00:00 2026-03-14T19:25:03+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=81#p81
palas wrote: Sat Mar 14, 2026 11:00 am It is indeed, though I'm really not a fan of the scope of locals introduced inside ifs being extended outside of the if { }. This combined with the absence of shadowing is so annoying! But at the time it's the only way to achieve let-else. And tbh, I'ld prefer let-else any day instead of what you have to do in C#.
Hmmmm you know what, you're right. Having if-let and let-else as separate constructs lets you differentiate when the bound object will be scoped inside the if statement only and when it will live outside. I had not considered this but the pain point you bring up here is very real.

Then there's the fact you can combine multiple if statements using "is" with boolean operators like && to access a chain of possibly null things, kinda like if-let-chains in Rust, which again makes me think the design of is is very elegant and versatile...
Palas wrote: As for the API to handle potentially missing entities... The first thing that comes to mind is Handle<T> with the following api:

Code: Select all

fn Entity::handle(self) -> Handle<Self>;
fn Handle<T>::get(self) -> T?;
Oh yup! I have things like this. Not for entities (maybe I should?), but for components I have a ComponentRef thing that's basically this. Maybe I should add a similar thing for entities, though nothing would enforce using an entity handle over just passing an entity around, so I'm not fully convinced there :nkoThink:]]>
palas wrote: Sat Mar 14, 2026 11:00 am It is indeed, though I'm really not a fan of the scope of locals introduced inside ifs being extended outside of the if { }. This combined with the absence of shadowing is so annoying! But at the time it's the only way to achieve let-else. And tbh, I'ld prefer let-else any day instead of what you have to do in C#.
Hmmmm you know what, you're right. Having if-let and let-else as separate constructs lets you differentiate when the bound object will be scoped inside the if statement only and when it will live outside. I had not considered this but the pain point you bring up here is very real.

Then there's the fact you can combine multiple if statements using "is" with boolean operators like && to access a chain of possibly null things, kinda like if-let-chains in Rust, which again makes me think the design of is is very elegant and versatile...
Palas wrote: As for the API to handle potentially missing entities... The first thing that comes to mind is Handle<T> with the following api:

Code: Select all

fn Entity::handle(self) -> Handle<Self>;
fn Handle<T>::get(self) -> T?;
Oh yup! I have things like this. Not for entities (maybe I should?), but for components I have a ComponentRef thing that's basically this. Maybe I should add a similar thing for entities, though nothing would enforce using an entity handle over just passing an entity around, so I'm not fully convinced there :nkoThink:]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by palas]]> 2026-03-14T20:37:25+00:00 2026-03-14T20:37:25+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=82#p82
Then there's the fact you can combine multiple if statements using "is" with boolean operators like && to access a chain of possibly null things, kinda like if-let-chains in Rust, which again makes me think the design of is is very elegant and versatile...
On, you mean something like this?

Code: Select all

if value.inner is Inner inner 
  && inner.inner is Inner inner
  && inner.inner is Inner inner {...}
 
This can also be very elegantly expressed in combination with potential-null-access operator.

Code: Select all

if value?.inner?.inner is Inner inner {...}
]]>
Then there's the fact you can combine multiple if statements using "is" with boolean operators like && to access a chain of possibly null things, kinda like if-let-chains in Rust, which again makes me think the design of is is very elegant and versatile...
On, you mean something like this?

Code: Select all

if value.inner is Inner inner 
  && inner.inner is Inner inner
  && inner.inner is Inner inner {...}
 
This can also be very elegantly expressed in combination with potential-null-access operator.

Code: Select all

if value?.inner?.inner is Inner inner {...}
]]>
<![CDATA[Game Programming :: Re: When to global, and how global is a global :: Reply by celes]]> 2026-03-14T22:31:58+00:00 2026-03-14T22:31:58+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=30&p=83#p83
palas wrote: Sat Mar 14, 2026 8:37 pm This can also be very elegantly expressed in combination with potential-null-access operator.

Code: Select all

if value?.inner?.inner is Inner inner {...}
Yup! I meant this, tho null-access is only one ingredient, I like "is" doubling up as pattern matching, especially for a hypothetical language with discriminated unions, you could do something along the lines of:

Code: Select all

if value is Str(s) && s.trim().to_lower() == "potato" { ... }
So while null-access is nice syntactic sugar for the most common case, having the full thing is still quite useful imo!]]>
palas wrote: Sat Mar 14, 2026 8:37 pm This can also be very elegantly expressed in combination with potential-null-access operator.

Code: Select all

if value?.inner?.inner is Inner inner {...}
Yup! I meant this, tho null-access is only one ingredient, I like "is" doubling up as pattern matching, especially for a hypothetical language with discriminated unions, you could do something along the lines of:

Code: Select all

if value is Str(s) && s.trim().to_lower() == "potato" { ... }
So while null-access is nice syntactic sugar for the most common case, having the full thing is still quite useful imo!]]>
<![CDATA[Game Programming :: Tip: Your input system should not be an opaque abstraction :: Author celes]]> 2026-03-14T16:42:54+00:00 2026-03-14T16:42:54+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=32&p=79#p79
In godot, they literally call it input actions, and every godot game uses it, so it's far from an unpopular idea!

But I did realize something. It's something that bit me in carrot survivors, and couldn't quite fix without a pile of hacks. This time, when encountering the same issue I decided to tackle it properly.

Consider this simple (heh) requirement:

"When using a joystick for movement, the character attacks in the direction of movement, unless they're using the right joystick, in that case the charcter attacks in the direction the right joystick is aiming. Oh, and if they're using a mouse forget all this, the direction of attack is the direction the mouse is pointing at from the player."

Now, you still want to go through some generic input layer for the most part, but you can see how the above requirement throws a wrench into the whole thing. The truth is, if you want your game's user experience to be good, you cannot really "abstract away" input devices. Sure, you can do this for the jump key, spacebar or some gamepad button, but anything more complex than that and you need to start delving into specifics.

The issue I'm dealing with rn is close to the one I described above. None of this is rocket science, it's just hard work and lots of iteration to figure out what people need to play in various ways (remember! the more input styles you support, the more accessible your game will be). But one particular aspect annoyed me: How do I know which input mode I'm in?

As an owner of a steam controller, I know how annoying it is when games use mose movement as an indicator that the player is using keyboard and mouse, so I'm not gonna do that. Furthermore, for Iris Blade, there's two keyboard input styles: One that uses the mouse, with Left and Right buttons to attack, and one that uses only keyboard, with the J and K keys for attacks instead[1]. Using keyboard input as a predictor that the player is playing with their mouse is thus not possible.

Having discarded mouse movement and keyboard input, the only reliable indicator that the player wants to attack with the mouse (and thus, we should use mouse position to direct their attack) is the mouse keys themselves... Again, no shit Sherlock, I could've been a bit less roundabout-y about this huh?

But circling back to the original input actions talk, I have an input action defined for the attack action, which is conveniently bound to Left mouse button, the J keyboard key and the X gamepad button all at once. I have a great API for this:

Code: Select all

if (Input.IsActionJustPressed(InputAction.Attack) {
   ...
}
Problem is, if my code has any sort of special logic that depends on the input device, as I described above, I need to undo that, and... check each input device manually? Now that's bad! I was planning on using the input action system to allow configurable key remappings and now I won't be able to... unless?

Actually the solution was simpler than I originally thought:

Code: Select all

if (Input.IsActionJustPressed(InputAction.Attack, out var actionBinding) {
   if (actionBinding.IsMouse()) {
       // mouse aim logic
   }
   else {
       // aim where the character is facing based on movement
   }
}
By adding this optional output parameter, I can check whether the action was pressed, but still get to peek at the internals of the event that produced the action to do custom logic. Now that was such a simple change that I'm very mad no other engine has it. :akko_nope:


[1] I'm planning on making this configurable, but that's a story for another day.]]>

In godot, they literally call it input actions, and every godot game uses it, so it's far from an unpopular idea!

But I did realize something. It's something that bit me in carrot survivors, and couldn't quite fix without a pile of hacks. This time, when encountering the same issue I decided to tackle it properly.

Consider this simple (heh) requirement:

"When using a joystick for movement, the character attacks in the direction of movement, unless they're using the right joystick, in that case the charcter attacks in the direction the right joystick is aiming. Oh, and if they're using a mouse forget all this, the direction of attack is the direction the mouse is pointing at from the player."

Now, you still want to go through some generic input layer for the most part, but you can see how the above requirement throws a wrench into the whole thing. The truth is, if you want your game's user experience to be good, you cannot really "abstract away" input devices. Sure, you can do this for the jump key, spacebar or some gamepad button, but anything more complex than that and you need to start delving into specifics.

The issue I'm dealing with rn is close to the one I described above. None of this is rocket science, it's just hard work and lots of iteration to figure out what people need to play in various ways (remember! the more input styles you support, the more accessible your game will be). But one particular aspect annoyed me: How do I know which input mode I'm in?

As an owner of a steam controller, I know how annoying it is when games use mose movement as an indicator that the player is using keyboard and mouse, so I'm not gonna do that. Furthermore, for Iris Blade, there's two keyboard input styles: One that uses the mouse, with Left and Right buttons to attack, and one that uses only keyboard, with the J and K keys for attacks instead[1]. Using keyboard input as a predictor that the player is playing with their mouse is thus not possible.

Having discarded mouse movement and keyboard input, the only reliable indicator that the player wants to attack with the mouse (and thus, we should use mouse position to direct their attack) is the mouse keys themselves... Again, no shit Sherlock, I could've been a bit less roundabout-y about this huh?

But circling back to the original input actions talk, I have an input action defined for the attack action, which is conveniently bound to Left mouse button, the J keyboard key and the X gamepad button all at once. I have a great API for this:

Code: Select all

if (Input.IsActionJustPressed(InputAction.Attack) {
   ...
}
Problem is, if my code has any sort of special logic that depends on the input device, as I described above, I need to undo that, and... check each input device manually? Now that's bad! I was planning on using the input action system to allow configurable key remappings and now I won't be able to... unless?

Actually the solution was simpler than I originally thought:

Code: Select all

if (Input.IsActionJustPressed(InputAction.Attack, out var actionBinding) {
   if (actionBinding.IsMouse()) {
       // mouse aim logic
   }
   else {
       // aim where the character is facing based on movement
   }
}
By adding this optional output parameter, I can check whether the action was pressed, but still get to peek at the internals of the event that produced the action to do custom logic. Now that was such a simple change that I'm very mad no other engine has it. :akko_nope:


[1] I'm planning on making this configurable, but that's a story for another day.]]>
<![CDATA[Game Programming :: Re: Tip: Your input system should not be an opaque abstraction :: Reply by palas]]> 2026-03-16T03:04:08+00:00 2026-03-16T03:04:08+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=32&p=84#p84
As an owner of a steam controller, I know how annoying it is when games use mouse movement as an indicator that the player is using keyboard and mouse, so I'm not gonna do that.
Very interesting, I've never considered this aspect. So it's not possible to differentiate between someone moving their cursor with a mouse vs steam controller?
Using keyboard input as a predictor that the player is playing with their mouse is thus not possible.
A bit confused by this. Even if the player uses the mouse to attack, they are still using the keyboard for movement. Could attack action be bound to both mouse and keyboard at the same time? That way, both styles are combined in one.
Problem is, if my code has any sort of special logic that depends on the input device, as I described above, I need to undo that, and... check each input device manually?
Ha! Before you showed your API, I've pictured something like:

Code: Select all

if Input.Pressed(InputAction.Attack) is action {
  match action { 
    ActionType::Mouse => {},
    ActionType::Controller => {}
  }
}
I'm very mad no other engine has it. :akko_nope:
Most likely another classic instance of "we do whatever is most simple for a beginner, any slightly more complicated use case is the user's problem".
But, surely every half competent engine has a way to tell whether a mouse or a controller is used, and process each one separately? This is such a fundamental feature after all.]]>
As an owner of a steam controller, I know how annoying it is when games use mouse movement as an indicator that the player is using keyboard and mouse, so I'm not gonna do that.
Very interesting, I've never considered this aspect. So it's not possible to differentiate between someone moving their cursor with a mouse vs steam controller?
Using keyboard input as a predictor that the player is playing with their mouse is thus not possible.
A bit confused by this. Even if the player uses the mouse to attack, they are still using the keyboard for movement. Could attack action be bound to both mouse and keyboard at the same time? That way, both styles are combined in one.
Problem is, if my code has any sort of special logic that depends on the input device, as I described above, I need to undo that, and... check each input device manually?
Ha! Before you showed your API, I've pictured something like:

Code: Select all

if Input.Pressed(InputAction.Attack) is action {
  match action { 
    ActionType::Mouse => {},
    ActionType::Controller => {}
  }
}
I'm very mad no other engine has it. :akko_nope:
Most likely another classic instance of "we do whatever is most simple for a beginner, any slightly more complicated use case is the user's problem".
But, surely every half competent engine has a way to tell whether a mouse or a controller is used, and process each one separately? This is such a fundamental feature after all.]]>
<![CDATA[Game Programming :: Re: Tip: Your input system should not be an opaque abstraction :: Reply by celes]]> 2026-03-16T09:53:58+00:00 2026-03-16T09:53:58+00:00 http://community.carrot-games.com/viewtopic.php?f=10&t=32&p=85#p85
palas wrote: Mon Mar 16, 2026 3:04 am Very interesting, I've never considered this aspect. So it's not possible to differentiate between someone moving their cursor with a mouse vs steam controller?
Well, for your average 3d third person games, camera controls with mouse are a lot more precise than joystick, so what many people do is set up the steam controller (or the steam deck) so that the right trackpad acts as a mouse, but the rest of the device acts as a controller. To the game, this looks as if the player is mostly playing with a controller but using the mouse to aim.

If the game supports this well it leads to a super pleasing experience, but in many games they implement very poor input device heuristics and for example using the mouse for aim prevents using the left joystick for movement until the mouse stops moving for a few seconds. You should never assume the players are gonna be playing the game exactly as you are!
palas wrote: Mon Mar 16, 2026 3:04 am A bit confused by this. Even if the player uses the mouse to attack, they are still using the keyboard for movement. Could attack action be bound to both mouse and keyboard at the same time? That way, both styles are combined in one.
Well the thing is, I have keyboard and keyboard&mouse as two separate input schemes, and I want to implement a feature (attack in the direction the mouse is pointing at) that should only work in one of the two cases. If you are using keyboard-only and the mouse happens to be left of your character, your attacks will always go to the left which would be bad :blobcatgiggle:
palas wrote: Mon Mar 16, 2026 3:04 am Ha! Before you showed your API, I've pictured something like:
Hehe, good one! Pretty much the same thing I came up with. Actually, I'm using that DUnion source generator you shared a while back for this, and the `.IsMouse()` in my code was auto-generated from the union, but I could've used at `.Match(...)` as well!

I've found the source generator really useful even if I've only used it in a few places. Input was one of them because it's very useful to describe input bindings. I still miss proper discriminated unions in C# but at least I get something that's really close and not half bad.
palas wrote: Mon Mar 16, 2026 3:04 am Most likely another classic instance of "we do whatever is most simple for a beginner, any slightly more complicated use case is the user's problem".
But, surely every half competent engine has a way to tell whether a mouse or a controller is used, and process each one separately? This is such a fundamental feature after all.
When I had this issue in Godot, I had to fall back to the non "input action" API, so checking the keyboard, mouse and gamepad devices directly without going through the abstraction layer. I'm not sure if it's any better now but all their "is_action_*" methods return a boolean, and judging by GDScript's limitations, it's unlikely they return anything else anytime soon ^^'']]>
palas wrote: Mon Mar 16, 2026 3:04 am Very interesting, I've never considered this aspect. So it's not possible to differentiate between someone moving their cursor with a mouse vs steam controller?
Well, for your average 3d third person games, camera controls with mouse are a lot more precise than joystick, so what many people do is set up the steam controller (or the steam deck) so that the right trackpad acts as a mouse, but the rest of the device acts as a controller. To the game, this looks as if the player is mostly playing with a controller but using the mouse to aim.

If the game supports this well it leads to a super pleasing experience, but in many games they implement very poor input device heuristics and for example using the mouse for aim prevents using the left joystick for movement until the mouse stops moving for a few seconds. You should never assume the players are gonna be playing the game exactly as you are!
palas wrote: Mon Mar 16, 2026 3:04 am A bit confused by this. Even if the player uses the mouse to attack, they are still using the keyboard for movement. Could attack action be bound to both mouse and keyboard at the same time? That way, both styles are combined in one.
Well the thing is, I have keyboard and keyboard&mouse as two separate input schemes, and I want to implement a feature (attack in the direction the mouse is pointing at) that should only work in one of the two cases. If you are using keyboard-only and the mouse happens to be left of your character, your attacks will always go to the left which would be bad :blobcatgiggle:
palas wrote: Mon Mar 16, 2026 3:04 am Ha! Before you showed your API, I've pictured something like:
Hehe, good one! Pretty much the same thing I came up with. Actually, I'm using that DUnion source generator you shared a while back for this, and the `.IsMouse()` in my code was auto-generated from the union, but I could've used at `.Match(...)` as well!

I've found the source generator really useful even if I've only used it in a few places. Input was one of them because it's very useful to describe input bindings. I still miss proper discriminated unions in C# but at least I get something that's really close and not half bad.
palas wrote: Mon Mar 16, 2026 3:04 am Most likely another classic instance of "we do whatever is most simple for a beginner, any slightly more complicated use case is the user's problem".
But, surely every half competent engine has a way to tell whether a mouse or a controller is used, and process each one separately? This is such a fundamental feature after all.
When I had this issue in Godot, I had to fall back to the non "input action" API, so checking the keyboard, mouse and gamepad devices directly without going through the abstraction layer. I'm not sure if it's any better now but all their "is_action_*" methods return a boolean, and judging by GDScript's limitations, it's unlikely they return anything else anytime soon ^^'']]>
<![CDATA[Langdev :: On Rusts expression-geddon :: Author palas]]> 2026-03-09T06:23:19+00:00 2026-03-11T00:57:54+00:00 http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=51#p51 Useful terminology
(this one is for yoshi-enthusiast):
  • ! (pronounced as "never") is a special type, which can never be instantiated. It has an important property of being auto-convertable to any other type. This is useful for language primitives that affect control flow. For example:

    Code: Select all

    //this function never exits
    fn panic() -> ! {
      ...
    }
    

    Code: Select all

    let opt = Some(10);
    let value: i32 = match opt {
      Some(v) => v,
      None => panic!("it crashed"); // ! is converted to i32, as in this branch we never return anyway
    };
    


I've been wondering for a while what's the reasoning behind rust's parser aggressive "make everything an expression" attitude. There are only f̶o̶u̶r̶ three types of statements: item, macro, let binding and expression with ; at the end.
So why make return, break, etc. expressions? They all evaluate to ! anyway. and furthermore it results in weirdness like

Code: Select all

let x = foo(return 5);
being valid rust.

Today it suddenly hit me. (UPD: and as it happens, right after the "discovery" I've stumbled on a HN discussion where the exact same thing is stated multiple times).
I'm sure there are more, but one of the benefits is improved ergonomics of match statements. Code like

Code: Select all

let x = match value {
  Some(v) => v,
  None => return None, 
};
wouldn't be able to be parsed if return wasn't an expression. You'd have to write

Code: Select all

=> { return None; }
Though sometimes you still have to wrap the expression in {}, like when it's a function call which returns something, while match evaluates to ().
I was doing to ponder on how this could be avoided, but it really isn't a problem in the first place. Having a block clearly indicates the intent to ignore the output of a function, which would otherwise be either lost completely (potentially bad), or needed to be stated in a comment (gross).

Bonus: enjoy this beautiful piece (and a valid rust program), proudly residing at weird_exprs.rs, among other specimen.

Code: Select all

fn zombiejesus() {  
    loop {  
        while (return) {  
            if (return) {  
                match (return) {  
                    1 => {  
                        if (return) {  
                            return  
                        } else {  
                            return  
                        }  
                    }                    
                    _ => { return }  
                };  
            } else if (return) {  
                return;  
            }  
        }        
        if (return) { break; }  
    }
}
There's also a quirk with return I wanna discuss. You might think that's it's just an expression with a type ! and additional effect of terminating the function it's contained in. But that would not be correct! It (along with anything else which returns ! ) has an additional ability to change the types of the outer expressions, however nested.

Code: Select all

let x = {
  { return; };
  5
};
The type of x is in fact !.]]>
Useful terminology
(this one is for yoshi-enthusiast):
  • ! (pronounced as "never") is a special type, which can never be instantiated. It has an important property of being auto-convertable to any other type. This is useful for language primitives that affect control flow. For example:

    Code: Select all

    //this function never exits
    fn panic() -> ! {
      ...
    }
    

    Code: Select all

    let opt = Some(10);
    let value: i32 = match opt {
      Some(v) => v,
      None => panic!("it crashed"); // ! is converted to i32, as in this branch we never return anyway
    };
    


I've been wondering for a while what's the reasoning behind rust's parser aggressive "make everything an expression" attitude. There are only f̶o̶u̶r̶ three types of statements: item, macro, let binding and expression with ; at the end.
So why make return, break, etc. expressions? They all evaluate to ! anyway. and furthermore it results in weirdness like

Code: Select all

let x = foo(return 5);
being valid rust.

Today it suddenly hit me. (UPD: and as it happens, right after the "discovery" I've stumbled on a HN discussion where the exact same thing is stated multiple times).
I'm sure there are more, but one of the benefits is improved ergonomics of match statements. Code like

Code: Select all

let x = match value {
  Some(v) => v,
  None => return None, 
};
wouldn't be able to be parsed if return wasn't an expression. You'd have to write

Code: Select all

=> { return None; }
Though sometimes you still have to wrap the expression in {}, like when it's a function call which returns something, while match evaluates to ().
I was doing to ponder on how this could be avoided, but it really isn't a problem in the first place. Having a block clearly indicates the intent to ignore the output of a function, which would otherwise be either lost completely (potentially bad), or needed to be stated in a comment (gross).

Bonus: enjoy this beautiful piece (and a valid rust program), proudly residing at weird_exprs.rs, among other specimen.

Code: Select all

fn zombiejesus() {  
    loop {  
        while (return) {  
            if (return) {  
                match (return) {  
                    1 => {  
                        if (return) {  
                            return  
                        } else {  
                            return  
                        }  
                    }                    
                    _ => { return }  
                };  
            } else if (return) {  
                return;  
            }  
        }        
        if (return) { break; }  
    }
}
There's also a quirk with return I wanna discuss. You might think that's it's just an expression with a type ! and additional effect of terminating the function it's contained in. But that would not be correct! It (along with anything else which returns ! ) has an additional ability to change the types of the outer expressions, however nested.

Code: Select all

let x = {
  { return; };
  5
};
The type of x is in fact !.]]>
<![CDATA[Langdev :: Re: On Rusts expression-geddon :: Reply by celes]]> 2026-03-09T17:19:11+00:00 2026-03-09T17:19:11+00:00 http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=54#p54
That last example at the end reminds me of when I had to implement this in my language. I think what you're observing here is related to the never type and how it propagates across the control flow graph of the program!

A block can either terminate (evaluate to !) or not, and whether a block terminates depends on whether the block contains a statement that terminates. This property is defined recursively so it works for nested blocks.

For control flow statements like "if" and "for", special rules also apply, like, an "if" expression terminates (evaluates to !) if all of its branches terminate. But if one of the branches doesn't, then it evaluates to the return type of the branch. When type checking an if expression, you wanna make sure all branches produce the same type, but a naive comparison would say "int ≠ never, so the if statement does not match", which is incorrect!]]>

That last example at the end reminds me of when I had to implement this in my language. I think what you're observing here is related to the never type and how it propagates across the control flow graph of the program!

A block can either terminate (evaluate to !) or not, and whether a block terminates depends on whether the block contains a statement that terminates. This property is defined recursively so it works for nested blocks.

For control flow statements like "if" and "for", special rules also apply, like, an "if" expression terminates (evaluates to !) if all of its branches terminate. But if one of the branches doesn't, then it evaluates to the return type of the branch. When type checking an if expression, you wanna make sure all branches produce the same type, but a naive comparison would say "int ≠ never, so the if statement does not match", which is incorrect!]]>
<![CDATA[Langdev :: Re: On named arguments :: Reply by palas]]> 2026-03-09T06:37:33+00:00 2026-03-09T06:37:33+00:00 http://community.carrot-games.com/viewtopic.php?f=21&t=18&p=52#p52
but when a function takes a bunch of booleans, I find it almost always pays off in terms of readability to specify the names those parameters.
Functions accepting booleans??? Don't you know it's forbidden by Clean Codeâ„¢?! :blobcatgiggle:

Anyway, named arguments are fantastic and Im beginning to miss them in Rust as much as I miss sum types in C#. The use case of combined named + unnamed args clearly shows how important it is to support using both in the same function.

I don't have much code examples as of now, but I guarantee you that I'll abuse the crap out of named arguments as soon as I'll be able to :blobcatnodfast:]]>
but when a function takes a bunch of booleans, I find it almost always pays off in terms of readability to specify the names those parameters.
Functions accepting booleans??? Don't you know it's forbidden by Clean Codeâ„¢?! :blobcatgiggle:

Anyway, named arguments are fantastic and Im beginning to miss them in Rust as much as I miss sum types in C#. The use case of combined named + unnamed args clearly shows how important it is to support using both in the same function.

I don't have much code examples as of now, but I guarantee you that I'll abuse the crap out of named arguments as soon as I'll be able to :blobcatnodfast:]]>
<![CDATA[Langdev :: Re: On named arguments :: Reply by celes]]> 2026-03-09T17:06:49+00:00 2026-03-09T17:06:49+00:00 http://community.carrot-games.com/viewtopic.php?f=21&t=18&p=53#p53
Functions accepting booleans??? Don't you know it's forbidden by Clean Codeâ„¢?! :blobcatgiggle:
Ah, the teachings of Mr Uncle! :blobcatthink:

I know you don't need convincing, but you've made me think of how a lot of very experienced developers try to go on an spread their knowledge and most of them fail terribly because they fail to realize what is it that makes them good at programming. One of the universal truths in programming is that no amount of foresight can replae experience, so whatever their teachings are, often end up being misinterpreted.

I'm sure whatever sillyness the readers of Clean Code can conceive wasn't in the mind of the author. Writing is hard, hehe.

But, point taken! I'll make sure to declare a new heap-allocated class `Decision` with two inherited subclasses `DecisionYes` and `DecisionNo` with a method `makeDecision()` instead of using booleans next time! :blobfoxthinksmart:]]>
Functions accepting booleans??? Don't you know it's forbidden by Clean Codeâ„¢?! :blobcatgiggle:
Ah, the teachings of Mr Uncle! :blobcatthink:

I know you don't need convincing, but you've made me think of how a lot of very experienced developers try to go on an spread their knowledge and most of them fail terribly because they fail to realize what is it that makes them good at programming. One of the universal truths in programming is that no amount of foresight can replae experience, so whatever their teachings are, often end up being misinterpreted.

I'm sure whatever sillyness the readers of Clean Code can conceive wasn't in the mind of the author. Writing is hard, hehe.

But, point taken! I'll make sure to declare a new heap-allocated class `Decision` with two inherited subclasses `DecisionYes` and `DecisionNo` with a method `makeDecision()` instead of using booleans next time! :blobfoxthinksmart:]]>
<![CDATA[Langdev :: Re: On named arguments :: Reply by Sugui]]> 2026-03-10T18:45:07+00:00 2026-03-10T18:45:07+00:00 http://community.carrot-games.com/viewtopic.php?f=21&t=18&p=57#p57
But, point taken! I'll make sure to declare a new heap-allocated class `Decision` with two inherited subclasses `DecisionYes` and `DecisionNo` with a method `makeDecision()` instead of using booleans next time! :blobfoxthinksmart:
You are missing `DecisionUnsure`, not all decisions are easy to take. :pensivecat:

And where is the `DecisionFactory` class here? were you even thinking in creating a `Decision` calling its constructor directly? Decisions are complex and should be created by a factory class that instanciates them accordingly. :dukeparty:]]>
But, point taken! I'll make sure to declare a new heap-allocated class `Decision` with two inherited subclasses `DecisionYes` and `DecisionNo` with a method `makeDecision()` instead of using booleans next time! :blobfoxthinksmart:
You are missing `DecisionUnsure`, not all decisions are easy to take. :pensivecat:

And where is the `DecisionFactory` class here? were you even thinking in creating a `Decision` calling its constructor directly? Decisions are complex and should be created by a factory class that instanciates them accordingly. :dukeparty:]]>
<![CDATA[Langdev :: Re: On named arguments :: Reply by Kampffrosch]]> 2026-03-12T09:56:49+00:00 2026-03-12T09:56:49+00:00 http://community.carrot-games.com/viewtopic.php?f=21&t=18&p=64#p64
I am always arguing for trying to make change easy instead of trying to make it unnecessary,
but I can't say I have had much success in convincing people where it mattered :pensivepumpkin:]]>

I am always arguing for trying to make change easy instead of trying to make it unnecessary,
but I can't say I have had much success in convincing people where it mattered :pensivepumpkin:]]>
<![CDATA[Langdev :: Re: On named arguments :: Reply by celes]]> 2026-03-12T13:23:05+00:00 2026-03-12T13:23:05+00:00 http://community.carrot-games.com/viewtopic.php?f=21&t=18&p=66#p66

When designing something you'll always get it wrong the first time, and having to deal with refactoring the bad abstraction is harder than if we started with no abstraction at all and a lot of usage code to learn from.

I always keep going back to this post by Casey Muratori, I know I repeat myself, but it's been such a great way to approach programming. Maybe I'm idealizing it a bit but I think it's a philosophy that avoids these pitfalls by default.]]>


When designing something you'll always get it wrong the first time, and having to deal with refactoring the bad abstraction is harder than if we started with no abstraction at all and a lot of usage code to learn from.

I always keep going back to this post by Casey Muratori, I know I repeat myself, but it's been such a great way to approach programming. Maybe I'm idealizing it a bit but I think it's a philosophy that avoids these pitfalls by default.]]>
<![CDATA[Music Creation :: Re: Tackling "The Decisive Battle" :: Reply by celes]]> 2026-03-12T09:33:49+00:00 2026-03-12T09:33:49+00:00 http://community.carrot-games.com/viewtopic.php?f=20&t=20&p=62#p62

I can play the right hand at an acceptable speed, about 170bpm tho the target is 210, which I can somewhat keep up with on my most inspired days but definitely not there yet.

A funny thing I realized is that, for a song I've known since childhood, there are some parts I was clearly misremembering. Subtle parts, a note here or there being slightly different as to what I remembered. At first I thought the score I found was wrong, but stopping and listening to the original I realized how I had just been playing it in my head wrong all this time, *even* when the song was playing in my headphones. Kinda crazy to think about!

I feel the right hand in this song was the most difficult one I have tackled so far, so it's been a great exercise to gain some dexterity. Coming up with the fingering was also a very fun exercise, and I think I made a good job of making it rememberable and comfy. ^^ Usually I can learn the right hand for a song with one or two days of practice, so it goes to show how much this was above my skill level.

Anyway it is now time to do this with two hands. I have started that too. The arrangement I shared is quite simple-looking, but there are some subtleties that I didn't notice at first glance that make it much more difficult than I originally anticipated :akko_nope:

The whole song uses the famous 3-3-3-3-2-2 rhythm, and for the majority of the song the left hand is just doing that: daan daan, daan daan, da-da... over and over! It's a very annoying rhythm because you're constantly switching between going with the beat or the off-beat, so it's the kind of thing that doesn't come out to me intuitively and I just have to grind it note by note, looking at the score and seeing how the left and right hands fit together.

I know from previous songs that this is temporary, though. Once you've played a couple songs that use a certain left hand pattern, like say an octave arpeggio, doing two different things with each hand becomes a lot more intuitive and less academic exercise :nkoThink:

Or I guess we'll see! Because for now, it's back to the grind mines. I'm now at bar 11 and this is by far the toughest part of the song. Once this is over it'll probably be downhill from there... I hope
image.png

Attachments


image.png (8.88 KiB)

]]>


I can play the right hand at an acceptable speed, about 170bpm tho the target is 210, which I can somewhat keep up with on my most inspired days but definitely not there yet.

A funny thing I realized is that, for a song I've known since childhood, there are some parts I was clearly misremembering. Subtle parts, a note here or there being slightly different as to what I remembered. At first I thought the score I found was wrong, but stopping and listening to the original I realized how I had just been playing it in my head wrong all this time, *even* when the song was playing in my headphones. Kinda crazy to think about!

I feel the right hand in this song was the most difficult one I have tackled so far, so it's been a great exercise to gain some dexterity. Coming up with the fingering was also a very fun exercise, and I think I made a good job of making it rememberable and comfy. ^^ Usually I can learn the right hand for a song with one or two days of practice, so it goes to show how much this was above my skill level.

Anyway it is now time to do this with two hands. I have started that too. The arrangement I shared is quite simple-looking, but there are some subtleties that I didn't notice at first glance that make it much more difficult than I originally anticipated :akko_nope:

The whole song uses the famous 3-3-3-3-2-2 rhythm, and for the majority of the song the left hand is just doing that: daan daan, daan daan, da-da... over and over! It's a very annoying rhythm because you're constantly switching between going with the beat or the off-beat, so it's the kind of thing that doesn't come out to me intuitively and I just have to grind it note by note, looking at the score and seeing how the left and right hands fit together.

I know from previous songs that this is temporary, though. Once you've played a couple songs that use a certain left hand pattern, like say an octave arpeggio, doing two different things with each hand becomes a lot more intuitive and less academic exercise :nkoThink:

Or I guess we'll see! Because for now, it's back to the grind mines. I'm now at bar 11 and this is by far the toughest part of the song. Once this is over it'll probably be downhill from there... I hope
image.png

Attachments


image.png (8.88 KiB)

]]>
<![CDATA[Music Creation :: Re: Tackling "The Decisive Battle" :: Reply by celes]]> 2026-03-12T10:06:44+00:00 2026-03-12T10:06:44+00:00 http://community.carrot-games.com/viewtopic.php?f=20&t=20&p=65#p65
If the score is well-written, the left and right hand pentagrams will be lined up so that notes coincide. I guess this is not mandatory, but it's good taste and it saves you from doing a lot of mental math when you have weirder rhythms.
image.png
And this is even more helpful when notes *don't* line up:
image.png

Attachments


image.png (10.98 KiB)


image.png (4.46 KiB)

]]>

If the score is well-written, the left and right hand pentagrams will be lined up so that notes coincide. I guess this is not mandatory, but it's good taste and it saves you from doing a lot of mental math when you have weirder rhythms.
image.png
And this is even more helpful when notes *don't* line up:
image.png

Attachments


image.png (10.98 KiB)


image.png (4.46 KiB)

]]>
<![CDATA[Music Creation :: Renoise: A tracker-inspired DAW :: Author celes]]> 2026-03-28T11:02:37+00:00 2026-03-28T11:02:37+00:00 http://community.carrot-games.com/viewtopic.php?f=20&t=34&p=89#p89
image.png
They have a linux version, and while I had a bit of a "linux audio" moment, it seems installing pipewire-jack and launching it with "pw-jack ./renoise" did the trick for me (kinda...). I wished they took the time to do this properly and use pipewire or pulseaudio at the very least, but having tried to look at the documentation for the newer subsystems, I don't blame them too much for going vintage on this. :akko_nope:

Anyway, you can find it at https://www.renoise.com :akko_fistup: It's paid software, but really affordable, and they have a demo with almost no limitations you can use as much as you like

Attachments


image.png (168.66 KiB)

]]>
image.png
They have a linux version, and while I had a bit of a "linux audio" moment, it seems installing pipewire-jack and launching it with "pw-jack ./renoise" did the trick for me (kinda...). I wished they took the time to do this properly and use pipewire or pulseaudio at the very least, but having tried to look at the documentation for the newer subsystems, I don't blame them too much for going vintage on this. :akko_nope:

Anyway, you can find it at https://www.renoise.com :akko_fistup: It's paid software, but really affordable, and they have a demo with almost no limitations you can use as much as you like

Attachments


image.png (168.66 KiB)

]]>