Page:
Saving Scenes
Pages
2D Drawing
2D Nodes
Code Edits
Code Services
Containers
Control Input
Engines
Exporting
File System Dock
Getting Started
Home
Input and Actions
Lisp Text
Live Pipelines
Painters
Playing a Game
Popups Menus and Dialogs
Popups and Dialogs
Project Manager
Projects
Render Targets
Resources
Saving Scenes
Scene Tree
Script Screen
Sessions
Signals
Status
The Debugger
The Editor
The REPL
Themes
Tweens
Undo and Redo
User Interface
Widgets
No results
3
Saving Scenes
dxtr edited this page 2026-10-20 10:09:08 +02:00
Table of Contents
Saving Scenes
save-scene saves a node and its descendants to a file, and load-scene
loads the file as a packed scene, which instantiate makes new nodes
from, as often as needed: a level, or each bullet a gun fires.
(asdf:load-system :pyssel/scene-2d)
(defpackage #:arena
(:use #:cl)
(:local-nicknames (#:sc #:pyssel/scene) (#:s2d #:pyssel/scene-2d)
(#:res #:pyssel/resource) (#:p2d #:pyssel/2d) (#:gm #:cl-gmath)))
(in-package #:arena)
(res:define-resource stats ()
((health :initarg :health :initform 3 :accessor stats-health
:type (integer 0) :property t)))
(sc:define-node enemy (s2d:node-2d)
((stats :initarg :stats :initform (make-instance 'stats) :accessor enemy-stats
:type stats :property t)
(target :initarg :target :initform nil :accessor enemy-target
:type (or null sc:node) :property t))
(:signals died))
(defmethod s2d:draw ((enemy enemy) canvas)
(p2d:fill-circle canvas 0 0 (* 4 (stats-health (enemy-stats enemy))) '(0.8 0.2 0.2)))
(defun cheer (arena)
(format t "~A lost an enemy.~%" (sc:node-name arena)))
(let* ((arena (make-instance 's2d:node-2d :name "arena"))
(player (sc:add-child arena (make-instance 's2d:node-2d :name "player"
:position (gm:make-dvec2 :x 40 :y 100))))
(brute (make-instance 'stats :health 6)))
(dolist (x '(160 240))
(let ((enemy (sc:add-child arena (make-instance 'enemy :position (gm:make-dvec2 :x x :y 100)
:stats brute :target player))))
(sc:connect enemy 'died arena 'cheer :persist t)))
(sc:save-scene arena "arena.scene"))
(defvar *tree*
(s2d:run (sc:instantiate (sc:load-scene "arena.scene")) :title "Arena" :width 320 :height 200))
Two red circles appear, drawn by nodes made from arena.scene, which
holds:
;;;; A Pyssel scene file. -*- mode: lisp -*-
(:scene
:format 1
:resources ((1 :embedded (arena::stats :health 6)))
:root
((pyssel/scene-2d:node-2d :name "arena")
:children
((pyssel/scene-2d:node-2d :name "player" :position (:vector double-float 40.0d0 100.0d0))
(arena::enemy
:name "enemy"
:position (:vector double-float 160.0d0 100.0d0)
:stats (:resource 1)
:target (:node "../player"))
(arena::enemy
:name "enemy-2"
:position (:vector double-float 240.0d0 100.0d0)
:stats (:resource 1)
:target (:node "../player"))))
:connections (("enemy" arena::died "." arena::cheer) ("enemy-2" arena::died "." arena::cheer)))
- What a scene keeps: each node's class, name, groups, metadata, and
the properties whose values differ from a new node's, so a changed initform
reaches every scene that never set the property; a property that holds
a node as the node's path, one that holds a resource as Resources says; and the
connections made with
:persist t. Connect so where a scene is put together, as an editor does, and not inready, which runs again each time the scene is loaded. - Properties go through
make-instance:instantiatemakes each node by giving it its saved properties as initargs, soinitialize-instancemethods see them, and the property metaclass checks them against their types, as for anymake-instance. Only a property whose value holds a node is set afterwards, once every node is made. Values are made anew for each instance; resources are shared. Saving refuses a value that loading would refuse: one not of its property's type, or one that cannot be saved, such as a function. - Instances: the nodes
instantiatemakes have the root as theirnode-owner, and the root has the scene as itsnode-scene. A scene that holds an instance of another refers to that scene's file and keeps only what was changed in the instance's nodes, and added to them, since; loading applies the changes to a new instance. So a change toenemy.scenereaches every level that holds enemies.(node-instance-root node root)says which instance belowroot, the root of the scene meant, made a node, ornilfor the scene's own: the editor's Scene dock shows a scene's own nodes and hides what its instances made, as Godot's does.(node-defaults node root)returns a new node whose properties are the defaults saving compares a node's with: a new object of its class, or for an instance, the same node of a new instance of its scene; the editor's inspector reverts to them. - Copies:
(duplicate-node node)copies a node and what is saved under it, as Godot'sNode.duplicatedoes, through the same packing as a scene: properties, groups, metadata and the connections among the nodes copied, an instance staying an instance of its scene, with what changed in it. A property that holds a node outside the copy cannot be kept, and is refused. - Files are S-expressions, read without evaluation and written the
same way each time, so they diff well. A file names the classes it makes
and the functions its signals call, so it is code: load only files you
trust, after the systems that define their classes. A property a class
no longer has is dropped with a
property-droppedwarning, and a change to a node that an instanced scene no longer has withscene-data-dropped. - Threads:
load-sceneandinstantiatemay run on any thread, such as a job-pool worker's, since the nodes they make are in no tree: add them to one withcall-in-tree.pack-sceneandsave-scenerun on the tree's thread.
Getting started
The core
Drawing
Scenes
User interface
Games and the editor
- Playing a Game
- Projects
- The Editor
- The File System Dock
- The Script Screen
- The REPL
- The Debugger
- Lisp Text
- Code Services
- Project Manager
- Undo and Redo
- Exporting
The project