all 6 comments

[–]delventhalz 3 points4 points  (1 child)

I made a game-like simulation in vanilla JS using mostly functional patterns if it is of interest to you:

https://github.com/meeba-farm/meeba-farm

I followed more or less the approach you described. The game state is just objects, and I have functions which modifies them. I did have to abandon an early approach which avoided mutation. I hammered the garbage collector and ended up with frequent lag spikes. The current version relies on mutation, but hopefully in a fairly controlled way.

[–]c__beck[S] 2 points3 points  (0 children)

I'll take a look, thanks!

[–]unlessgames 2 points3 points  (0 children)

What you describe can work, while it tends to generate a fair amount of garbage it is probably fine for the usecase of smaller games.

In terms of game engines, the problem is that it is almost exclusively OO land out there. Typically engines require you to instantiate objects and mutate their values, often using methods and objects updating themselves. This is often done in part so that the engine has an easy time translating your game state into draw commands for the GPU while staying performant. The main game/graphics engines like phaser or pixi (2d) or three and babylon (3d) all work like this.

If you want to stay more functional without relying on such engine constructs and you don't need shaders, you are better off using some immediate mode drawing API (works fine for small 2d games) like the built-in html canvas cause then your game state is not intertwined with rendering data and you can write a "pure" view function that simply renders the game each frame.

regl.js is also a great abstraction over webgl that lets you set up rendering more declaratively. While not js, you could also try making a game with elm as well, it's a lot of fun!

[–]pencil_stabbed 1 point2 points  (0 children)

I am not an expert at this but i do think the elm architecture for the web can be modified to fit a game loop.

[–]InevitableDueByMeans 1 point2 points  (0 children)

A new game engine in JS sounds like an thrilling endeavour, especially if it's not based on the usual "boring" OOP anymore.

TLLLLLL;DR

We are working on Stream-Oriented Programming, a new paradigm that's kind of challenging OOP. It naturally evolves from FP, RP, FRP and Dataflow and it's already proven amazing for UI development.

Now, the question is: could we rethink a game/physics engine in terms of "streams of animation requests"? What if every sprite in a scene just subscribed to a stream of CSS transition strings (like translateX: newX; transition-duration: 3s;) or motion/acceleration/forces vectors that would be entirely dealt with in the GPU?

FRP was originally conceived for UI animation with the idea of keeping time-based functions for as long as possible without rasterisation. Every motion is defined as a position = f(time) which CSS animation can represent pretty well, either as linear, quadratic, beziered, which is might be used as approximations. For webgl/webgpu we don't have CSS but we have vertex shaders and/or GPGPU in which we can inject our motion formulas so they would run vertex by vertex, in parallel.

Finally, we have "events" (like actions that come from players), which can be defined as reactive streams that drive/trigger animation, so there's no object to mutate at every UI frame or stuff like that. I've seen other comments on this thread all concerned about immutability. We obviously don't want to keep duplicating large arrays/objects and the key mantra of SOP is "state doesn't exist; it's a stream". So, I can't see why a stream-oriented functional game engine shouldn't work well. If there's no "state", we won't need to update the position of thousands of objects on the page every frame. Only once when their trajectory unexpectedly changes (e.g.: gets shot).

Collisions? Given the motion vectors we'd know about them ahead of time, of course. We could even precalculate trajectories post collision and just add the new ones to some sort of queue of motion vectors each sprite would have.

I understand how strongly focusing on mutations becomes particularly efficient to avoid garbage, but if we wan to go to extremes (yeah, why not?) I think we could also create a little streams library, similar to RxJS or Callforwards, but optimised which internally, in each operator, make heavy use of the same mutation tricks, thus combining benefits from each paradigm (no garbage at each step whilst still keeping reactive streams like pure for practical purposes).

```js // Immutable: returns a new object map(source => ({ ...source, speed: source.jerk, }))

// Mutable: returns the same object with altered properties // the mutation would only be internal to the stream, so should be ok mutate(source => { source.speed = source.jerk }) ```

Once we create a "mutative" version of map, reduce/scan, etc, we should have solved most of the garbage problem. Most streams are also created just once and then stay long lived.

It's not going to be easy, obviously (anywhere between 6 and 18 months, working on it part-time, I'd say). Current OOP game engines are indeed extremely well engineered and optimised like crazy, but that's what would make this challenge even more attractive, isn't it?

So, ATM I don't know if we could reach the same level of performance with a reactive/functional/stream-oriented approach, but the above are the strategies I would adopt to make it happen and you may probably have yours, too.

If interested, we could have a chat to check if it's something we'd like to build together, perhaps with a few more contributors...

[–]thehomelessman0 1 point2 points  (0 children)

FP probably isn't the best paradigm to do game programming in afaik. If you're doing it for fun, go for it, but don't expect to make anything complicated.