Nyx - A Bullet Hell Game in Haskell

Hi All, first post in this new discourse. Thanks for setting it up!

I really enjoy seeing the stuff people make with Haskell and wanted to kick-start the Show and Tell category by sharing something that I made a while ago - a bullet hell game called ‘Nyx’ which is written in purely functional style using Haskell and sdl2.

You can see a short gameplay video here:

The source code and binaries are available at my website.

I’d love to hear comments and answer questions about the game if you have any, and I hope to see some of the things you made as well!

15 Likes

This is awesome! Had you written any games in other languages before? Are there any advantages or disadvantages people should be aware of if they’re considering writing a game in Haskell?

Do you feel like we’re missing a higher level library over sdl2 that provides things like a game loop? I see you made your own here which looks pretty nice.

Also, how annoying was dealing with the statefulness of SDL? I’ve only ever used Gloss, which is simpler and stateless.

Thanks!

I have written games in other languages before. The biggest disadvantage with Haskell is probably the lack of tutorials and examples. Most tutorials about game development are directed towards object-oriented and event-based programming and are not very straightforward to translate to Haskell (and it’s probably not a very good idea to try and do that either). So you probably need to have some experience with games and purely functional programming to translate these ideas well.

Also, some people will definitely rather work with Unity or other well established engine or library, there are quite a few game logic things you need to build from scratch if you are using Haskell and SDL2.

Other than that, I felt that Haskell works fine and provides some interesting solutions for game development. I gave a short talk about this if you are interested. The slides are here.

Thanks!

I think there definitely could be something that provides some convenient things on top of SDL2, I have found a few things like that myself and tried to abstract them (Like the things in src/Play/Engine).

But I’m still not exactly sure how the interface will look like and if it’ll be reusable for many games. I think seeing more uses of SDL2 could help find the things that are generally useful and abstract them.

I didn’t feel like the statefulness of SDL2 was annoying. It was actually fairly easy to build an ‘imperative outer shell’ around it because it wasn’t event-based. I haven’t tried Gloss though so it’s hard to compare!

2 Likes

The presentation mentions a DSL for scripting, it looks really nice. I’m curious, have you considered a non-Haskell approach to scripting? We use hslua for scripting in pandoc: naturally, this comes at the expense of very little type-safety, but it works (surprisingly) well. It also frees users from the requirement of having Haskell installed to extend the program.

Right now, I extend hslua for what we need to use it with pandoc. I’d be very interested to learn about other people’s need, and how we could support them.

Thanks! I have not considered a non Haskell EDSL solution simply because it would be much more complicated for my use case. Using an EDSL I can define exactly the kind of things I want to happen (such as shake screen or spawn enemy) and interpret them (and only them) in my engine.

I wish you luck with hslua! I’m sure it can benefit some Haskell applications that need to be extended by a general programming language.

Do you have any chance try using gloss for writing game?

I really like the high-level API of gloss but wonder why lots of Haskell game dev go for more low-level (OpenGL, SDL).

I haven’t used gloss. I wasn’t familiar with the api and performance characteristics and didn’t want to take a chance that something might be missing or it not performing as well as I’d like. I had some experience with sdl2 and knew it was unlikely to fail there.

Because most abstractions just wrap OldGL and not “modern” shader-based gl with vertexArrayObjects, etc.
But you need that for performance above some simple hello-world things. You can abstract some things away, but OGL is a hell of a state-machine.
Graphics & Game-Development is full of hacks that only work in a very constrained orchestration…

I wrote a bit with it some years ago and also tried to abstract things, but this is not easy … :frowning:

Some frameworks go a long way to make it easier - but if you need advanced features (in that case 5 years ago it was tessallation) to really make use of modern hardware most frameworks either don’t cover it or you are better off writing everything low-level as you do not fragment your codebase with different libraries depending on the features used (or knowledge of the programmer).

This is divergent, but can someone explain me this piece of code (taken from game’s source)?

case r of 
   Texture (("assets/imgs/" ++) -> f) -> do

I don’t understand part after Texture, what is going on there? (“assets…” ++) is partially applied function, but what the arrow is doing there?! First time seeing this, uterly confused :slight_smile:

That’s the syntax for a view pattern. The idea is that the function to the left of the arrow is applied to the argument before it is matched against the pattern on the RHS of the arrow.

For example:

foo (span isAlpha -> (as, bs)) = as

Then when foo is applied to the list it will return the prefix of the list which satisfies isAlpha.

> foo ['a','b','0','a']
['a','b']
3 Likes

And the motivation for using view patterns is to avoid giving names to thing you do not want to use in the body of the function.

For example in this function i will always want to use "assets/imgs/" ++ f. If I made a mistake and used only f instead, that’d be a bug.

Another useful case is if you want the result of the function application to be part of the pattern matching, so you can skip to the next pattern if it fails.

2 Likes

I have created a full playthrough video of me playing (and losing, but eventually winning) Nyx for those who are curious about the game.

That’s an interesting aspect of view patterns, that I haven’t thought about - I mostly use them for conciseness, and didn’t realise they can help with safety. Thaks!

Thanks, Haskell has so much neat stuff! :slight_smile: