aboutsummaryrefslogtreecommitdiffhomepage
path: root/README.org
diff options
context:
space:
mode:
authorColin Okay <colin@cicadas.surf>2022-07-08 12:13:16 -0500
committerColin Okay <colin@cicadas.surf>2022-07-08 12:13:16 -0500
commit1cba62c355602528890ecf8f669f5362e4c4d9a7 (patch)
treed953fc00289048f61ea396b147c234dd548c0f0b /README.org
parent8bf0e572ee084e6659f56b2fecd44a54e2775b6c (diff)
[add] [doc] README; [refactor] rendering of containers
Diffstat (limited to 'README.org')
-rw-r--r--README.org196
1 files changed, 174 insertions, 22 deletions
diff --git a/README.org b/README.org
index e55d9d1..0b3065b 100644
--- a/README.org
+++ b/README.org
@@ -7,7 +7,7 @@
/A Sprite System in Common Lisp for Games and GUIs/
** Installation
-
+
Ensure that sdl2 is installed on your system.
*** Fetch Systems That Are Not In Quicklisp
@@ -41,52 +41,204 @@ Then load one of the example files and call its "start" function:
#+end_src
-** TODO Basic Use
+** Basic Use
+
+ The best introduction is to look at and play with the examples.
+ What follows is a cursory explanation of how pieces are meant to
+ fit together.
+
*** The Application
+
+When you want to use Wheelwork, you must make a sublcass of ~application~. The ~application~ is the main container supplied by wheelwork. It is the root of the [[*The Display Tree Protocol: Units and Containers][display tree]], holds references to [[*The Asset Protocol][loaded assets]], and handles [[*Events & Event Handling][events]] from the user.
- The best introduction is to look at and play with the examples.
- What follows is a cursory explanation of how pieces are meant to
- fit together.
+For example, from the pong example in =examples/08-pong.lisp=, the application class looks like
+
+#+begin_src lisp
+(defclass/std solo-pong (ww:application)
+ ((paddle ball game-over intro-text)))
+#+end_src
-**** Your Application
+/note: I'm using defclass-std:defclass/std to define the above, see [[https://quickdocs.org/defclass-std][here]] for more./
+This defines a subclass of appliation along with some state needed for the pong game.
+
**** The Boot Method
+It isn't enough to define a subclass, you must also implement a ~ww:boot~ method for your application class. The boot method is called right after the opengl context becomes available. Inside boot, you are expected to do everything necessary to start your game: load assets, create some display units, add them to the scene, and add event handlers. Here is what the boot method looks like for the pong game.
+
+#+begin_src lisp
+
+(defmethod ww::boot ((app solo-pong))
+ "Adds the intro text and sets up the start button handler."
+ (sdl2:hide-cursor)
+ (let ((intro-text
+ (make-instance
+ 'ww:text
+ :content "Press any key to start"
+ :font (ww::get-asset "Ticketing.ttf")
+ :x 160
+ :y 300
+ :scale-x 3.0
+ :scale-y 3.0)))
+ (setf (intro-text app) intro-text)
+ (ww:add-unit app intro-text)) ; add to scene
+ (ww:add-handler app #'press-to-start))
+
+#+end_src
+
+You can see that it does very little. It first creates an instance of [[*Text][text]], with the appropriate scale and starting position. Then it just sets the app's ~intro-text~ slot to the newly created text before adding to the scene.
+
+It ends by adding a handler to the app called ~press-to-start~, where presumably, the rest of the game is set up.
+
**** The Shutdown Method
-*** Display Units
+The shutdown method is optional and is called right before the application exits. If supplied, it is a good place to do things like: make save files, close network connections, clean up foreign memory resources that are not already managed by the [[*The Asset Protocol][asset protocol]].
+
+
+*** The Display Tree Protocol: Units and Containers
+
+Objects that render to the screen are organized into a display tree. There are two basic kinds of objects: units and containers. Units are, roughly, "what you want to display" and containers are "when and where you want to display it". Additionally, containers hold a list of units.
-**** Bitmaps
+In general, the last thing added to a container will be the last thing rendered - i.e. it will be "on top". Containers are themselves units, so they too can be added to other containers. Nesting of containers allows you to render a one set of units before or after some other set of units.
-**** Text
+The application is a container, and is the only unit that does not need to be added to a scene. Nothing else will display until it becomes part of the display tree rooted at the application.
-**** Sprites
+Containers have "bounds", which are screen coordinates for the left, right, top, and bottom of the region inside of which units will be displayed. If a unit moves out of bounds, it will not show up on the screen, and will not receive mouse events. (It may, however, still be focused - and hence receive key events.) The bounds of the application are the visible window itself. Other containers may have custom bounds.
+
+See =examples/07-scrollarea.lisp= for an example that uses a container.
**** Containers
+The main thing you can do with containers is add and remove units:
+
+: (add-unit app my-unit)
+: (drop-unit my-unit) ;; it knows its container
+
+You can also adjust their bounds, for example:
+
+: (setf (container-top my-container) 100)
+
+**** The Affine Units
+
+Most units of any interest implement the "affine protocol". I.e they have orientation and scale in the 2d plane of the game window.
+
+More specifically, you can use the following accessor functions on them:
+
+ : x
+ : y
+ : width
+ : height
+ : scale-x
+ : scale-y
+ : rotation
+
+There are a few convenience functions also defined that use the above functions under-the-hood. I'm not including them here because the API is still stabilizing.
+
+The affine units are things like:
+
++ ~bitmap~: display an image that has been loaded from a file asset (currently only png is supported)
++ ~text~: display text
++ ~frameset~: display an animated sequence of images
++ ~sprite~: display a "bundle" of framesets
+
*** Events & Event Handling
-**** User Input Events vs Psuedo-Events
+Anything that "happens" in a wheelwork application happens through the course of handling some event.
+
+Wheelwork defines an ~event-handler~ class that is used to to create functions for handling events. Instances of ~event-handler~ are funcallable objects with a slot that specifies the kind of event being handled.
+
+Event handlers are added to instances of ~interactive~, which includes most kinds of units and the application itself. Notably, the base ~container~ class is not a subclass of ~interactive~ and so cannot handle events.
-**** Event Handler Macros
+Wheelwork provides a macro called ~defhandler~ that can be used easily create instances of ~event-handler~ and bind them to a name. Most event handlers are created using a macro that looks like ~on-EVENTNAME~, which are discussed below.
-**** Focus Events
+Here is a simple example where a handler is defined, from the pong game:
-**** Perframe Events
+#+begin_src lisp
+(ww:defhandler pong-mousemove
+ (ww:on-mousemotion (app)
+ (setf (ww:x (paddle app))
+ (- x (* 0.5 (ww:width (paddle app))))
+ (dx (paddle app)) xrel)))
+#+end_src
+
+See the example for details.
+
+There are two kinds of events: User Interaction Events and Psuedoevents.
+
+**** User Interaction Events
+
+User Interaction events are, curiously enough, generated by user interaction. These include
+
+: keydown
+: keyup
+: mousewheel
+: mousedown
+: mouseup
+: mousemotion
+
+The first three will fire on whichever object has focus. The last three will fire on the first visible object that intersects with the cursor.
+
+**** Psuedoevents
+
+Psuedoevents are generated by the wheelwork itself, and include the following:
+
+Display tree events:
+
+: after-added
+: before-added
+: before-dropped
+
+Focus events
+
+: focus
+: blur
+
+Frame events
-**** Display Tree Events
+: perframe
-**** Mouse Events
+See the documentation for the ~on-*~ forms for these events to get a sense of how to handle them.
-**** Key Events
-*** Extending Wheelwork
-**** The Display Tree Protocol
-
-**** The Asset Protocol
+*** The Asset Protocol
+
+Assets are resources loaded from disk. The application's ~asset-classifiers~ list associates file extensions (like "ttf", and "png") with classes (like ~font~ and ~png~) that load and prepare assets for use in an application.
+
+Every asset has a "key", which is just a string path name that is relative to the application's ~asset-root~. These keys are used by ~get-asset~ to fetch assets, possibly loading them for the first time if they have not been previously fetched.
+
+Some classes (like ~text~ or ~bitmap~) require an instance of an asset class to fill one of their instance slots (like ~font~ or ~texture~) in order to work properly.
+
+E.g. In ~examples/03-font-render.lisp~ you see
+
+#+begin_src lisp
+
+(make-instance
+ 'ww::text
+ :content (format nil "Hell!~%Oh World...")
+ :font (ww::get-asset "Ticketing.ttf"
+ :asset-args '(:oversample 2)))
+#+end_src
+
+which fetches the ~"Ticketing.ttf"~ font asset for use in rendering the text content.
+
+The asset root relative to which ~"Ticketing.ttf"~ is resolved can be set during instantiation of the application.
+
+e.g., for the same example:
+
+#+begin_src lisp
+(make-instance
+ 'font-display
+ :fps 60
+ :refocus-on-mousedown-p nil
+ :width 800
+ :height 600
+ :title "Wheelwork Example: Font display"
+ :asset-root (merge-pathnames
+ "examples/"
+ (asdf:system-source-directory :wheelwork)))
+#+end_src
-**** The Affine Protocol