diff options
author | Colin Okay <okay@toyful.space> | 2022-07-24 13:11:12 -0500 |
---|---|---|
committer | Colin Okay <okay@toyful.space> | 2022-07-24 13:11:12 -0500 |
commit | 994f3fc0f093321857a9fb5614f86c5ceba9c352 (patch) | |
tree | 8ee24c2ba32f52222016c0df0bc2878a5fb54217 | |
parent | 294abc20603b1f9c53f1379d9c163f80c1aeaeac (diff) |
[remove] old documentation in the readme
-rw-r--r-- | README.org | 256 |
1 files changed, 0 insertions, 256 deletions
@@ -43,259 +43,3 @@ Then load one of the example files and call its "start" function: #+end_src -** Basic Use - - **The best introduction to wheelwork comes through looking at and playing with the examples.** You may find the examples under =examples/= in the source tree of this repository. The following document supplements the examples with a high level overview of wheelwork, its parts, and how those parts 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. - -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 - -/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 application along with some state needed for the pong game. - -**** The Window & Scale & Coordinates - -Wheelwork uses SDL2 to create windows and generate events. The application includes a global scale factor that affects how the game interprets coordinates inside the window. A window, for example, can be 800x600 pixels on your computer monitor, but if the application's scale factor is 2.0, then it will only have a 400x300 logical space of coordinates. If you add a sprite that is 30x30 pixels big, it will appear twice as large, but it will occupy 30x30 "logical pixels". - -The =0,0= coordinate is the bottom left corner of the game window, and the top right corner is =w,h=, the width and height of the scaled screen, respectively. - - -**** 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~ 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 - -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**. Roughly, units are the things you want to display and containers help to control when and where you to display them. - -Units are added to and removed from containers, and a unit will belong to at most one container at a time. In general, the last thing added to a container will be the last thing rendered - i.e. it will appear to 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. - -The application is itself a container, and is the only unit that does not need to be added to a scene. No unit will be displayed until it becomes part of the display tree rooted at the application. - -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: - -+ ~image~: 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 -+ ~canvas~: display a region of mutable pixels - -*** Events & Event Handling - -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. - -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. - -Here is a simple example where a handler is defined, from the pong game: - -#+begin_src lisp -(ww:defhandler pong-mousemove - (ww:on-mousemotion (app) - (with-slots (paddle) app - (setf (ww:x paddle) (- x (* 0.5 (ww:width paddle))) - (dx paddle) 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 - -: perframe - -See the documentation for the ~on-*~ forms for these events to get a sense of how to handle them. - -**** Defining event handlers - -Event handlers can be defined with ~ww:defhandler~ and ~ww:on-EVENTTYPE~ macros. For example in, - -#+begin_src lisp - -(ww::defhandler thing-clicked - (ww::on-mousedown (target x y) - (format t "~a was clicked at ~a,~a!~%" target x y))) -#+end_src - -The ~on-mousedown~ form creates the event handler, and the ~defhandler~ form assigns it to a name in the function namespace. It has been written this way to allow you to redefine ~thing-clicked~ while your application is running in order to experiment with handlers - i.e. in order to support interactive development. - -Another reason that the ~defhandler~ form is separate from the ~on-EVENTNAME~ forms is that each of the ~on-*~ forms accept different arguments, all optional, depending on the event they are handling. - -For example, ~on-mousedown~ is a macro. The above could have been written - -#+begin_src lisp -(ww::defhandler thing-clicked - ;; lambda list variable names are all optional. - (ww::on-mousedown () - (format t "~a was clicked at ~a,~a!~%" target x y))) -#+end_src - -Or it could have been written - -#+begin_src lisp -(ww::defhandler thing-clicked - ;; lambda list variable names are all optional. - (ww::on-mousedown (my-unit my-x my-y) - (format t "~a was clicked at ~a,~a!~%" my-unit my-x my-y))) -#+end_src - -See the docstrings for each ~on-*~ form for specifics. - -The upshot is, if you are using SLIME, you will get hints about what arguments your handler code expects. - -**** Handling Events - -To get an object (either a unit or the application itself) to handle an event, you add a handler to that object. Handlers know what event they are meant to handle, so you just need to call: - -: (ww:add-handler my-unit my-handler) - -Where ~my-handler~ is either an ~event-handler~ instance. - -Likewise you can drop an event handler - -: (ww:remove-handler my-unit handler-or-event-type) - -If ~handler-or-event-type~ is an event handler instance, it is removed if present. If ~handler-or-event-type~ is symbol whose ~symbol-name~ is the name of an event (e.g. ~:mousedown~ or ~'perframe~), then all handlers of that type are removed from the unit. - - -*** 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 ~image~) 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 - - - - |