fable-compiler / fable-graphics

Fable bindings for graphic tools: D3, DC, Three, Pixi
https://fable-compiler.github.io/fable-graphics/
Apache License 2.0
23 stars 8 forks source link

Functional game engine #4

Open alfonsogarciacaro opened 7 years ago

alfonsogarciacaro commented 7 years ago

Hi @whitetigle and @MangelMaxime! Let's start a new issue as the previous one went off-topic long ago ;)

I've a bit of experience with game engines in the past (before I learnt functional programming) so here are my thoughts:

So we need to take the classic game loop and try to make it more functional:

let gameLoop(dt) =
    update(dt)
    render()
    window.requestAnimationFrame(gameLoop)

I have a couple of concerns at the moment:

MangelMaxime commented 7 years ago

Nice idea for starting this issue.

(the only optimisation is to skip rendering of elements that are completely hidden).

In fact, if I remember well this is possible for the user to cache others drawing element in another canvas for example and draw in the "visible" canvas using the other as a source. It's useful when dealing with tiles or tilesheets for example.

Optimisation in games usually have to do with the update method, as it often involves expensive physic calculations. We need to receive the time elapsed (the argument in the callback to animation frame), first to do calculations by time and not by time frame (e.g. so bunnies rotate at the same speed even if the frame rate drops) and second to skip calculations that are not needed at that point.

That's true and this is really easy to add the time elapsed. Current internal trigger is this function:

/// Function used to delay the Draw action (60fps based)
let schedule(msg) = scheduler.Post(PingIn((fun _ -> inbox.Post(msg))))

We should simply make the "Internal Actions" time based. For example (need to think if all need the time based or no)

type AppMessage<'TMessage> =
      | AddSubscriber of string*Subscriber<'TMessage, 'TMessage>
      | RemoveSubscriber of string
      | Message of 'TMessage * float
      | Tick of float
      | Draw of float

Events: we could take the events happening in every frame and put them in a buffer that is passed to the update function. Any ideas?

For the moment, the events are supported the same way as for virtual-dom. I mean each time we have an event occuring in the "view", it's push an Action to the App Actions buffer. https://github.com/fable-compiler/fable-graphics/blob/feature/fable-architecture/samples/pixi/fable-architecture/fable-architecture.fsx#L81

State...

True that having the state functional and immutable can be hard. But perhaps we could later the user use mutable model or mutable properties of the model. Moreover, I actually succeeded in using Aether with Fable. And this could help a lot to manipulate the data. (Still less performant than full mutable states) but still more elegant.

I think, we should make a compromise for a first time about performance and elegance of the architecture.

In fact, it's seems for me that we can enforce good architecture and good practice with a Framework Engine. Like using Aether, Immutable and pure functional approaches but at some point we should probably have an Advance topics to solve performance issue for example.

alfonsogarciacaro commented 7 years ago

Using lenses would be really cool! Do you have some sample code? Could you link to it?

MangelMaxime commented 7 years ago

I will try to push something this evening or tomorrow.

where should we put the lenses samples? Consider it a new helper? Or simply but putting in a repo on github.com like one of mine?

alfonsogarciacaro commented 7 years ago

I would create some cool examples with Pixi and lenses, see people feedback and then create an independent lenses helper if needed.

alfonsogarciacaro commented 7 years ago

Pinging @andrea who already has experience on game engines with F# and can give us very useful advice 😸

whitetigle commented 7 years ago

Just some extra food for thoughts:

So if we consider that we want to aim for 60FPS, it means that we may have some horsepower available. So it would be interesting to check how much duty/time each task consumes and see if we could take this into account.

That's it for now :)

alfonsogarciacaro commented 7 years ago

About web workers, I've seen samples of them being used with some physic JS libs. It's not straightforward because you've to sync the background calculations with the frame rendering and code for web workers have some limitations: they've to be in a different file and they need to reload all dependencies (including a module loader if needed). We could give them a try but maybe it's better to start without them and then moving some operations to the background as we run into performance issues.

whitetigle commented 7 years ago

Hi! I just found some interesting topic regarding UI architecture. . Some more food for thought.

[EDIT]: interesting article on game loop

whitetigle commented 7 years ago

So I am porting my current tools and projects to fable/pixi and of course while porting I'm wondering how I could make things way better using fable and fable architecture. So here are some thoughts.

1 - Behaviours.

I have played a few times this year with Construct2 to build some projects and they have what they call behaviours: basically a way to transform sprites following a few rules.

For instance if you take the car behaviour, you will have some friction when you move. A bullet behaviour will constantly incrase speed. We could have a simple particle behaviour: it would appear and then alpha would go down to 0. etc...

So I was wondering how it would be cool to have all these behaviours that we could just add to a sprite. Of course if we we using a physics engine it would just be a matter of chosing the right parameters for our bodies. In all cases to have a kind of library of Transform Behaviors would reduce dramatically the work and allow for fast debugging.

2 - UI layout management

Every project ends up the same: I need a basic positioning system to position elements without spending too much time. In Pixi there's no CSS so it's just a matter of changing x and y coordinates and add the sprites to the stage in the correct order (container, parent-child, etc..)

Since using Fable compilation + watch is fast. That's great. So I can imagine doing this by hand like I would with CSS. Now it would be great to have a flexbox like system to allow for fast positioning:

A - Position; place elements easily and move them on screen without any compilation to allow for very fast iterations. Allow for predefined positioning (centered. So outside config file would be needed.

B - Snapping: position elements relative to a grid

C - Align: stacked all up, all down, all centered, left, right, etc...

D - Spacing: take n elements and align them in the same space with the same distance between each.

Other similar ideas:

E - Groups: do transforms on a set of elements (=Container)

F - PinTo: transform element relative to another.

3 - Screens

I would call a screen, a context where I would put elements and that would hold them until I dispose it. It could also be called a View. For instance, in XCode we would create a storyboard to navigate from a view to another.

And as a matter of fact, there's some transition render happening between each screen. A fade, a simple hide or more complex transitions like sliding the whole screen and make the next one appear in a smooth move.

So basically in my projects I have screens. Each screen defines its rules and its own update. When I change screen, I launch the transition process and load the new screen and dispose the previous one.

In a screen I have a top container that will hold all my graphical elements (sub containers or sprites) and setup/init + update + dispose functions. I have found this approach very easy to use and understand so far. It has been used in many other frameworks. But it's clearly not modern. So maybe we could make it absolutely pretty and even more powerful using fable-architecture?

4. No DOM. 100% Pixi. Ramblings 😉

A while ago I was thinking about using Dom or VDom for ingame UI. After thinking about both dom and no dom approach, there's something that keeps coming back: the poor implementation of CSS everywhere and the hard souvenir of how much time I lost using flexbox a few months ago on safari iOS (flexbox just for fun).

In a perfect world, I would be very happy to design everything using CSS. In the real world, CSS has proven a constant pain in my back. Every year I say to myself: things get better. Every year I stumble on some tricky behavior. I curse CSS but remember that it works most of the time. 98% of the time. And that's the 2% that literally swallow hours.

So in a game environment, and for UI I think using DOM + CSS would prove a bad move after all. I think it would lead to a more than wanted complex architecture. After I wthink we should assume Pixi engine is stable on every platform and do the same thing everywhere. What do you think?

Ok that's pretty much it for today. Thanks for reading!

More links for fun:

MangelMaxime commented 7 years ago

Hello :)

Good job on all your research :).

1 - Behaviours.

For the point 1, this is something needed to simplify game development but behavior are really something coming from POO world. We should try to find something more functional I think. Because I played for some time with Class and F# and it's work great that can feel a little strange by moment.

Here is a code I was playing with to test some GUI library over Pixi.

Rectangle sample Button inherited rectangle

2 - UI layout management

I already tried to write some GUI library. I first tried Immediate Mode and Second Time Component mode which is the code I show on point 1.

I would definitely go for IMGUI because this could be more functionnal and also it's don't enforce the user to make the GUI retain the informations. He can use the storage he wants :)

The reference in term of IMGUI is I think this project: https://github.com/ocornut/imgui It's in C++ ok... But if you look at the samples it's kind of powerfull and the code is simple.

I am working a port of IMGUI for Fable. Still under development and I am trying to look at how we can use Pixi as Backend. Because Pixi support the Text rendering and it's the hard part :) (Everyone can draw a rectangle).

3 - Screens

Screens are good enough but old has you say. We can try to modernize it like Godot do.

In fact in Godot there some Scenes (aka screens here) and this Scenes contains some Nodes. And in fact, everything is a Node (sprites, collisions box, etc.) and even Scenes can be a Node.

For example, you can make a scene "player" which contain the sprite, spriteanim, collision box, the scripts, etc. And you can import it in others scenes. And so if you change the Player scripts it reflected everywhere. It's some how different from the traditional game engine but I don't really explain it well. I would advise you to do some research if you can.

4 - No DOM. 100% Pixi. Ramblings :wink:

I hate CSS and that why I am working on a GUI library. Really this language is a pain to play with and I think we could have better result by saying ok we only have a canvas. So our GUI part (draw, event, etc.) can only be used in this context ("pixel based").

I am not really giving you solutions except perhaps on point number 2 where my GUI will only say I am based on pixed otherwise I am agnostic to your implementation. So a good idea could be to share our time between the different aspects of the project ?

Finally, I think if we can abstract the pixi part which is somehow POO based. We could try to make something 100% functional. Because Pixi is already doing a great with performance for Rendering. There is one other place where performance are needed which is the Physics part and probably here POO or Prototype are better. But otherwise we could stay functional and have the benefit of using F# and not JavaScript :)

Ps: Hope to make some sense :)

whitetigle commented 7 years ago

Thanks @MangelMaxime for these anwsers! The information you shared has proven very interesting.

I like the IMGUI approach; It's seems indeed very convenient to use. Now for a game UI, I still need to make my mind around it. For instance for a life bar à la Street fighter.

I read the samples you provided (Button and Rectangle), thought about all the stuff you've shared ☕ and did a few experiments before coming back to this idea of behaviors which in the end would look like to me like function composition.

So I made a first try at it using an OO approach:

open System
open Fable.Core
open System.Collections.Generic
open Fable.Core.JsInterop
open Fable.Import.PIXI
open Fable.Import.PIXI.extras
open Fable.Import.Browser
open Fable.Import.JS

let options = [
  Antialias true
  BackgroundColor ( float 0x7A325D )
]
let renderer = WebGLRenderer( 800., 600., options)

let gameDiv = document.getElementById("game")
gameDiv.appendChild( renderer.view )

// create the root of the scene graph
let mutable stage = new Container()
stage.interactive <- true

type Easing = float -> float -> float -> float -> float

// So this is our Behavior contract
[<AbstractClass>]
type Behavior() =
  abstract Update : Sprite -> unit

// this is a sample of behavior: a simple fade
type Fade(easeFunction, duration) =
  inherit Behavior()

  let _easeFunction : Easing = easeFunction
  let _duration : float = duration
  let _start = DateTime.Now

  override self.Update(s:Sprite) =
    if s.alpha > 0. then
      let newT = DateTime.Now
      let elapsed = newT - _start
      let result = _easeFunction (float elapsed.TotalMilliseconds) 0. 1. _duration
      s.alpha <- 1. - result
      if s.alpha < 0. then
        s.alpha <- 0.

// another behavior
type Blink(freq) =
  inherit Behavior()
  let _freq : float = freq
  let mutable _start = DateTime.Now

  override self.Update(s:Sprite) =
    let newT = DateTime.Now
    let elapsed = newT - _start
    if elapsed.TotalMilliseconds > _freq then
      s.visible <- not s.visible
      _start <- DateTime.Now

// a last one for fun
type MoveBehavior(speed) =
  inherit Behavior()
  let _speed : Point = speed

  override self.Update(s:Sprite) =
    s.position <- Point( s.position.x + _speed.x, s.position.y + _speed.y )

// this is our "smart" sprite that uses behaviors
type ESprite(t:Texture) =
  inherit Sprite(t)

  let _behaviors = List<Behavior>()
  member self.Behave(b:Behavior) =
    _behaviors.Add(b)

  // the "magic" is happening here
  member self.Update() =
    _behaviors |> Seq.iter( fun b ->
      b.Update(self)
    )

let nodes = ResizeArray<ESprite>()

let rec animate (dt:float)=
  // now our behaviors are plugged, the animate loop remains easy to read
  nodes |> Seq.iter( fun n -> n.Update() )
  renderer.render(stage)
  window.requestAnimationFrame(FrameRequestCallback(animate)) |> ignore

// Now let's try!
let g = Graphics()
g.beginFill(float 0xFFFFFF)
g.drawCircle(0.,0.,10.)
g.endFill()
let r = U2.Case2 renderer
let t = g.generateTexture(r,Globals.SCALE_MODES.LINEAR,1.0)

let easeLinear (t:float) (b:float) (c:float) (d:float) : float =
  c * t / d + b

for i in 0..1000 do
  let dot = ESprite(t)
  dot.anchor.x <- 0.5
  dot.anchor.y <- 0.5
  dot.position <- Point(Math.random() * renderer.width,renderer.height * Math.random())
  stage.addChild(dot) |> ignore

  // add blink
  dot.Behave( Blink(500. * Math.random() + 200.) )

  // add move
  let dirX = if Math.random() <= 0.5 then 1. else -1.
  let dirY = if Math.random() <= 0.5 then 1. else -1.
  dot.Behave( MoveBehavior(Point(Math.random() * 0.5 *  dirX,Math.random() * 0.5 * dirY)) )

  // add fade
  dot.Behave( Fade(easeLinear, Math.random() * 5000. + 1000.) )

  nodes.Add(dot)

animate 0.

So there's and Eprite which is a Sprite with a list of behaviors. A behavior is a class with an Update(sprite) method. The sprite is the updated through the internals of a behaviour. No direct manipulations and no sprite?whatever mutable var. Everything is secured in a Behavior. So it's quite easy to create new behaviors and plug them.

I have not plugged this to fable architecture but it would work great since it's on another level. We now also have the possibility to add behaviors while playing.

Now I think this could be done in an even more functional way. What do you think?

whitetigle commented 7 years ago

@alfonsogarciacaro

Optimisation in games usually have to do with the update method, as it often involves expensive physic calculations

Found some interesting information: Time corrected Verlet integration

matter.js use of Verlet Integration

And a great post on reddit

MangelMaxime commented 7 years ago

Thanks for this sample. The code looks really nice.

Life bar case

For the life bar, you quoted yes it's can be a bit harder because the base library will not provide a "progess bar" with 3 layers. But you will be able to customize the color and have the right behavior.

Because it's should still be easier to prototype and use the UI than if you need to make Retained UI Mode (like the one from the browser which retained the state of your data).


General case

After a lot of research (it's been more than 1 year that I am prototyping UI and searching stuff on it^^). Both IMGUI and RUIM are good and the performance of both are similar like for every program it's depend on what you want etc.

I think RUIM are easier to develop because we can easily wrap Pixi like I did with the sample shared. And still get a good final user experience :). If you look at the Demo folder it's more or less clean :) (still need to be refined because I was just experimenting the internal structure not the usage of the UI).

IMGUI can be really good to work with etc. But I still have some hard time to wrote it with pixi because I need to understand how to use pixi to draw generic stuff like Rectangle, Button, Text, etc. And also manage the position etc.

whitetigle commented 7 years ago

I agree with you about RUIM although now you told me about IMGUI, my mind is running wild 😈 I need to think about it!

whitetigle commented 7 years ago

Ok here is my second take (still done in standard architecture).

I added some basic cleanup system on Behavior and ESprite with the possibiliy to call an OnComplete callback.

  1. when completed, Behaviors set themselves to Dispose. ESprite then take rid of them but tries to call the OnComplete before (unit ->unit) option.
  2. Same for Esprite: some behaviors can set them to Dispose then they just remove themselves.

Although the callback system works and is super easy to understand, I'm not very happy with it since it could lead to some nasty chain of callbacks

Are there interesting options to search or should I leave it this way?

Now using fable-architecture, I was thinking about:

  1. using callbacks for direct issues without side effects on our model (spaceship collides -> generate explosion -> play sounds -> quake effect, etc...)
  2. using post for anything that would change our model: basically generate new updated state

For a scene, I was thinking about having one playing scene in our model that would change when calling some ChangeScene( newScene, transition ) action. something like that.

For instance we would have an Intro scene, a main menu scene, a game scene, etc... each one holding it's own tree of DisplayObjects.

ChangeScene would launch the transition effect, dispose the old scene and call the next scene. So a scene would also host some basic scheme routing to tell what scene would come next.

What do you think?

[EDIT] forgot to mention that the current sample take 100 balls and launch the 50 last against the 50 first. Each one then disappear on collision. It was to test interactions using behaviors and see if they would fit well.

whitetigle commented 7 years ago

[Just for fun] 🎨 Same algorithm but a little bit more arty ;) wtfpainting

alfonsogarciacaro commented 7 years ago

Wow, isn't that cool! 👏

Sorry, guys. I don't have proper internet connection these days and I cannot follow the conversation very thoroughly, but if there's a specific thing you need may help with or if you need a specific feature in Fable compiler just shout my name :)

BTW, have a look at the Uniqueness types. They should be very useful for games, though the PR won't be probably merged until 0.7.0 (end of next week maybe).

whitetigle commented 7 years ago

BTW, have a look at the Uniqueness types.

Sure I'll try to understand what it does 😉

So we've covered a lot of subjects with @MangelMaxime and I think we'll eventually come to something interesting.

Meanwhile I begin to get myself comfortable with f# so things are becoming much more interesting for me. yet, I spend a lot of time on https://fsharpforfunandprofit.com which is clearly cool and am happy to read top topics on Fable gitter from top people 👍

So it rocks! 🎸

MangelMaxime commented 7 years ago

Really nice work @whitetigle

I wish to have more time next week to be able to work on some IMGUI code. But it's really impressive to see how the code is expressive from your sample :) Great work once again

whitetigle commented 7 years ago

Hi! Thanks for your feedback! Happy you like it!

I've added some new behaviors and am trying to see how far it could be usable so I'm doing some arty experiments (old idea that comes back to life).

I've also rewritten a simple loader for pictures, json remote and local, sounds, shaders, etc... that works but can certainly be optimized and will certainly be simplified with pixi v4. It's more a working draft and certainly needs some functional boost but allows me to work with old projects. Basically it loads some asset list from a json file and then loads each asset and stores them in the appropriate Dictionary. Callback is then called when everything's been loaded.

// start our loading
let assetLoader = Loader(onLoadComplete, errorCallback=errorCallback, progressCallback = progressCallback)
assetLoader.LoadFromJson("loader.json")

I have to think about and build yet the concept of scene/node and bring the whole back into fable architecture to see how the whole merge together. Then I will have the required basis to work on the next projects with Fable.

