clojure-gamedev

Jakub Zika 2021-02-22T08:12:08.006300Z

What are you using on FE? Re-frame etc. or do you paint in canvas?

Fabim 2021-02-22T11:12:39.006600Z

@jakub.zika-extern I normally use re-frame for my projects. For the game instead I use html divs and css animations. I would switch if I find a good what to handle the animations

2šŸ’Æ
Jakub Zika 2021-02-22T11:16:17.006800Z

@merklefabian I am playing with Quil currently (wrapper over processing(js)), it is nice. You can create simple 2d/3d games with it. http://quil.info/api You can also provide atom to quil so it can be easily connected to RUM (and reagent and reframe - but i did not try it yet), so you can control your canvas by custom html elementsā€¦ + I would recommend tips taken from: https://github.com/alexkehayias/chocolatier Tips Here are some tips for optimizing performance of game loops: ā€¢ Use the Chrome dev tools to do a CPU profile and trace where time is being spent ā€¢ Donā€™t useĀ `partial`Ā orĀ `apply`Ā as they are slow ā€¢ Always specify each arrity of a function instead of usingĀ `(fn [& args] ...)` ā€¢ Donā€™t useĀ `multimethod`, useĀ `cond`Ā orĀ `condp`Ā and manually dispatch ā€¢ UseĀ `array`Ā when you need to quickly append items to a collection (mutable) ā€¢ UseĀ `loop`Ā instead ofĀ `for`Ā orĀ `into`Ā with transients or arrays as the accumulator ā€¢ Avoid boxing and unboxing i.e multiple maps/fors over a collection, use transducers, reducers or loops ā€¢ Donā€™t make multiple calls to get the same data, put it in aĀ `let` ā€¢ Avoid heavily nested closures as the lookup tree becomes very long and slow ā€¢ Favor eager operations over lazy operations i.eĀ `reduce`Ā instead ofĀ `for` ā€¢ Donā€™t useĀ `concat`Ā orĀ `mapcat`Ā as they can be slow and generate lots of garbage ā€¢ Donā€™t useĀ `last`Ā as it will need to traverse the whole sequence, useĀ `nth`Ā instead if you know how many elements are in the collection ā€¢ Donā€™t use hashmaps as functionsĀ `({:a 1} :a)`, instead useĀ `get`Ā or keywords as functions ā€¢ Always return the same type from a function (V8 can then optimize it)

Fabim 2021-02-22T11:27:24.007100Z

@jakub.zika-extern Thanks for the extensive tips. I saw chocolatier before but had the impression that it was discontinued. Quil looks interesting. Why do you say Donā€™t useĀ multimethod` `

Fabim 2021-02-22T13:11:19.007400Z

@oconn Thanks for your answer. Would you share the code how you do 1. with me, please?

oconn 2021-02-22T13:34:26.007600Z

@merklefabian No problem. That workflow is going to be dependent on what youā€™re using for your websocket server & authentication / user pool. It may be more useful if you share architecture / code for your specific setup. With that said, Iā€™m doing this; 1. Each time a player opens the browser, I check to see if they are authenticated. (Do they have a valid auth token) 2. If the player does have a valid auth token then jump right to an authenticated connection

(let [connection-url
      (cond-> ws-url
        (some? credentials)
        (str "?token=" credentials)

        (nil? credentials)
        (str "?token=anonymous"))

      set-new-connection
      #(let [ws (new js/WebSocket connection-url)]
         (doto ws
           (aset "onopen" (partial ws-on-open ws on-open))
           (aset "onclose" (partial ws-on-close ws on-close config))
           (aset "onmessage" (partial ws-on-message ws))
           (aset "onerror" (partial ws-on-error ws on-error))
           (aset "connection-identifier" connection-identifier))

         (reset! last-connection-attempt (time-now))
         (reset! conn ws))])
3. On the server, before the authenticated connection is created, the credentials are checked against the user pool to identify the player. 4. The player can now send / receive messages over the websocket. You would adjust the onopen onclose onmessage onerror handlers to work with your app. Not sure what youā€™re using for your websocket server, but Iā€™ve had luck using https://github.com/ptaoussanis/sente and http-kit (one of its supported web servers) before.

Jakub Zika 2021-02-22T16:21:20.008300Z

Chocolatier is discontinued, yeah. Multimethod can get slower than using condp . Your code can get ugly once you will start optimizing JS / Java in Clojure - or you can keep your game simple and it should be fast enough šŸ™‚.

Fabim 2021-02-22T16:46:08.011300Z

Slower of call time or initialization? Iā€™m using multimethod a lot to separate out the logic of different cards. Didnā€™t notice any huge speed difference.

Jakub Zika 2021-02-22T16:46:56.011500Z

I remember that i had got some better call time results with condp but dont take it as granted. It is long time agoā€¦

Fabim 2021-02-22T16:48:07.012100Z

What are you using Quil for?

Jakub Zika 2021-02-22T16:51:11.012300Z

I did some fun stuff like walking in 3d environment with wasd and jumping etc. But nothing is finished yet. I am more interested in creating the engine and the toolset than the game itself to be honest.

2021-02-22T18:53:56.012800Z

why not edn in a text field?

Fabim 2021-02-22T21:09:37.012900Z

how would you persist ā€˜a text fieldā€™?

2021-02-22T21:36:28.014500Z

Postgres is highly optimized for indexing and searching data at a granular level. If you do JSONB, you get all the indexing and search stuff for free. If you do EDN in a text field, itā€™s just an opaque blob as far as Postgres is concerned, so you canā€™t look inside it and search on / index its constituents.

Fabim 2021-02-23T13:48:53.016100Z

@paul.legato What are lib are you using to convert the edn game state into JSONB before saving it into Postgres?

2021-02-23T17:34:37.016300Z

JSONB just means ā€˜JSON binaryā€™; itā€™s an optimized storage format used internally by Postgres. You can put any standard JSON in there. I use Cheshire, but any JSON will work.

2021-02-22T21:37:39.015700Z

you will wind up adding redundant metadata to the row that allows you to find, say, ā€œinventory for player 1234ā€ or ā€œall players who have item XYZā€, which will eventually drift out of alignment with what it says in the EDN blob. Synchronizing two sources of truth is a hard problem.