Hacker News new | past | comments | ask | show | jobs | submit login

Defining the behavior of enemies and bullets in a shmup is programming.

Says who? And for what definition of programming?

I'm not sure how you want it to be done without the programming part.

Maybe you should point out the parts that can't be static data.

Even branching, triggers or graphs could be straight data.

Which parts needs loops, state and computation?




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.


Time: 0.3, Burst: true, Count: $phase, Location: $playerXYZ, LocationTime: -0.3


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 :)


I suppose now the engine evaluates all of the Locations that any pattern could ask for every frame

Is that what the custom language is doing? Why would that be necessary?

Someone asked the question and I answered.

Even if you wanted to write new stuff, why not just use lua?


> 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?

I listed some reasons here: https://news.ycombinator.com/item?id=41257693


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:

  aim()
  wait(18)
and in BulletML:

    <action label="main">
    <fire>
      <direction type="aim">0</direction>
      <bullet>
        <speed>1</speed>
        <action>
          <vanish/>
        </action>
      </bullet>
    </fire>
    <wait>18</wait>
    <fire>
      <direction type="sequence">0</direction>
      <bullet>
        <speed>2</speed>
      </bullet>
    </fire>
  </action>
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.

http://lua-users.org/wiki/TableSerialization


> 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?


> ...What?

Here's where you dismissively suggested that evaluating and storing locations that might be asked for in the future was unnecessary: https://news.ycombinator.com/item?id=41266544

> 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.

Lua didn't even have coroutines until 2003.


> 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.


Well, if the claim is that a majority of all bullet hell games were made before 2000, then it's just false,

That's what you think I said?

C had setjmp and longjmp in 1979. Why would Lua need them?

I don't know what this is supposed to mean, all I said was that lua didn't have them until 2003.

I can't even follow what you're saying, you're all over the place.

Are you really trying to say every video game ever made uses coroutines?


> That's what you think I said?

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.


Yeah I think that usually "most of" means "a majority of" and "originated" means "was created."

What are you even talking about? All I said was that shooters in the 80s and 90s were done with asm. Why is every comment so far off the map?

I will venture to avoid spending my time writing words for low-literacy audiences from now on.

It's definitely not that you have huge ranting comments full of indecipherable tangents.

What was your point with all this again? I lost track of whatever you're trying to say a long time ago.


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?




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: