Skip to content

Latest commit

 

History

History
377 lines (271 loc) · 13.9 KB

doc-preamble.org

File metadata and controls

377 lines (271 loc) · 13.9 KB

XE2 Developer’s Guide and Reference

Introducing XE2

file:../images/xe2.png

XE2 is a portable free-software graphical 2D game engine written in Common Lisp.

The engine supports layered, tile-based 2D worlds with a limited z-axis and interacting, independent agents called “cells”. These cells are programmed with the help of a library called CLON (Common Lisp Object Network) which adds prototype-oriented objects to Common Lisp with a custom syntax. Sprites are also supported, and these may collide with each other and with grid cell objects. Both turn-based and realtime play are supported. XE2 uses LISPBUILDER-SDL for cross-platform graphics and audio; games built with XE2 can be distributed as binaries for GNU/Linux, Mac OSX, and Microsoft Windows. (For an example, see XONG.)

Other features include:

  • Basic random terrain generation tools
  • Simple AI support
  • GUI widgets with flexible keybindings
  • Mouse and joystick support
  • Ray-casting light and shadow effects
  • Bresenham’s line-of-sight

Features in the process of being ported from the 1.0 codebase include A-star pathfinding, rule-based map generation, a map editor, and a menu system.

Setting up your development environment

First you need an operating system. Because XE2 runs on Linux, Mac, and Windows, you can develop games on any of those platforms. (All the development tools and libraries mentioned below are cross-platform).

  • GNU Emacs is strongly recommended for developing XE2 games, because of CLON’s special syntax requirements (see below) and also because SLIME allows you to develop and debug interactively from within the editor.
  • SLIME (Superior Lisp Interaction Mode for Emacs)
  • clon.el — a short Emacs Lisp program that adds support for CLON syntax editing to Emacs
  • SBCL (Steel Bank Common Lisp) is recommended.
  • LISPBUILDER-SDL and its prerequisites are required.
  • SDL, SDL-IMAGE, SDL-MIXER, SDL-GFX are required .
  • GIMP, Audacity, Ardour, and so on can be used to edit audio and image files used as game resources.

Visit the repository to see the latest installation instructions for getting started with LISPBUILDER-SDL, CLON, and XE2. Installing GNU Emacs, SLIME, or the other programs mentioned above is beyond the scope of this document, but most of them will have downloads available on their websites.

Design overview

In this section we take a brief tour of the main areas of functionality from a design perspective. After that, we will delve into API details from a programmer’s point of view.

CLON

CLON is an object system designed especially for games. It uses prototypes instead of classes, and has built-in support for serialization. Messages (i.e. method invocations) can be queued and pre-processed before sending to their recipients. Message forwarding (i.e. doesNotUnderstand) is also supported.

See also clon.lisp.

The console

The “console” is an imaginary video game machine whose native language is Common Lisp. XE2 games are implemented as “modules” that plug in to the system, akin to old cartridges or tapes. All services of the engine (opening the screen, drawing text and images, playing sounds, joystick input) are provided here with a platform-neutral Common Lisp interface. Currently the console uses LISPBUILDER-SDL as a backend, but other backends are possible.

See also console.lisp.

Interactive graphical widgets

Widgets are CLON graphical user interface objects. The console is designed to draw a set of “active widgets” to the screen for each video frame. The console also delivers event data to these widgets. Events are things like keystrokes, joystick buttons, timers, or mouse clicks. The keybinding system (where events are mapped to responses) draws inspiration from Emacs.

Also included in widgets.lisp:

  • basic layout widgets
  • an interactive command prompt
  • an Emacs-like formatter with fontification and inline images
  • scrolling text box widget
  • a “pager” to switch between different active widget layouts using hotkeys.

Cells

“Cells” are interacting CLON objects. Each cell represents some in-game entity; player characters, enemies, weapons, items, walls and floors are all different types of cells. Game play occurs in a three-dimensional grid of cells called a World (see below).

Cells may be stacked along the z-axis, and may also contain other cells. Cells interact by sending messages to one another and to other objects in the environment; these messages are queued and processed by the world for delivery to their recipients.

In cells.lisp you will find some basic roguelike logic built into cells.

  • Basic features like name, description, and discovery.
  • Unified container, inventory, and equipment system.
  • Cells have an optional weight in kilograms, and the calculation recursively includes containers and equipment.
  • The “action points” system allocates game turns to different cells.
  • Basic melee and ranged combat support.
  • Equipment slot system (i.e. “paper doll”) not restricted to humanoid actors.
  • “Proxying”, a feature used to implement drivable vehicles and/or demonic possession.
  • “Stats”, for numeric-valued attributes susceptible to temporary and permanent effects (i.e. stat increases and drains, or encumbrance). Also supports setting minimum and maximum values, and keeping track of units (meters, kilograms.)
  • “Categories” allow arbitrary tagging of objects, with some categories having special interpretation by the engine.

These are in effect a basic set of roleplaying rules or “physics”. By defining new prototypes based on cells, you can change the rules and run the game the way you want.

Worlds composed of cells

A World object ties together all the elements of XE2 into a playable situation. A World is a 2.5D grid of interacting cells. This object performs the following tasks:

  • Keeps track of a single player and delivers command messages to the player cell
  • Time and turns for player and CPU (the “Action Points system”)
  • Lighting and sound propagation
  • Generating the map and placing cells on maps.
  • Queueing and processing messages

There are also Universe objects composed of interlinked worlds.

See also worlds.lisp.

