Well, the question is quite general, so I'll give a general answer. If you want specific details, feel free to ask.
Although play-clj supports functional game development, the base elements backing it are Java classes; accessed, set up, and modified in an imperative way. That means you will cause a huge amount of side effects using play-clj. And some behaviour may feel out of your hand when you're used to Clojure, but it sure makes some sense.
Despite the name, screen
contains a custom instance of the complete libGDX engine, that is it wraps Java-objects for sound, graphics, physics, and asset management. It gets mutated in an imperative way, by calling commands followed by a bang.
For example(let [screen (update! screen :world (box-2d))] (...))
will add a 2d physics engine to your game which will exist from that point of time, even after you leave the binding. Its functions may also mutate entities as side effects, like dropping a ball by applying gravity.
All the entities
on the other hand will get treated as properly as possible by functions like step!
(mutating their states by applying physics if appropriate) or render!
. They are maps, and their treatment is based on properties. :body
means, they need physics, :shape
means they need to be drawn as a primitive, :x, :y, :width, :height
etc. do what you'd expect, and so on. You can add other properties and work with them as usual.
Functions expecting these two arguments have one thing in common: If you give them a return value, the game uses this return value as entities
-vector from that point of time. So don't just modify and return a single entity unless you want to leave the poor bugger on the screen all alone...
Also make sure to read the docs at https://oakes.github.io/play-clj/, they explain a lot.