Closed mjkalyan closed 2 years ago
So, as it is, this PR folds with-scene
logic into do-game-loop
. You can see how it affects core example 2:
@@ -43,16 +46,13 @@
(defun main ()
(with-window (:title "raylib [core] example - basic screen manager")
(do-game-loop (:livesupport t
+ :scene *logo*
:vars ((scenes `(,*logo* ,@(alexandria:circular-list *title* *gameplay* *ending*)))
(frame-count 0)))
(incf frame-count)
(when (or (and (next-screen) (not (eql (car scenes) *logo*)))
(and (eql (car scenes) *logo*) (> frame-count (* 2 *target-fps*))))
- (setf scenes (cdr scenes)))
- (let ((scene (car scenes)))
- (with-scene scene (:free :later)
- (with-drawing (draw-scene-all scene)))))
+ (setf scenes (cdr scenes))
+ (switch-scene (car scenes)))
+ (with-drawing (draw-scene-all *scene*)))
And we control which scenes are freed when in make-scene
. So in this case we let the default :free :now
work on the *logo*
scene, which is only used once, while using :free :later
for the cyclic scenes.
My thoughts on the changes so far:
with-scene
switch-scene
to add-scene
and rem-scene
, and turn *scene*
into *scenes*
. This probably also means that *scenes*
should contain names for the scenes like their assets/object lists.do-game-loop
expose the scene objects (what with-scene-objects
does) of the active *scene*
automatically but I'm really not sure of the implications of this.
- I'm curious about whether we ever want multiple scenes drawn at the same time
Maybe. Remember that a scene is really just an encapsulation of variables and CLOS objects. There's nothing special about *scene*
other than convenience, and there's nothing stopping the user from calling set-up-scene
on a different scene or drawing objects directly from that scene. The problem you run into with multiple scenes is the same problem I brought up about with-scenes
-- namespace collision. So basically you're choosing between multiple scenes (but having to call scene-object
on everything) or having a single scene but you can have the convenience of exposing the variables automatically, as you mentioned in your third point. Personally, I prefer the latter.
- maybe it makes sense to represent a UI as a scene
Fun fact: There's no loading involved with Raygui (or at least I don't think there is; haven't gotten far enough to be 100% sure). Most likely direction at this point is we'll do the same thing re: creating an object hierarchy. Maybe the GUI will use scenes or maybe it will be a slightly different abstraction but there should not be any asset-like classes involved except indirectly, like this example. I sure wish it had a cheatsheet though, ugh.
- It kinda makes sense to let
do-game-loop
expose the scene objects (whatwith-scene-objects
does) of the active*scene*
automatically but I'm really not sure of the implications of this.
Yes for a single scene, no for multiple scenes. The implications would be that you can't reuse object names in your game logic, but I'd hope users wouldn't be trying to do that anyway.
- I'd probably change
switch-scene
toadd-scene
andrem-scene
, and turn*scene*
into*scenes*
. This probably also means that*scenes*
should contain names for the scenes like their assets/object lists.
Well... I'd be remiss if I didn't mention another approach. Use your multiple scenes technique, give each scene a unique name (so *scenes*
is a hash table), and then you can still expose the variables and ensure they are unique by concatenating each name to be like level2-monster5
. That has the potential to be kind of messy but also kind of awesome.
So basically you're choosing between multiple scenes (but having to call scene-object on everything) or having a single scene but you can have the convenience of exposing the variables automatically, as you mentioned in your third point. Personally, I prefer the latter.
Okay, that makes sense to me! We'll go that route then.
give each scene a unique name (so
*scenes*
is a hash table), and then you can still expose the variables and ensure they are unique by concatenating each name to be likelevel2-monster5
I elided the details but this is basically what I was thinking. We can keep it in the back of our heads for now.
Maybe the GUI will use scenes or maybe it will be a slightly different abstraction
Good to know, I should look into raygui more.
To complete before merge:
We want with-scene-objects
to handle multiple scenes, so we're going to want multiple scenes loaded at once.
If I understand @shelvick's intention (from #19), I think one approach that fits is having do-game-loop
track a list of active (loaded/should-be-loaded) *scenes*
(instead of *scene*
). do-game-loop
would initially call set-up-scene
on all the scenes in *scenes*
, and eventually tear them all down. Then we detect differences in *scenes*
and rectify them (setting up/tearing down) at the start of a loop iteration.
But... It also seems fine to just let the user track a scene list of there own, e.g.
(let ((my-scenes (list *level-1* *level-2*)))
(mapcar #'set-up-scene my-scenes)
(with-scene-objects my-scenes
(do-game-loop ...))) ;; I can decide to set up or tear down more scenes as I please
or, provide with-scenes
to ease this a bit
(with-scenes my-scenes
(with-scene-objects my-scenes
(do-game-loop ...)))
We can still set up and tear down scenes in the loop as long as with-scenes
doesn't have a problem with tearing down scenes that are already torn down.
Since the problem we were trying to address was just better handling of multiple scenes and we're not doing any binding stuff in do-game-loop
, I don't think we even need *scene*
or switch-scene
at all.
I think one approach that fits is having
do-game-loop
track a list of active (loaded/should-be-loaded)*scenes*
(instead of*scene*
).do-game-loop
would initially callset-up-scene
on all the scenes in*scenes*
, and eventually tear them all down. Then we detect differences in*scenes*
and rectify them (setting up/tearing down) at the start of a loop iteration.
This seems straightforward enough to me. Is there any reason not to do it?
We can still set up and tear down scenes in the loop as long as
with-scenes
doesn't have a problem with tearing down scenes that are already torn down.
It shouldn't. There may still be bugs but in general I was pretty careful to check for NIL's and all that in the free
methods.
Since the problem we were trying to address was just better handling of multiple scenes and we're not doing any binding stuff in
do-game-loop
, I don't think we even need*scene*
orswitch-scene
at all.
Agreed. Actually, we don't need those either if we're doing that in add-scene
and rem-scene
could still be convenient though even if all they do is push/pop the list and call set-up-scene
/tear-down-scene
.do-game-loop
, do we?
Actually, we don't need those either if we're doing that in do-game-loop, do we?
Correct, assuming we use the beginning-of-iteration approach. But add-scene
/rem-scene
would probably be faster (and easier to write) than checking each iteration for changes in a list of *scenes*
. But again, we would only take this approach with a *scene*
or *scenes*
variable.
Instead, I think we go forwith-scenes
to do an initial setup and a catch-all tear down, then allow users to manage additional scenes as they please with set-up-scene
/tear-down-scene
.
Alright, I switched core example 2 to with-scenes
. If you like this, @shelvick, then I'll update all the examples and we can start to merge this.
At first I wasn't sure how I felt about with-scenes
instead of just doing it in the game loop. After seeing the example code I think it makes sense. It's a good separation and still easy enough to handle more scenes within the loop if you want to.
Onward and upward! :shipit:
An attempt at addressing https://github.com/defun-games/claylib/issues/17
We export
game-scene
andswitch-scene
, which uses a new special variable*scene*
to track the current scene. Simplysetf
ing*scene*
and doing the loading/unloading at the beginning of a game loop iteration didn't seem like the best way to switch scenes, that's why there is theswitch-scene
convenience function. It allows you to fully switch scenes mid-loop.As mentioned in 97d107a52549c10b0fc3d89de87ab676aa83c9e5, this is vulnerable to changes in
*SCENE*
whether by nesteddo-game-loop
calls or otherwise.Also this is not addressing potential loads inside a loop. If we don't want to load a scene entirely up-front, then we have to either
set-up-scene
(not exported yet) at some point before we need it (there's potential to do so in a non-blocking way here) so thatswitch-scene
does less work, orswitch-scene
It is probably safe to push the
tear-down-scene
call inswitch-scene
to another thread unless thescene
's%free
is:now
and it really must be done now.Let me know what you're thinking @shelvick. I changed core example 2 to use this new method so you can check that out.