BulletML was developed by ABA Games (aka kenta cho, creator of the classic rrootage, which is now open source).
It was rrootage that sent me down a rabbit hole and eventually stumbled onto fal-works and firedancer after someone suggested their works had the same aesthetic appeal.
The libbulletml c++ library is still somewhat used in some niche vertical shmup games (mostly Japanese indie games). You can read a little about bulletml and the history of it all in the libbulletml readme file.
Yes. I was exposed to this when I came across kenta cho's games. I even tried to use it a little iirc for a shooter i was playing with but couldn't get very far and abandoned it.
As you may have guessed by the archive.org link, it doesn't have much support. Compared to BulletML and Firedancer, it has a very terse and rich syntax that kind of makes me think of Perl.
I wasn't sure exactly what you meant by "terse and rich syntax" but I wasn't expecting the example on the CML github which is referred to as quite cryptic:
[aside: I'm not usually one to pedant over what qualifies as a "markup language", but I have to say this is not it. I'm happy to report that the author didn't think so either, because the 'ML' stands for "macro language".]
Looking at the demo source code it seems like it is running everything in browser and is not prerendered. It just lacks a parser. So the AST nodes in all examples are created through function calls https://github.com/fal-works/firedancer/blob/4a15e80fe55893b... .
This is cool but I never understand most DSLs, looks like most of them can just be defined as some functions in your host language. The example on the main page just looks like JavaScript and you define the parallel(), radial() functions yourself to return some data, and a run() function to deal with these data / run the game. Writing a compiler and VM for this is a huge overkill imo, if it aims to be productive and not just a programming practice. Am I missing something?
A run() function to deal with the data is an interpreter (the data has stuff like calls to the wait function and subroutines in it) or at least a command-replayer, if the control flow all runs before any gameplay happens. The behavior might also be dynamic such that the command-replayer approach doesn't work.
One reason BulletML is useful compared to a general-purpose language is that you could arbitrarily recombine BulletML ASTs to produce valid bullet patterns, which Kenta Cho did for rRootage[0]. I think another reason that people implement VMs for these languages is that the host language lacks coroutines, so it cannot express the wait() function, and they don't want to turn their bullet pattern code inside-out. Even if the host language has coroutines, they may not be serializable, so saving your game in the middle of a level may not be possible if you build on top of coroutines, whereas it is definitely possible if you control the interpreter state of your DSL.
I find that a lot of DSLs are essentially "I need these N languages features in a serializable and dynamically runnable language." If you're already in a scripting language its not all that interesting but if you're in a compiled language, a small embedded DSL can scratch an itch.
Even in JavaScript, you might not want the full power of eval() exposed and instead you want something with less power that can be parsed and sanitized.
> Writing a compiler and VM for this is a huge overkill imo
Personally, that's what attracts the artist side of me to this, like it's the ultimate creative product. There seems to be a lot of blogging and tweeting recently around the subject has also created a bit of an itch.
I understand where you're coming from. I also value a lot on the artistic side of a project, but if you compare to a project like 100r's uxn, a lot of custom language / vm projects fall a bit short on both the creative and utility sides. But still, I'm all for creating things just for oneself or just for the pleasure of creating it.
fair
I'm curious how you feel uxn fails as a utility (eg. missed stated goals? or unstable and therefore not practical? ) and as a creative product tho it'll be hard to take comments on the latter as objective.
BulletML[0] (circa Java applet, AD) is a markup language to describe shmup bullet behaviours.
The Firedancer README says the project is "Inspired by BulletML".
Cool to see it living on, in a way.
But where is the toothpaste laser? What even is a toothpaste laser? I hear you cry, well...
Today in Search Engines: Software for No-one, I try (somewhat naively) to search for "toothpaste laser" in hope of finding an accurate analysis/explanation/implementation of the famous weapon/s from the Raiden series.
It won't surprise you to learn that this got me a grid of photos of toothpaste tubes and mechanically-induced fluorescent grins.
It might surprise you to learn that prepending 'raiden' to my query barely helped.
If only there was a search engine that allowed the user to decide what to search for.
Long story short, Raiden (series) is about as popular as the intersection of Raiden (Kharacter) and referring to electrical arcs as "lasers"[1].
Both of these topics are totally eclipsed by the Bend-Tech Dragon A400: The Essential CNC Tube Production Machine[2].
Per the description at https://www.youtube.com/watch?v=XVoZ-1le8s0 the weapon is pretty weak. It has to be to compensate for not needing to aim it at all, but it seems like even then it's not worth it. It's pretty iconic though. I'd like to see it in more games.
That's referring to the same "toothpaste laser", yes. The discussion never arrived at a satisfactory algorithm -- I tend to agree with the gist of the incremental "steering" ideas, though.
The way the scripts are presented in a text box makes it feel like I should be able to edit the text box contents and have the change represented in the image. It's a bit disappointing that I can't play around with it like that.
Laundry looks easy! Just move slightly to the left from bottom center to the safe zone. And speaking of safe zones, so many frameworks miss the opportunity to visualize them, calling for Ice Sign "Icicle Fail" in almost every geometric shmup.
I guess the idea is was that Firedancer is designed with fixed update rate in mind. And the demo makes an assumption that it will run at 60 FPS = 60 updates per second, probably becoming very fast and hard on 360 Hz screens.
But that's just nitpicking on details in library demo. It would be bad to have such mistake in a real game or game engine demo, but it's not that surprising for example code to skip details for the sake of simplicity. The main purpose of example is demonstrating the library as cleanly as possible without distracting reader by containing more boiler plate code for third party game engine than the thing it's attempting to demonstrate.
I don't see a reason why you couldn't update FireDancer VM at whatever fixed rate you want (faster or slower than typical FPS) just like many games and game engines with fixed tick rate for physics or game logic handle it.
If the shots weren't timed to a regular fixed clock, then there would be visual jitter in the pattern. Even if you correctly spawn shots in between two ticks, each bullet would only first appear some random distance along its trajectory.
If an enemy spawns e.g. a circle of bullets, you would see it rapidly expanding/contracting.
I suspect a variable frame rate bullet hell game would be harder to play, but I'm not a fan of the genre.
This is unfortunately a classic example of mixing data with execution. Data is easy to understand and manipulate. Execution is much harder because you don't know what it produces without executing it.
Inventing a language is the third step and even worse, because you not only have execution instead of data, but you wipe out the entire ecosystem of tools, libraries, fragments and knowledge and start all over again.
Defining the behavior of enemies and bullets in a shmup is programming. I'm not sure how you want it to be done without the programming part. Could you maybe show us?
Okay, let’s see your data-only implementation of “shoot n fast homing projectiles in a burst that takes a total of a third of a second where n is the number of times this attack has been performed in this phase at a location where the player was one third of a second ago.”
I guess you could accomplish this with invisible bullets and making bullets targetable, but at that point, you’re just creating state and loops and pretending you’re not.
I suppose now the engine evaluates all of the Locations that any pattern could ask for every frame (or more often?) so it can store a buffer of past values, all so we could avoid writing aim() and then wait().
Then we write a new class or add new members to our class every time we want to do anything even slightly different. For example we'll have two members for doing a fixed angular and temporal step between bullets, then another for controlling the number of volleys of a fixed number of simultaneous bullets with fixed angular and temporal steps between them, then a couple more for controlling linear increases and decreases in the volley count, then when implementing Border of Wave and Particle we may as well add quadratic terms to all of the above (although we will only need one of them). Later we'll realize that our linear step-up-and-down knob doesn't actually differentiate between these sorts of things:
e
.
. .
. . .
. .
.
e
.
. .
. . .
. .
.
e
.
. .
. . .
. .
.
so we'll turn it into two knobs or add some hack that lets us start adding angular increments after a delay in the middle of a single non-programming data-command.
To allow some enemies to aim some single bullets to the left or the right of the player, we'll just add an arithmetic expression evaluator to our location member too :)
> Is that what the custom language is doing? Why would that be necessary?
Sorry, I guess instead I should ask "How does your engine fire at locations that are evaluated in the past?" One way is to keep a queue.
Honestly this whole business of targeting locations in the past is not a dealbreaker to me. I don't know of games that need it. It's sufficient to implement this sort of delay by spawning some non-damaging thing under the player and having it spawn bullets later, or aiming at some time and then firing later.
> Even if you wanted to write new stuff, why not just use lua?
Sorry, I guess instead I should ask "How does your engine fire at locations that are evaluated in the past?" One way is to keep a queue.
Again, how does the language do it? If you query past values you can then know to keep track of past values.
Also there is no reason to use coroutines to make game bullets. You can just let a function keep track of state if it needs to and call it every frame.
At the core of all this is classic examples of over complicating things. As soon as someone declares they are making a brand new language with VM and coroutines to decide how bullets emit in a simple shooter, anyone else involved should be stepping in and putting a stop to it.
> Again, how does the language do it? If you query past values you can then know to keep track of past values.
I don't think that asking for something causes you to have recorded it in the past! In the various languages you would aim and then wait. Here it is in Firedancer:
This is a bit of a hack. We fire a bullet that immediately disappears to set our firing direction for the direction type="sequence" that we will use 18 frames later.
> Also there is no reason to use coroutines to make game bullets. You can just let a function keep track of state if it needs to and call it every frame.
Quite uniformly, developers of bullet hell games do not want to separate their code into basic blocks delimited by any part that might involve time passing in the game, and they don't want to give up on subroutines existing. Turning all of your code inside-out and giving up on subroutines existing obviously makes the work extremely tedious and makes it hard to iterate or to know what's going on in some file you haven't touched in a week. I don't necessarily advocate for making a new language and VM, but if you review the tools people actually use for this stuff, nobody does what you say everyone should be doing.
For a third example, here's a danmakufu tutorial in which we learn that we should make Marisa's laser not freeze the game by adding a yield to a loop: https://sparen.github.io/ph3tutorials/ph3u1l5.html
I have done this stuff in lua, but in any real game I would probably want the game state to be serializable.
I don't think that asking for something causes you to have recorded it in the past!
You don't think it's possible to read the file when the game loads, see a negative number, and save previous values?
in which we learn that we should make Marisa's laser not freeze the game
This seems to be a common theme with coroutines, the idea that it's solving a problem that can't be solved any other way (except for how it's been solved in the ten thousand other games over the last 40 years).
I have done this stuff in lua, but in any real game I would probably want the game state to be serializable.
> You don't think it's possible to read the file when the game loads, see a negative number, and save previous values?
That's why we were going to evaluate the locations that we could look into the past for on frames when we weren't looking for them. You decided it wasn't necessary to do so.
Can you link some of the ten thousand bullet hell games that are written in your preferred style? It seems like so far we have a lot of games written in one style that include certain kinds of complex behaviors and a lot of games written in another style that do not include these. So I am mostly comparing all of the output of ABA Games, ZUN, and almost every Touhou fan shmup on the one side to nothing on the other, rather than saying "wow, it's so impressive that people were able to make the amazing bullet patterns in Stephen's Sausage Roll with just switch statements!"
That's why we were going to evaluate the locations that we could look into the past for on frames when we weren't looking for them. You decided it wasn't necessary to do so.
...What?
Can you link some of the ten thousand bullet hell games that are written in your preferred style?
Games without coroutines? That's almost every game ever made.
You realize most of these types of games originated in the 80s and 90s and were made in assembly right?
> Games without coroutines? That's almost every game ever made.
Bullet hell games? None? Honestly it took several tries for me to come up with a game that lacks an internal language and VM (e.g. StarCraft and WoW have at least 1 and at least 2 respectively). Stephen's Sausage Roll seems like a potentially winning bet only because I don't think you can make all of it in PuzzleScript, which is what the author normally uses.
> You realize most of these types of games originated in the 80s and 90s and were made in assembly right?
There weren't any of these games prior to Batsugun in 1993. According to the people who have reverse engineered the games, the more well known DoDonPachi DaiOuJou in 2002 used the same level planner that Toaplan used for Batsugun. Assembly obviously has coroutines, but we know that these games did not use a coroutine implementation for their bullet patterns. I obviously don't work at CAVE and can't just ask the devs what they were thinking, but if you play these games on the original hardware, even though every bullet has no behaviors attached the game will start to run slow when there are a lot of bullets on screen (and the game seems to be tuned around this fact). So I expect that even if the developers wanted to make the sorts of things we have in modern games, they would run too slowly to be practical.
To be fair, modern games that use danmakufu or ZUN's ECL or BulletML or Firedancer or Lua would probably have less distinction between "enemies" and "bullets" and the "enemies" in these games often do have some behavior attached. In the case of DoDonPachi DaiOuJou, each enemy that spawns uses one of a small fixed table of movement patterns.
I'm not sure what the claim about "most of these types of games originating in a certain time period" really means. A majority of bullet hell games were probably made in the last decade, and that will probably continue to be true for a long time. If you mean that it's common for these games to contain references to games from the 90s, I agree, but that has nothing to do with whether modern games are written in assembly or how the behaviors are scripted.
Here's where you dismissively suggested that evaluating and storing locations that might be asked for in the future was unnecessary
What you actually said was "evaluates all of the Locations that any pattern could ask for every frame" so you are being very dishonest.
None?
You think no video game has ever been made without coroutines?
I'm not sure what the claim about "most of these types of games originating in a certain time period" really means.
What is there to process here? Games in the 80s and 90s were made in assembly language, then C. There were lots of shooters in those two decades, clearly they didn't need coroutines to make bullets and no other game did either. I don't even know how someone gets into a mindset that some shiny programming feature they love is the only way to do things when decades of progress before had nothing to do with it.
> What you actually said was "evaluates all of the Locations that any pattern could ask for every frame" so you are being very dishonest.
Sure, I think that's a reasonable implementation. Maybe we know all the Locations for all the behaviors in the current stage, and of those, for each one that can be evaluated in the past, we should evaluate it each frame and store it in a collection. The stored past-locations that are old enough that they can no longer be asked for can be discarded. So the data is accessed like a queue normally, but maybe if multiple behaviors look into the past by different amounts for the same Location then one of them has to access the middle of the queue. This is fine because the queue can be backed by a circular buffer. Is this a bad implementation in your view?
> You think no video game has ever been made without coroutines?
Is this an honest followup after someone asks exclusively about games in a single genre?
> What is there to process here?
Well, if the claim is that a majority of all bullet hell games were made before 2000, then it's just false, but it's sort of unclear if that is the claim. Maybe the claim is about "types" rather than "games." Even if it's about "games" then maybe Touhou Hisoutensoku (2009) "originated" in 1994 with the release of Street Fighter.
> Lua didn't even have coroutines until 2003.
Assembly had them in the 1960s (well, they are first documented to have been used in the 1960s, despite existing before then). C had setjmp and longjmp in 1979. Why would Lua need them?
To review the situation, we have 0 bullet hell games from the 80s. We have some bullet hell games made starting in 1993 by 2 companies (one of which is made up of the staff from the other) with exclusively bullets that never turn, home, accelerate, bounce off of things, or spawn other bullets. The games by these companies use a stage plan format the execution of which could reasonably be described as "not a VM". We have numerous other bullet hell games, the vast majority of all bullet hell games ever made, that use imperative scripting languages that can execute a single function that spans many in-game frames to attach behaviors to bullets. We also have a number of bullet hell games larger than the number of Toaplan/CAVE games but smaller than the number of BulletML/ECL/Danmakufu games for which I can't make any claims either way about the scripting environment (Valkyrie Sky, Jamestown, Shikigami no Shiro, etc.).
Obviously it's possible to implement anything in any Turing-complete language, in the same way that Billie Eilish could simply type the waveform she wants to put on her album into a text editor if she wanted to. I just think that it might be unreasonable for people who have sold 0 albums to tell her to do that instead of using FL Studio or whatever.
Yeah I think that usually "most of" means "a majority of" and "originated" means "was created."
> I don't know what this is supposed to mean, all I said was that lua didn't have them until 2003.
Well, I guess I apologize for assuming that that was supposed to be relevant.
> I can't even follow what you're saying, you're all over the place.
This submission is about a tool for creating a specific genre of art. Some works of art are created in an iterative process where the artist repeatedly evaluates and perturbs the work to make it more to their liking. Some ways of working make this easier than others. Almost every work in the genre has been created in a specific way. This submission's content is a part of the same tradition that includes the tools that have been used to create the vast majority of works in the genre. If someone who knows nothing about any of this insists that almost all of the artists are doing everything wrong, well, the base rate for that person being correct is not great.
> Are you really trying to say every video game ever made uses coroutines?
No. I gave specific examples of games in this genre that are known based on reverse-engineering to not use coroutines.
I will venture to avoid spending my time writing words for low-literacy audiences from now on.
Most of the patterns in the demo have loops. If you make a "repeat data object" with a "bullet firing action" in it, how would you differentiate that from a loop?
Awesome - I have designed similar things in my head dozens of times over the years. Often I end up drifting into thoughts away from these deterministic patterns and into a more adaptive approach based on how the user is performing - At least at the 'closer to the boss' parts of the levels ...
Just the same thanks for sharing and I'm enjoying playing around with this !
I find myself strangely drawn to Japanese bullet-hell shmups despite sucking at playing them. There's something about peak 2D pixel art sprites over parallax scrolling fantasy environments while dodging through insane geometric bullet patterns and spectacular animated explosions that's beautiful and hypnotically compelling.
I just wish I wasn't so hopeless at them, only managing to survive seconds at a time by constant credit feeding. While I realize the entire genre is notorious for being brutally difficult and that's in fact the appeal to many serious players, I dream of shmups where:
* Difficulty scaled dynamically up a smooth gradient based on real-time player performance
* Success didn't require deep level and pattern memorization. My cognitive style is anti N-back and this requisite shmup ability remains forever beyond my reach. I need the shmup equivalent of Fischer Random Chess (aka Chess960) where the necessity of book memorization is removed and the focus is mostly on reactively dodging in the moment.
* The engine supports interactive practice modes enabling weaker players to gradually build skills such as: rewind, ghost follow and partial dynamic slowdown (eg only bullets get slower, only when N of bullets > Threshold, growing gradually slower as N increases, returning to normal below N).
On the last point, some modern shmups do at least have "practice modes" but they seem like developer afterthoughts, typically very basic bolt-ons instead of being integrated, dynamic and granular. The result often leaves me feeling more frustrated than trained. While it is possible to apply hacks to emulated shmups which can help a bit, these are usually limited to brute force such as pause, global frame rate limiting and infinite lives or invincibility. While MAME and Retroarch do have rewind features, I believe they are hacky since I've never been able to get them to work reliably with shmups.
My deep attraction to the genre seems doomed to remain unrequited despite buying and playing so many, watching various training videos and following many guides. I feel like I can't reach the level needed to trigger the fun-obsession loop of a typical bullet-hell shmup because it currently requires a minimum level of a few neuro-competencies I don't possess. Even top players and fans admit (and occasionally revel in) the inaccessibility of the genre, highlighting the need to memorize deep pattern sequences, commit dozens of hours to rote practice and how it helps to be "at least a little on-the-spectrum."
This makes me sad because top players, fans and developers are also worried about the long-term decline of the genre from cool niche in the 90s to seriously at-risk in recent years. I suspect there may be others who are, like me, strongly attracted to shmups but too-often barred by a metaphorical "Must Be This 'Neuro-Level' to Ride" sign. Yet other once niche gaming genres have successfully broadened their appeal by deeply integrating 'accessibility' features like racing games have done with: ghost cars, rewind and suggested driving line/speed indicators, and fighting games with granular, smooth-scaling training modes with extensive on-screen instruction.
I recommend taking a look at "The Electric Underground"'s videos on how to get better at shmups. He has a lot of useful advice (where you should be looking while playing, conventions, how to see danger, etc).
Thanks, I have watched several of his videos previously and they are indeed full of good tips but somehow I'm still not able to get to the point where the shmup loop kicks in. For me, I think it's not mainly an issue of knowledge or technique, hence my post with ideas on how future shmups might be made more accessible to those like myself.
The classic book references for programming shmup bullet patterns (in Japanese) are シューティングゲームアルゴリズムマニアックス and シューティングゲーム プログラミング, but in general, a lot of this stuff is folk knowledge and isn't communicated well outside of trial and error until you get something passable. I would suggest looking up web tutorials and game source code and learning from that (also will mention the Development sub forum on shmups.system11.org being quite useful).
While these patterns are pretty to look at, I don't think bullet-hell shmups are fun to play. The challenge becomes to very carefully dodge bullets that appear in the same pattern over and over again, instead of, you know, actually shooting at the enemy. Yes, dodging is part of shmup gameplay, but I find it much more enjoyable when the bullets don't have a recognizable pattern, and when the goal is not to overwhelm you with the amount of them on the screen.
I love the shmup genre, but would rather play traditional ones like Raiden, DemonStar, R-Type, Gradius, Blazing Star, etc., and modern entries like Sky Force, than Ikaruga and the like. But, hey, to each their own. :)
If you’d ever played danmaku games at a high level, you’d realise that the patterns are not the same over and over. Most well-made games have many elements of positional advantage and economy of movement to shape the bullet patterns in your favour.
R-Type, on the other hand, is a pure memorisation game, much more so than Touhou or Donpachi, and definitely features the same patterns every single time.
Right. Memorization of patterns is definitely a staple of most shmups. I suppose I didn't express myself properly.
What I meant is that when there is a pattern in the objects you have to avoid, and the number of them becomes overwhelming, it doesn't make the gameplay more enjoyable (IMO). Those micro movements you mention surely require a lot of skill, but to me they're just not fun.
Take a game like The Binding of Isaac as another example, whose core mechanic is a shooter. Twin-stick shooters are an evolution of the shmup after all, where the player is in control of their attack direction. What makes TBoI so much fun is the unpredictability of enemy movement and randomness of objects you have to avoid. It does have areas where attacks are overwhelming, but player progression is usually well balanced (though it is possible to become overpowered as well :), so it's never just a dodging game.
But I concede that my experience with bullet-hell shooters is limited, due to this initial dislike of their core mechanic.
On a slight tangent, this is why I don't see the appeal of the recently popularized "survivors-like" games. Sure, there are swarms of enemies, but they come at predictable patterns, so the main mechanic is just strafing around them. Add to that that they remove your ability to aim or fire, and the gameplay boils down to just using directional keys in a specific order. It's so boring and unengaging. But then again, there are entire genres of clickers and idlers I don't get either, so this is purely a subjective take. :)
I think you’re actually not harsh /enough/ on survivors games. Most of them don’t even have any dodging in them. They’re idle games masquerading as action games. And they’ve polluted the ‘bullet hell’ tag on Steam to the point where it’s impossible to find real danmaku games.
BulletML is a markup language that describes the barrage of bullets in shooting games. - https://news.ycombinator.com/item?id=410898 - Dec 2008 (2 comments)
https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...