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 8 years ago

alfonsogarciacaro commented 8 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:

alfonsogarciacaro commented 7 years ago

@whitetigle All these ideas look awesome! And absolutely, behaviors are great and I think they fit the functional paradigm very well (simple functions, composable...) Do you have anything that we can use as a basis to write samples using behaviors?

On my side I don't have anything yet but I've been investigating a bit about web workers and I think it could be possible to write a kindda proto-game engine that puts physics calculation to a webworker and sends it to the main thread on every requestAnimationFrame cycle so it can be used by the behaviors. As usual, the main limitation for this is my time budget :/

About cool tiny samples for quick testing it'd be really nice to have an online editor to edit the samples in real time (again, time constraints...). Sonic-Pi looks really awesome! Is there any prototype of how interaction with a foreign language through OCS messages could work?

BTW, any news about upgrading to Pixi 4? I think @davidtme already has some bindings for it?

davidtme commented 7 years ago

I've abandoned using pixi mainly due to slow (and understandable) bug fixes and I was spending most of my time trying to keep the mutable scene in check (didn't feel very functional). I'm happy to help get the binding up to v4 (or any other way) because I do love the engine just. I used pixi v4 with the v3 binding and just fixed any funnies as I went along. I don't know how different it is really https://gist.github.com/davidtme/cb3d58024c2fdfa0cc6acca45cbbabff

I'll try and post up (in that gist) any other helpers I made or snippets - like importing .glsl with webpack which I think v4 uses

whitetigle commented 7 years ago

pixiv4

I do agree with @davidtme about the bugs. I have been watching the issues since the release of the v4 and there have been quite a lot of things that just did not work. Now things seem to get better. Still I don't know if it's worth the work right now.

BTW the API seems to have quite a few changes: v3 vs v4

webworkers

FYI there's some demo with 3D physics engine oimo.js with workers here.

Behaviors

I do have samples to showcase. I could easily create simple ones. But right now, what would be Uber cool would be define the right architecture to use them. And here I need your help.

There are a few things that I am currently not happy with:

"Start this and then when it's done, let's start that and that and when it's done start this new thing and then when all is done, eventually trigger this"

So here is the current (dirty) state of the Behavior System I have been using.

And here are the Easing functions I use. and that we could add to fable/graphics

So what I can prepare is a simple complete sample written like I usually do with behaviors and see how we can transform this? Then I will base all my samples on this new architecture.

Sonic-Pi OSC

Here is some Erlang test.. There's a javascript lib that I can try to use: OSC.js. So it seems that it should not take long to know if it works.

MangelMaxime commented 7 years ago

I think that having one simple complete sample like you usually do can be a good start. And like you said from here we could, start tweaking your code with feedbacks from others.

PS: I have little during this 1-2 weeks but later I should be free again to work on Pixi.JS again.

whitetigle commented 7 years ago

ok!

BTW just some more benchmarks using 2d/3d html5 frameworks

[EDIT]: results from my PC

2016-11-23 13_43_23-benchmark - bl ocks org

whitetigle commented 7 years ago

Hi there! I just finished a simple complete sample using behaviors.

@alfonsogarciacaro: I have updated just a little bit your architecture to fit my needs and so we have promises, behaviors as functions, and ESprite with IDisposable.

In order not to create a new branch in fable repository yet, I created a new repository here: https://github.com/whitetigle/fable-behaviors. If we decide to use this sample once cleaned-up, then you'll tell me where to move it (samples?)

Since GitHub allows hosting, there's also a live demo to play with: https://whitetigle.github.io/fable-behaviors/

what's inside this sample?

So I decided to have fun with GitHub API and get some data to display. So here we have the history of additions and deletions per week since last year (doc / fable actual data )

For each week, I spawn additions particles that go to the fable repo/planet and deletion particles that leave the repo to vanish in space.

Then I also update counters that sum deletions and additions (I could do that better, but it's a start). What's interesting is that some week just display huge activity. That's quite funny to watch. And some none (holydays maybe?)

Since this uses the GitHub API without authorization, and we do have a limit (60 queries/hour) I also cache the data for the current day in Local Storage.

Everything being far from perfect, sometimes getting the data does not work (I don't know why) and so I create an empty array [] in LocalStorage. So to make it work again, we need to remove the content of the LocalStorage.

So as you can see there's some more work to do if we want to use this sample.

Space theme πŸš€

Last but not least, sorry for this "space" theme, but to go fast I just reused some of the logic of my previous project. (and I kind of love space themes πŸ˜‰ )

Pixi

So I am still using Pixi v3.0. In this sample we have a few interesting things to comment upon (if we want to use and document this sample):

Ah just before I forget: I did not use scaling. So the sample works in a set resolution of 1024x600.

Conclusion

Nothing's better than a cool pic. So here it is! wip

Hope you'll like it!

MangelMaxime commented 7 years ago

@whitetigle Really cool showcase :)

I will try to play with it a little and learn about Behaviors etc.

whitetigle commented 7 years ago

Thanks @MangelMaxime! The code is not that pretty yet. So if you need help ping me :)

Andrea commented 7 years ago

Nice stuff!!

On 29 Nov 2016 8:39 a.m., "Whitetigle" notifications@github.com wrote:

Thanks @MangelMaxime https://github.com/MangelMaxime! The code is not that pretty yet. So if you need help ping me :)

β€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fable-compiler/fable-graphics/issues/4#issuecomment-263509096, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAMH-psmXtmN9LL1m8AykfvWaU30j_Eks5rC-TQgaJpZM4J6up3 .

whitetigle commented 7 years ago

Thanks! πŸ˜„

alfonsogarciacaro commented 7 years ago

Hi @whitetigle! Sorry I didn't reply before. The app is beyond awesome, fantastic work! πŸ’ƒ

With your code and all the information you've provided, now I have a lot of ideas, but I'd rather start little by little or nothing will be done at the end :wink: I'll try to send you a PR this weekend to ask your opinions. Some of the ideas I'm considering:

That's my food for thought at the moment. I'll try to send some actual code soon :+1: And again, thanks a lot for your awesome contributions @whitetigle!

whitetigle commented 7 years ago

Thanks @alfonsogarciacaro ! So I'll wait for your PR so we can review all the things to do and discuss all the points you raised one by one.

whitetigle commented 7 years ago

Hi! just so you know, as planned, I just finished a simple proof of concept to interact with Sonic-Pi from a web page using Fable. I think we'll be able to build fun things with that from now on.

It's there with full instructions.

wip

By the way thanks @alfonsogarciacaro for your help today. I was desperatly trying to use osc.js and could not find my way. Now it works πŸ‘

alfonsogarciacaro commented 7 years ago

Woooow, looking awesome!! NP! Any time you need help just ping me, you can also contact me (and other Fable users) in Gitter for quick answers πŸ˜„

whitetigle commented 7 years ago

@alfonsogarciacaro I took some time to review and start answering your remarks

  • I'll probably make the Behaviours synchronous again, I'm afraid some of the promises may take too long to fulfil, locking the animation loop.

ok

  • I'll try to separate the skeleton of the app into a reusable file/lib.

ok

  • Would it be useful if, instead of sprites, behaviours act on a custom model and then the model is used to update the sprite? This would make the code more decoupled from Pixi, but maybe it's not that convenient at the end. At the very least, Behaviours should accept custom types deriving from ESprite.

I don't know. Although I understand the desired effect, I am not a fan of generic systems. For instance, Pixi is made for speed and is clearly an agnostic engine. Its goal is not the same as Phaser which provides with a game centric approach although they do share a great part of the same code (at least for now)

So in the end I think I would prefer having several behaviour systems, each one tailored for the underlying engine: pixi-behaviors, phaser-behaviors, pandajs-behaviors?

Well it's clearly a vast topic, I need to think more about this. Maybe you would have some ideas in mind?

  • Organize the objects in hierarchies, as with React components for example. This way, calculations made in the parent can be passed to the children. How do containers work in Pixi?

Well a Container is a DisplayObject and a Sprite is a Container and then we have ParticleContainers which are deriving from Container as well and have their own optimised code under hood.

So sometimes my root container is a Sprite so that I can apply effects to the whole stage. The shake effect works like this for instance.

In the end it's all about having Containers or ParticleContainers.

  • Implement an event system. Probably a different (fictitious) thread should be collection event messages and then feed them to the behaviours in each iteration of the animation loop.

ok

  • [Future] Add object pools so we can reuse memory. I have some ideas to use JS hacks in a way that it looks like you're actually constructing and destructing objects in F#.

ok

  • [Future] Move intensive calculations to a web worker and collect the results (preferably passed with a shared Array Buffer) as if they were events.

ok

whitetigle commented 7 years ago

@alfonsogarciacaro about the model: were you thinking about using behaviors with CSS or canvas as well for instance? Maybe we could find something to do.

whitetigle commented 7 years ago

Hi everybody! First of all, Happy new year! πŸ˜„ I wish all your professional and personal projects all the best!

I don't know what @alfonsogarciacaro thinks but it seems like 2016 has been a great year for Fable. And I hope 2017 will even be better!

I have been quite busy the last few weeks with teaching so I did not write any new F#/Fable things. But I do have Fable things planned:

If you are ok, I think I could turn some of these projects into Fable samples? I would also like to do the same with the GitHub data project since it seemed to trigger some interest but I am still stuck on the architecture side. I will try to refactor things. but If you have time to continue our discussion, I would of course be happy to follow new leads.

Now, if you would like me to go any another way to promote Fable through samples, please tell me what you would need.

A last word, special thanks to @alfonsogarciacaro and @MangelMaxime for your help regarding the learning process of F# and Fable. I have been able to do great and fun things thanks to your energy. I will try to contribute to help you reach your goals too in 2017 πŸš€

MangelMaxime commented 7 years ago

Hey, happy new year to you too @whitetigle

It's would be really great to have some of your projects are samples.

I think, this would be a great things to have a real but small game (perhaps several) as a sample. Because, on every projects we are always showing small part of the libs etc. but the gap between a sample and a real case is too big.

For example, in Fable Arch we have something like 10 samples showing the basics. But nothing showing what a real web app could look likes and this is really much the harder part than just making a clock or a counter :). This is why, I am writing the docs website using Fable arch itself to serve as a concrete sample.

I probably made a little digression here but nevermind. :)

I would love to help you work on the samples/documentations part for pixi.js if you want @whitetigle we could discuss about it on Gitter. I think we really need to dig dipper about the bahaviors system which was looking really promising the last time I checked it.

whitetigle commented 7 years ago

I think, this would be a great things to have a real but small game (perhaps several) as a sample. Because, on every projects we are always showing small part of the libs etc. but the gap between a sample and a real case is too big.

Ok! I think I understand your point.

I would love to help you work on the samples/documentations part for pixi.js if you want @whitetigle we could discuss about it on Gitter.

Great! We'll do that then πŸ‘

I think we really need to dig dipper about the bahaviors system which was looking really promising the last time I checked it.

Cool! We'll see that then.

MangelMaxime commented 7 years ago

Just wanted to share status of my current work.

I am writing a minimal game and the goal is to see how functional I can make it. I am not allowing a lot of mutable state in the game architecture because performance are not a problem yet ^^. And also, by having the minimal mutation possible I think we can minimise the side effects.

GameState

Here is the GameState record:

  type GameState =
    { Renderer: WebGLRenderer
      Id: int
      SpriteSheets: Map<string, ResizeArray<Texture>>
      State: State
      Root: Container
      MouseState: Inputs.Mouse.MouseState
      KeyboardState: Inputs.Keyboard.KeyboardState
      Data: obj
      Containers: Map<string, Container>
      Resources: loaders.ResourceDictionary
    }

    [<PassGenerics>]
    member self.GetData<'T>() =
      unbox<'T>  self.Data

    member self.ClearStage() =
      self.Root.destroy(true)

      { self with
          Root = new Container()
      }

    member self.GetCenterX () =
      self.Renderer.width / 2.

    member self.GetCenterY () =
      self.Renderer.height / 2.

    member self.Survivors
      with get () = self.SpriteSheets.[Survivors.SPRITES_SHEETS_KEY]

As you can see from this record we have access to inputs state (Mouse and Keyboard) and most important we have Data property. This property will be where the user store all the information he wants.

We simply use GetData<'T> to unbox the data type and get intellisence etc. Let's just consider it's working I need to polish this part and advance more on the sample.

Inputs

Initializing the inputs is as simple as:

    let state : GameState =
      { [....]
        MouseState = Inputs.Mouse.init stage
        KeyboardState = Inputs.Keyboard.init renderer.view
        [....]
      }

The keyboard inputs are attached directly to the canvas because they are globals to the application. However the mouse inputs are attached to a Container this allow more flexibility (could be attach to a sprite for example).

The goal here is to give access to a functional representation of the inputs.

For example, the Mouse is simply a record containing the X, Y position etc.

  type MouseState =
    { mutable X: float
      mutable Y: float
      mutable Left: ButtonState
      mutable Right: ButtonState
      mutable Middle: ButtonState
    }

The property are marked mutable but the user do not need to update the value only read it. The values will be updated thanks to the listener register by Inputs.Mouse.init

Scene hierarchy

A game is dynamic and we need to know how to update the GameState or even the view depending if we are on a SplashScreen, Loading, play a level etc.

To do that, I use Discriminated Union to represent the current state.

  type BasicSceneDU
    = Init
    | Run
    | Pause

  type Scene
    = Level1 of BasicSceneDU

  type LoadingDU
    = Init
    | Waiting

  type State
    = Nothing
    | Loading of LoadingDU
    | Scene of Scene
    | SplashScreen of BasicSceneDU

Thanks to this DU, I know that when the state is Scene (Level1 BasicSceneDU.Init) I need to create the scene data and set the next state to be Scene (Level1 basicSceneDU.Run)

GameLoop

The gameLoop is the function which contain all the game logic.

For example, if we take the last scene here is the code used to handle the scene init and logic to play during the level.

    let rec gameLoop (gameState: GameState) (dt: float) =

      let newState =
        match gameState.State with
        | Scene subScene ->
            match subScene with
            | Level1 sceneDU ->
                match sceneDU with
                | BasicSceneDU.Init ->
                    let state' = gameState.ClearStage()

                    let ground = new Container()

                    state.Survivors?(Survivors.SPRITE_GUN)
                    |> unbox<Texture>
                    |> makeSprite
                    |> setPosition 100. 100.
                    |> addToContainer ground
                    |> ignore

                    createText("Level 1")
                    |> setPosition (state'.GetCenterX()) 50.
                    |> setAnchor 0.5 0.5
                    |> (fun x -> state'.Root.addChild(x))
                    |> ignore

                    state'.Root.addChild(ground)
                    |> ignore
                    // Set the state used to resolve the next Frame
                    { state' with
                        State = Scene (Level1 Run) }
                | Run ->
                    // Add here the game logic
                    // Example: Player movement
                    gameState
                | Pause -> gameState

      // Mutable the state variable
      // This is the only mutation needed
      state <- newState
      // Render the view
      state.Renderer.render(state.Root)

      Browser.window.requestAnimationFrame(fun dt ->
        gameLoop state dt)
      |> ignore

Summary

There is a lot of code in this message sorry :). If we try to summarise:

Another benefit but not yet shown, will be the possibility to write a TypeProvider to have intellisence over the spritesheets.

I hope my explanation was clear if you have any questions/remarks don't hesitate :). I will try to post my code later, when I will have played with the game logic and the Behaviors system developed by @whitetigle and @alfonsogarciacaro

whitetigle commented 7 years ago

Excellent work @MangelMaxime thanks for taking time to share with us. πŸ₯‡ And while I am at it thanks for the awesome doc on fable-arch. πŸ‘

I will try to play with both architectures in my next projects and give you feedback.

Meanwhile, I think it's now time to move to Pixi v4 since v3 is now being deprecated even from the examples. (https://github.com/pixijs/examples/pull/24) I will take a look next week to see how we can move to v4. But maybe @davidtme would have some updated versions of the bindings?

whitetigle commented 7 years ago

Question: what if we use this electron app to hold the mini samples I was talking about ? I had a haxe based NW.js project which worked like that. We could have a front page/menu with a tiled display. Clicking on a tile would launch the mini sample with the inline doc and highlighted code?

PS : ok to use Kenney assets too !

Le 30/01/2017 Γ  22:24, Maxime Mangel a Γ©crit :

@whitetigle https://github.com/whitetigle I just made my code available here https://github.com/MangelMaxime/PixiTraining

This is not the best code ^^ but I am just raging on Pixi and also game dev (it's never go the way I want ^^) at the moment. So I need to make a pause. My idea was to use functions like in Mario sample http://fable.io/samples/mario/index.html to handle the steps inside the Scene Run.

I think the code can be enough to show the "function way" I was trying to follow. If you have any question please ask them here, or open an issue on my projet or even on gitter :).

PS: For the moment, we just have a player moment left and right ^^. PS2: I am using assets from Kenney - Abstract Platformer http://kenney.nl/assets/abstract-platformer

PS : I put your message there so we can get it for history.

MangelMaxime commented 7 years ago

^^ I was removing somehow on purpose because I don't want to give up ahah (change my mind after a good sleep πŸ›Œ ).

For me this is ok to use electron to hold the mini samples. I am just trying to focus on the architecture for the moment but nothing prevent us to add a step before to make the page/menu.

Today goals for me is to animated the sprite with a walk animation.

MangelMaxime commented 7 years ago

@whitetigle I added a function to add animation for the player (really basic it's don't ensure full animation cycle, etc.). Just wanted to point this out because I saw you fork my repo :)

I will stop here (for real) for the moment. I don't know how to implement the collision and would like to know if you have any idea or sample for it.

whitetigle commented 7 years ago

Oh well I usually use physics based collision detection through engines like nape or box2d.

So today in the javascript world, there's matter.js we talked about. (cool pen by the way here)

One of my latest experiment with Fable was with SAT.js. I did not finish this sample but if I remember well basic collision was working. So I could go on with that if you wished.

Since I also use game engines based on physics like Construct2 or even 3D things like Unreal, I think our best move would start using one and avoid trying to reinvent the wheel or stick with simple things like this one.

Now I'm a bit biased on the question because on the frontend side I have always been more into puzzle games with lot of particles than action games especially platformers (shmups collisions being easier to solve). And usually collision has been just a matter of bounding boxes, hit points and basic distance tests without all the penetration stuff and complex shapes.

So being very pragmatic, when I need more then I simply go with a physic engine. So for the samples since what's at stake is making things functional whatever the underlying engine, we could choose one for all our collision needs? It would also allow for neat samples right away and would allow us to work on the logic rather than on that part of the engine. And make shiny things to show on social networks to promote Fable faster.