Regarding IMGUI, I will try to throw some thinking and let you know so we can share ideas.

Last but not least, regarding the samples that I could add to fable: I wonder what would be the most appropriate. The Fable dots sample is quite complete but I wonder what would fable users really want regarding pixi. So maybe we could ask the community about some useful samples? Like a how would I do that using pixi and fable-architecture? Or give ideas and let them vote on Github? Something interactive? Do you have any idea? Or maybe it's too soon and we need to wait for a fable-architecture + pixi v4.x?

You tell me 😄

MangelMaxime commented 7 years ago

I think having a real case sample is generally best whatever the project.

So we could probably a simple platformer with:

I think this should not be to complicated and could be a nice starter to new users :). If they want to compare the synthax etc. They already have the current samples.

whitetigle commented 7 years ago

So a platformer? Why not. Random monster generation based on count of last week remaining bug issues and coins based on commits? 😉

@alfonsogarciacaro what do you think?

MangelMaxime commented 7 years ago

Ahah :) How to motive developers ^^

alfonsogarciacaro commented 7 years ago

Hi there! Sorry, I didn't have time to read the comments in depth until now. First of all, I must say I'm really excited by the work of you both @whitetigle and @MangelMaxime and the research (the html-gl and matter-js libraries look really interesting). It would be incredible awesome to have a simple platform game, and @whitetigle's idea of linking it to Github can be extremely funny 😆 This would serve as a workbench to later extract a general-purpose game engine from it.

I find @whitetigle idea of attaching behaviors to the sprites very appealing. I've tried to adapt your sample here. The new code is neither perfect nor purely functional (it still relies on mutations for performance), but it does try to be more F# idiomatic. For example, there's the obvious step of making the behaviors more functional by turning them from objects into functions. This should make compositions and abstractions easier.

Another thing I'm experimenting with is making behaviors return promises. This is clearly an overkill in this case as all the code in the sample is synchronous, but it could be a solution to the problem @whitetigle mentioned with multiple callbacks or if in the future we decide to use web workers. Thanks to F# computation expressions, we can deal with multiple promises as if they were synchronous. In any case, please note the code is still too simplistic: if one promise takes too long to be resolved that would halt the animation.

Unfortunately, I'm gonna be a bit busy the next days so I may be not able to check progress in much detail, but please contact me at any time for specific questions/requests and if you want me to review code so I can try to give advice when possible (you actually have much expertise than me with pixel-based GUIs :wink:).

Really looking forward to the development of Fable's game engine!

whitetigle commented 7 years ago

Hi ! First of all, thanks @alfonsogarciacaro for the amazing piece of code you shared. It has been literally enlightening for me. Especially bypassing OO way of thinking through function composition and closures.

So here are some new ideas I'm evolving around. Sorry it may look a little bit messy and far from complete but maybe it will light some sparks?

Interactions.

I started to ask myself a very simple question: what is an interaction?

Citing wikipedia: Interaction is a kind of action that occurs as two or more objects have an effect upon one another. The idea of a two-way effect is essential in the concept of interaction as opposed to a one-way causal effect

So I started with Human Machine Interactions. User presses a key. Something happens. Thus User touched something = something was touched.

Then I took other samples related to video games and tried to understand what is an interaction and what it does mean..

OneToOne

Context: a moving player grabs a rotating coin.

Let's cut this to:

A. Player has a moving behavior B. Player can interact with a coin C. A coin has a rotating behavior D. A Coin can interact with player.

Let's say that there is only one player and many coins. A possible meaning would then be: A Coin is an Entity waiting for an interaction with a Player. Let's generalize a bit: a coin is an entity which has some behavior and can interact with another entity which also have some behaviors called Player. Furthermore it is waiting for this one very special interaction with the Player. And this very special interaction is a simple collision.

So we've got entities that possesses behaviors. And can have interactions with other entities. But here fundamentally what we have is a one-to-one relationship between two entities. Nothing will ever happen if these two don't collide. And no other collision exists for one of these two.

Let's take another example: a player character touching a door to open it. It would lead to the same kind of interaction; the player has to collide with the door. Or we could say the door is waiting for the player to collide with it.

Another example: a glowing health kit is an entity with a glowing behavior waiting for a collision with another entity.

OneToMany

Context: the player fires a missile against hordes of foes.

From the missile point of view what we have is some kind of one-to-many interaction. Indeed the missile can touch any foe and it will have consequences.

So let's cut this: A. The homing missile is an entity B. it has a moving behavior C. A foe is an entity D. on collision something may happen to both entities or only the missile.

So the missile is kind of agnostic. The differences with the previous OneToOne samples are:

  1. the target is unknown until collision happens.
  2. the result of the collision between the entity called missile and any another is unknown. It means that it will be solved on collision. Here the context of both entities is important.

Last but not least, we can add one new information: one entity may be able to transform another entity on collision. This was also true with the health kit.

OneToNone = Particles

The last entity would be a particle which has no collision with any other entity and exists only as a visual candy .

OneToNone=

  1. Entity
  2. with Behavior
  3. without collision

For instance: non dangerous fire sparks, stars in the sky, explosion particles, smoke, etc...

An entity will die

Every entity has its own lifespan and has some known dying condition .

For instance, a particle will die after it's alpha value reaches zero. A foe or an hero will die when taking too much damage. A mechanism will "die" with the death of the level (for instance a door). A health kit will die after a short timespan. etc..

So we need a die check to be able to dispose entities.

First conclusion

So far we have

  1. entities
  2. that have behaviors = inner transforms
  3. whose only goal is to collide to generate events/consequences/effects
  4. that can be transformed on collision by other entities
  5. that die

Then we have

  1. OneToOne relationships: an entity is waiting for a collision with a designated entity
  2. OneToMany relationships: an entity can interact with any other entity.

On collision

So now we're left with a central question: on collision things happen. But what are such things and how do we manage them?

Sample 1: player shoots foe. Let's refine this to keypress event -> spawn bullet entity with moving behavior
even better keypress event -> spawn bullet entity at position with time/frame based transforms

then update until collision happens. When collision happens, if relevant apply transforms. How can we know that it is relevant?

Case 1: bullet can touch quite any entity. On collision we try to apply transformations but it may fail. ( (Type checking? )

Case 2: bullet can only touch one defined subset of identical entities. Transformations to bullet and target are automatically applied on collision.

Case 3: bullet can only touch a defined subset of compatible entities. Transformations to bullet and target are applied on collision but effects are contextual to the entity.

Sample 2: player grabs some health kit WIP :wink: .

Random topics

Here are other things I'm thinking about.

Mixed interactions

These need both ingame collision and user interactions. For instance, to open a door, the player needs to make the collision between his ingame character and the door happen. Then when the character is inside the collision zone, the player must press the enter key to actually open the door. So we would need inner and outer interactions.

Scene

What is a scene?

Could it be the collisions of Entities that, when all dead, trigger an end? Then could this mean that some Entity may be empowered with some death power over all other entities?

For instance the door of the level that when colliding with the hero triggers a next level event and forces every entity to die?

What is sure is that there are some conditions that trigger the a scene's ending if such exists;

Time for more playing

Thanks for reading. I will continue to think, refine and list more interactions to try to have some complete system and see how the whole could work together.

MangelMaxime commented 7 years ago

What an amazing topics @whitetigle

This is really interesting to cut the interaction part like that. It's give me some idea (and some for presentation of the interaction). I will try to find some time tomorrow to share my mind :)

I think that if we could keep going by splitting interaction like you did. We could find some schema/architecture we want to follow. I will explain better my ideas tomorrow with some drawing etc. :)

whitetigle commented 7 years ago

Great @MangelMaxime, can't wait to know what you have in mind!

MangelMaxime commented 7 years ago

Here are my ideas. At first I was not wanting to use classical Entity management because I find it a little old but from you code and sample I want to go deeper with it. Sorry don't really have time to work on this project for the moment so I will only give ideas.

All the thing that @whitetigle said in is last long post are really nice and a clear approaches. We saw that we can simply relate on making every thing a behavior and it's depends the mix of these different behavior make the "Entity" specialized.

From my point of view, when reading, your post I was making some groups of behavior and here ares the ones I have in minds:

Here I have just tried to make some sort of organization around the behavior. In the samples with the dots, we are only manipulating the position of the dot (ok ok also, the color, size, etc. but it's not important^^). What happen if I want to have Health attach to my player Entity.

We could make use of inheritance to make Player inherit from ESprite for exemple but the past show that OOP concept are goods but not perfect. :)

So here is my proposition. Sorry I tried to make a Graph but couldn't find a good presentation. So it's will be textual. Hoping it will be clear enough.

We could have what we call an Entity, which have Component attach to it.

A Component has a list of behavior and also it's own reference to Attributes.

An Attribute is for example the X coordinate, or the number of Health.

I can say that an Engine is the entry point of a game. And that Engine contains a list of Entities which have different Behavior and Attributes.

Here we see that there is a lot of internal list because:

So to render one frame we will need to iterate over a lot of list. But we could optimize this by having several updates running in parallel. It's something common in game engine to have a Rendering loop and another for the Physics for example. But we could make it possible to have more for example, if the game got some IA probably we need a loop for it too etc.

So we could puts tags to help the loop to take place. A tag can be "Physics", "Display", "IA", etc.

Like so we can simplify the access to the different sub trees etc.

Only problem is how to make the different loop to synchronize at some points. This specific point make me think of the Railway Oriented Programming where you have different tracks and them as the last you have a merge function. But on this point I am not sure of the implementation :)

One more idea about the loops could be to have specialized loops for Particle system, Background, etc. The idea, is in the far end: If we have some heavy charge in the current scene we can probably decided to stop the particle or reduce it's updates or even better reduce the count of particles drawn etc.

And one last point, it's possible that all this loop are only updating internals value. And them only when the Frame is ready to be draw we should commit the current calculated value.

I tried to lecture myself several times and hope I have been clear enough to share my ideas :).

alfonsogarciacaro commented 7 years ago

Sorry, I still need to go through your very interesting comments in depth, but just I quick note: collision detection is indeed one of the most demanding calculations in games. But if we're to use a physics lib like matter-js, they usually already have performant ways to detect collisions. I've to check yet about matter-js but in other libs there's usually a flag to specify if the entity is not moving or not participating in collisions to improve performance.

whitetigle commented 7 years ago

Thanks @MangelMaxime for all these ideas 👍 I too need to take time to review all this! Another interesting lib: sat collision detection

[EDIT while I'm at it]: simple camera primer no object culling in pixi

MangelMaxime commented 7 years ago

Ah yes now that you say that, for the physics loops this could be only the part where our app interpret the result given back by an another library. Via events, or whatever communication system.

When I said loop this because this a recurrent action. But we could use event or interact with another library more specialized to handle physics, or particles updates etc. Idea is to be sufficient generic because users could want to use one or another libs depending on is feeling and the performance needed. All physics library are fully performant in all cases if I remember correctly.

whitetigle commented 7 years ago

I have been thinking about all this lately.

I have to admit am a bit afraid of us speaking about a Functional Game Engine since there are dozens or great game engines out there today. But I know that having functional tools would can developing faster and even better: safer.

That's what I was looking for and that's what I'm getting today. Less lines. Easier to read. Even more safety. Fast compilations. Fast iterations. Great support.

So I was wondering about instead branding Functional Game Helpers: libs that we could promote without the need to brand a complete full fledged architecture: Functional tools to get started easily. Getting a piece of the cake instead of the whole cake + chocolate sauce.

Then as we said we could think of use cases and refine our thinking around these use cases like we said for the platform sample. This could be promoted like: here is how we could do a simple platformer using fable and pixi.

And that's exactly what we did when @alfonsogarciacaro reworked my sample to make things functional: we can show something that was at start imperative and object oriented and how it became functional.

And we could then introduce some mechanisms available from our libs (like our behaviors or the entity+component system @MangelMaxime is considering) through the samples.

I know that I may sound redundant but while we're deep thinking I think this is a good time to speak about what we want in the end.

[Edit] To summarize: neat tools and functions that would do their job and could be added to any engine.

MangelMaxime commented 7 years ago

From my point of view, I don't like game engines. Really yes ^^. I feel so constraint about using them.

But what I love is Helpers, or libraries doing well their job and only their job. So I am on for just considering showing the Functional way and have Functional Game Helpers. Like so we could enrich the Eco system we helpers if needed. And user can simply take what he need to write is game or part of the game.

MangelMaxime commented 7 years ago

And also having a full game engine is a hard and long work. So my ideas would be the following:

To be completed :) But this some points could be a good start and are the most common case encounter in games I think.

whitetigle commented 7 years ago

@MangelMaxime That's ok for me 👍

whitetigle commented 7 years ago

wip

So I've taken time yesterday afternoon and today to put our first sample together. I've got

I'm working right now on collisions and once done I will add some gravity then we'll have some sample we'll be able to review together.

The code is already kind of simplified / optimized and turns at a steady full screen 60fps without any slowing down.

I'm working with SAT.js for collisions and I was relieved that I was able to use it right away after converting the .ts file. Nothing to change at all. That's really great!

That's it for today :)

MangelMaxime commented 7 years ago

Really nice to see your sample :) @whitetigle

For my part, I am working on some experimentation for UI management. For the moment, I go with pure 2D canvas implement (means without Pixi.js) just to keep things simple and see how Functional Paradigm can be go.

The project is here. Most of the Code will need a pass to make it clearer and add documentation.

But I am quite happy with the Mouse management. Because we simply need to start the Mouse.Init and after from anywhere in the App we have access to the Mouse State via Mouse.State

I will add the same kind of module for the Keyboard.

And this two files could be a first brick for Functional Game Helpers because they abstract the Events etc.

whitetigle commented 7 years ago

So you are using Aether?

This is very interesting I will definitely take a look because I would like to see how it works. It's kind of cryptic for me. Thanks for sharing! For the mouse that seems indeed easy to use.

Regarding the keyboard, I am right now using some simple handler that can bind multiple keys for the same "event" (jump, moveleft, etc..)

It's clearly far from being functional proof (was updated from a previous imperative project) but what's interesting me is to be able to trigger an event with any key that was previously registered.

For instance bind Up arrow and W/Z key to the Up event. I don't know if you have already considered this approach, but it would be great to be able to remap the keys like this. I know it's very game centric but maybe it could give you ideas?

MangelMaxime commented 7 years ago

Yes I am using Aether now. And I use it in all my projects. It's really help having a clean syntax when updating nested records.

I just need to write a Type Provider or something to dispense my self of writing the statics methods :).

Thanks for sharing your code. Yes has you said your code is game centric but could a be a start I will what I found. My idea would be to make a Map of the keyboard state something like the Mario Sample shows but we more utilities behind.

MangelMaxime commented 7 years ago

Ok, so after this week-end of experimentation, I finish to have something good.

It's based on Immediate Mode concept and use direct Canvas Api and so it's only handle the rendering. The data storage is up to the user.

Here is the GUI code.

And below you can see how to implement a button and output "clicked" to the console on click. Full code

    if GUI.Button(1, "Click me", 50., 50.) then
      console.log "clicked"

I just wanted to share this little update. I will work on more widgets during the week. And I will try to create the first Layout too which could make a big plus.

PS: The port to PixiJS should direct for the moment. But I don't thinks it's needed to keep the experimentation going. My only concern about PixiJS is that PixiJS used Sprite to retain state of drawing object. Where here I directly draw the pixel. Should we use only one Sprite and use it to draw all the GUI ? @whitetigle Do you have an idea of how you would do it with PixiJS ?

whitetigle commented 7 years ago

@MangelMaxime ok; What I can do if you want is try a port to pixi when your experimentation is done so we can try several strategies and benchmark them? Anyway I really enjoy the immediate mode. That's very interesting and so easy to use and read. Thanks for sharing 👍

MangelMaxime commented 7 years ago

Yes sure, benchmarks are always welcome to compare this kind of stuff :)

I will need to write a Fable plugin I think to have an automatic management of the IDs.

Because they need to be the same at each render loop. So I used __LINE__ to get the line number of the code as a temporary automatic solution. This allow you to not have to write maintain yourself the ID's uniqueness.

The currently have the limitation to not support widget creation over a loop otherwise they would have the same IDs. ^^

alfonsogarciacaro commented 7 years ago

I had a look at the code, cloned the repo and run the sample. Looks nice! Great job @MangelMaxime :clap:

Dealing with events in the render loop is a very interesting idea and simplifies development, though as it's done synchronously it can prevent the use of reactive streams.

Also, drawing a GUI directly with Canvas frees us from the tyranny of DOM and CSS. But it makes it a bit difficult to work with several screen sizes, I wonder if it's possible to adapt the canvas to the screen size easily. Something similar is done in the threejs sample.

MangelMaxime commented 7 years ago

I will just keep one thing from your comments @alfonsogarciacaro

[...]frees us from the tyranny of DOM and CSS

Thanks for your feedback. That's true that working with several screens sizes is one of the next challenge. Actually first one will be the Layout system :) but some prototypes are already on the work.

This should be possible to adapt canvas to the screen size yes. Could be done with calculated aspect ratio like shown from your sample or we could directly manipulate the canvas attributes. If i remember their was a way to work with the width/height from css and direct attribute to make simulate a resolution.

But I would go for an aspect ratio because this should open more doors for the future.

Here is what I am planning to do:

  1. Slider (horizontal/vertical)
  2. Add keyboard support
  3. Text field
  4. Checkbox
  5. Work on a prototype for the layout system
  6. Try to solve this issue about ID generation

PS: You see @alfonsogarciacaro I really try to find a usage to plugins (between Aether and this problem :D )

whitetigle commented 7 years ago

Just a quick follow up only done using Behaviors:

wip

MangelMaxime commented 7 years ago

WooW.

That's really cool.

whitetigle commented 7 years ago

Thanks! 😄

whitetigle commented 7 years ago

Hi there! Sorry guys, I've been really busy these days finishing a job and running for a new one. Running. Halting. Doing great stuff. Doing boring stuff. Well. That's the freelance life (tm) 😉

Anyway, I am in a rather cool in-between spot right now. Meaning, I've got a few days to make new neat and fun things with @fable.

I've been thinking lately about many things (random) regarding Fable:

So all in all, maybe there's something to find in between all these ideas that could be updated from time to time. I don't have clear ideas yet I feel you may have some to share so something's can come out?

Thank you for reading and congrats again for the release!

whitetigle commented 7 years ago

Oh there's something I completey forgot mentioning: Sonic-Pi is a great project with a great and active community . And it seems like we can communicate with it through OCS messages.

Maybe creating some kind of bond with this community through a simple and nice fun project could be a good thing?

[EDIT: if we want to take this road I first need to do some tests]

MangelMaxime commented 7 years ago

Allowing people (or here kids) to explore their creativity could be something really cool. I think, it's something which start to make is way in some schools and can be a way to go :)