Math routines

  • Basic dice rolls
  • Distance, compass directions
  • Drawing shapes made of cells
  • Bresenham’s line algorithm
  • Random midpoint displacement “plasma”

See also math.lisp.

Pathfinding

Not yet fully ported. See path.lisp.

Introducing CLON

CLON stands for Common Lisp Object Network. CLON is a prototype-based object system for Common Lisp. It is different from CLOS in several important ways:

  • CLON is prototype-based, not class-based. A prototype is a template object from which other objects are “cloned”.
  • Method invocation happens via message-passing, not generic functions; messages are conceptually different from synchronous function calls and may be freely queued, forwarded, and filtered.
  • Built-in support for serialization.
  • Simple and small: as of December 2008, clon.lisp contains about 750 lines of code and commentary.
  • Special syntax support for message sending:
  [method-name object arg1 arg2 ...]

and for accessing fields (i.e. “slots” in CLOS terminology):

  (setf <slot-name> value)

clon.el: Emacs editing support for CLON

CLON includes a small Emacs Lisp program that adds optional support for CLON syntax, complete with fontification.

To set up clon.el, add the following to your Emacs initialization file:

(add-to-list 'load-path "~/clon") ;; Change this to where you installed CLON
(require 'clon)
(add-hook 'lisp-mode-hook #'clon-do-font-lock)

Code examples

What is an object in CLON?

An object in CLON consists of a set of fields (keyword/value pairs), and optionally:

  • a name (a symbol)
  • a link to a parent object from which this object delegates slot lookups

See also clon.lisp, “Object data structure”

Defclass-like prototype definitions

First we must define a prototype and name its fields:

(define-prototype rectangle ()
  x y width height)

See also clon.lisp, “Defining prototypes”

We could also have provided initialization forms for the slots, and documentation strings:

(define-prototype rectangle ()
  (x :initform 0 
     :documentation "The x-coordinate of the rectangle's top-left corner.")
  (y :initform 0 
     :documentation "The y-coordinate of the rectangle's top-left corner.")
  (width :documentation "The width of the rectangle.")
  (height :documentation "The height of the rectangle."))

Single inheritance

And if there was a “shape” prototype, from which we would like “rectangle” to inherit data and methods, we might have written:

(define-prototype rectangle (:parent =shape=)
  (x :initform 0 
     :documentation "The x-coordinate of the rectangle's top-left corner.")
  (y :initform 0 
     :documentation "The y-coordinate of the rectangle's top-left corner.")
  (width :documentation "The width of the rectangle.")
  (height :documentation "The height of the rectangle."))

Notice the equals signs surrounding the parent object’s name; all objects made with define-prototype are accessible via special variables with such names.

The reason for this is that usually you want to call a widget a widget, but if that name is taken for a special variable “widget” whose value was the prototype for all widgets, then you will have to use some other probably less effective name for the binding, like “w” or “wt” or “wydget”, everywhere you want to just talk about a “widget” in your code. So instead we only reserve the equals-sign-delimited name:

 =WIDGET=

Cloning objects

The function CLON:CLONE is used to create new objects from these prototypes. Now we write an initializer, which is passed any creation arguments at the time of cloning.

(define-method initialize rectangle (&key width height)
  (setf <width> width)
  (setf <height> height))

See also clon.lisp, “Cloning objects”.

Notice how field accesses can be written with the angle brackets; this works both for reading and for writing, so long as you use “setf” for the latter.

See also “Field reference syntax”.

Now when you say:

(setf rectangle (clone =rectangle= :width 5 :height 12))

The rectangle’s initializer method is invoked with those arguments, and a rectangle of the correct height and width is created.

Basic field access

(field-value :width rectangle)
(setf (field-value :height rectangle) 7)

See also clon.lisp, “Fields”

Methods

Now we define a few methods:

(define-method area rectangle ()
  (* <width> <height>))

(define-method print rectangle (&optional (stream t))
  (format stream "height: ~A width: ~A area: ~A"
	  <height> <width> 
	  [area self]))

See also clon.lisp, “Methods and messages”

And invoke them with the aforementioned square bracket notation.

(defvar rect (clone =rectangle= :width 10 :height 8))

:

[print rect]

The result:

"height: 8 width: 10 area: 80"

Message queueing

CLON also supports a concept called message queueing. When there is an active message queue, messages may be entered into the queue instead of directly invoking a method:

[queue>>render widget]
[queue>>attack self :north]

The sender, receiver, method name, and arguments are all recorded in the queue. The developer can then filter or process them before sending.

See also clon.lisp, “Message queueing”

Message forwarding

And finally, I will mention message forwarding, which handles the case that an object has no handler for a particular method. This is akin to Smalltalk’s “doesNotUnderstand” concept.

See also clon.lisp, “Message forwarding”

A simple example

Before we move on to the reference dictionary of the objects, functions, and variables of XE2, here’s an example game to peek at: example.lisp

You can try it by running (xe2:play “example”) at the SLIME REPL.

There’s more to making an XE2 module than just a Lisp file; you must have resource files (.png, .ogg) and a resource index (.pak file).

See http://github.com/dto/xe2/tree/master/example/ for the full list of files in the example game.

Support links

If you have questions about XE2 or have problems, feel free to use the following lines of support:

Symbol dictionary

The remainder of this reference lists documentation for all the exported symbols, in the following order:

  • Prototypes
    • Fields
    • Methods, in alphabetical order
  • Special variables, in alphabetical order
  • Macros, functions, and variables, in alphabetical order