A flexible platform for Homestuck Games in HTML5/Javascript. Coded by Andrew Dutcher (http://andrewdutcher.com), primarily for Thoughtstuck (http://andrewdutcher.com/MSPA/)
These instructions represent a version of Jwalker several versions old and very much has changed. Quite a bit is still accurate, so they can be useful, but if you want to get something to work, I'd recommend examining the tests' code, looking at one of their deployments on Thoughtstuck, and just asking me. I'm pretty sure I've littered a few methods of getting at me around.
- In an HTML file, have the following:
- A preloading image with
id="loadimg"- this will be displayed before the JS kicks in and whenever resources are loading - A deployment div:
<div id="jwalker-deploy"></div> - A bit of inline JS setting the following paths, relative to the HTML file:
jwRoot, to the directory the Jwalker sources are kept inrecRoot, to the directory the game assets are kept insrcPath, to the game definition file (more on this later)
- A script tag sourced to
jwalker.jsand its cronies, or justjwalker-min.js
- A preloading image with
- In the game definition file (javascript), set attributes of the
gobject to design your game. More on this later. - If you're trying to run it locally, don't forget to tell your browser to allow file access from the file:/// schema. (
--allow-file-access-from-filesfrom command line in chrome)
All of Jwalker is packed into a single JSON object, called g. In the main jwalker.js, you can see the base for the object, as well as the loading of a number of peripheries and the keyboard/mouse capture. The function g.tick() is called at 60FPS, and that will call everything else, which is defined in pieces throughout the project files.
Assets are handled through and array of resources, containing a filename and a bit of metadata for the asset. (size, type, usage, etc.)
The base level of gameplay in Jwalker is something called an area. An area is a set of functions regarding how to handle gameplay (i.e. a gameplay engine), a list of requred assets, and some state data. When an area is loaded, its requred assets are checked to be sure they are loaded, and if they are not a loading screen comes up, loads them, and processes them if need be.
The base level of interactivity in Jwalker is called a sprite. A sprite is essentially two functions: one if the sprite is currently the player, and one for otherwise. In each area is a list of currently active sprites, each of which is called an instance. An instance contains state data such as position, speed, etc. To process each instance, the code for its defining sprite is run.
g.resources is the list, and eventually all the data too, of all the assets to be used in the game. It takes the format of an array, with each item being a JSON object with the following properties:
filename- Required - the filename of the resource to be loaded, found in the directory defined in the HTML file (for audio assets, leave the extension off)size- Required - the filesize of the asset. Can be in whatever units you like, provided the units are the same for all assets.type- Required - the media type of the asset. Valid values areimage,music, andtext, but may be set tonull(the string, not the constant) if loading fails for some reason.use- Required - the way in which the asset will be used. Valid values vary depending on the value oftype:- for
type:image, valid values arebackground,spritesheet,hitbox,quiltdata(Not implimented), andquiltpatch(Not implimented). - for
type:audio, valid values arebgmandsfx. - for
type:text, currently, the only valid value isdialog. - for
type:null, this should contain some kind of error message about why it failed to load.
- for
extensions- Required for audio files. An array of valid file extensions for the file, in order of preference. Currently, the only supported values aremp3andogg.- For spritesheets, four more values are required:
framexis the number of horizontal pixels in a single frame of the spritesheet.frameyis the number of vertical pixels in a single frame of the spritesheet.framewidthis the number of frames horizontally across the file.frameheightis the number of frames vertically across the file.
offsetis optional, and can be used to offset a hitbox a number of pixels from the origin.g.offset = {x:0, y:200};would make the hitbox extend 200 pixels above the visible area.
The above area all values that should be set in the definition file. Below are a number of values set at runtime:
datacontains the actual loaded data for each resource. For different kinds of assets, it will be a different object type:- for
type:image/hitboxandtype:image/quiltdatait will be an array. - for all other
imagetypes, it will be an Image object. - for all
musictypes, it will be an Audio object. - for all
texttypes, it will be a String.
- for
loadedis a boolean for whether or not the object has been loaded.clientis the XmlHttpRequest object responsible for fetching text assets.getData(x,y)is a function defined in the loading process for typesimage/hitboxandimage/quiltdata. Can be called to retrieve pieces of that image.
g.loading (in loading.js) contains all the functions and data related to the loading of resources. In general, you shouldn't need to touch this, as everything is handled by g.area.
g.loading.activeis a boolean for whether or not the loading process is underway.g.loading.load(list)is the function called with a list of resource IDs to load. It starts the loading process for each of those resources not already loaded and setsg.loading.activeto true.- This section is due for an overhaul and will get one when I have time.
The concept of a timeout in Jwalker is fairly simple. You can set a function to be called after a number of frames elapse, or to be called every frame for a specified number of frames.
To set up a timeout, call g.timeouts.addtimeout(frames, func [, eachframe]).
eachframeis a boolean determining if the function should be called each frame until it expires (true), or if it should only be called when the number of frames elapses (false). Defaults tofalse.funcis the function to be executed. If the function is to be called continuously, this can take one argument, which will be filled with the number of frames remaining.framesis the number of frames the timeout should last. Pretty simple.
For the curious, g.timeouts.list is the list of timeouts, and g.timeouts.process is the function used to process the timeouts. All this is found in timeout.js.
g.gfx contains all drawing related things. It essentially works by maintaining a queue of things to draw, assigned to a "layer". At the end of each frame, everything is "painted" from the bottom up.
g.gfx.draw(redic, x, y, frame, layer [, flip, alpha]) is what you should call if you want to draw something to the screen.
recidis the index of the image resource you would like to draw fromg.resources.xandyare the x and y position of the top-left corner of whatever you would like to draw, relative to the top-left corner of the canvas.framechanges depending on the type of image you are drawing.- for images of type
image/spritesheet,frameis the number of the frame you would like to draw. - for images of type
image/background,frameis a JSON object with the following properties:left: clipping x offset from lefttop: clipping y offset from topwidth: clipping x widthheight: clipping y height
- for images of type
layeris a number representing the layer upon which the image will be drawn. Seeg.gfx.layers.flipis and optional JSON object containing the following properties:x: a boolean for whether or not to horizonally flip the imagey: a boolean for whether or not to vertically flip the image- If
flipis omitted, it will default to {x:false, y:false}
alphais an optional number between 0 and 1 that sets the opacity of the image you are drawing.
g.gfx.drawfunc(func, layer) is for when you need to manually draw to the screen with custom code.
funcis the function to do the drawing. Takes no arguments.layeris a number representing the layer upon which the code will be drawing. Seeg.gfx.layers- In the function, the object
g.crefers to the canvas' drawing context.
g.gfx.paint() unloads the queue onto the screen. Do not call; is called by g.tick().
g.gfx.layers is a reference for drawing onto layers. It is a JSON object with a number of drawing uses assigned to keys. Useful such that if you decide you need an extra drawing layer, you can change the layers definition instead of tweaking a hundred calls to g.gfx.draw().
Common use would be g.gfx.draw(3, 100, 200, 0, g,layers.sprites);.
Default value is {fading: 6, dialog: 5, ui: 4, prioritysprites: 3, prioritybg: 3, sprites: 1, bg: 0}
Dialog information and functions in Jwalker are stored in g.dialog.
Actual data for the dialogs is parsed out of resources with type text/dialog by the function g.dialog.init(recnum) and stored in g.dialog.data. The data format is a bit lengthy to explain here, so I have it documented in the source. Formatting for the dialog text files is explained elsewhere. I haven't written that yet, actually. But it will be explained elsewhere.
To call a dialog, run g.dialog.show(name [, callback]). name is the name of the conversation as named in the dialog text file. callback is an optional function that will be run when the user finishes the dialog -- not closes it with > Close conversation.
You can also make up your own simple dialog by calling g.dialog.notice(text [, callback]). text is either a string or an array of strings to show, and callback is a callback function like the one of g.dialog.show.
g.dialog.prefs is a SETME, so it needs to be set with the setting for your characters, such as their poses, colors, etc.
A couple of additional values that may be important:
g.dialog.activeis true when there is a dialog running, but not during the fly in/out animations of the textbox.g.dialog.numis the name of the currently running dialog.g.dialog.part,g.dialog.state,g.dialog.line, andg.dialog.charall control various granularities of the state of the dialog. You shouldn't be messing with them.g.dialog.boxgoalandg.dialog.boxcurare the target and current x positions of the textbox, used to animate it in conjunction withg.dialog.spriteframetimer.g.dialog.drawsprite,drawtext, anddrawboxare functions that draw their eponymous objects to the screen.
A query, for which all data is stored in g.query, is a little yellow-and-orange popup prompting the user to make a choice.
Pose a query by calling g.query.show(options, x, y [, callback]). options is an array of strings that the user can chose from, x and y are the x and y position of the query box, and callback is a function that will be called when the user makes their choice. It should take one argument, which will be set to the zero-based index of the user's choice.
That's pretty much all there is to it. Code for g.query is stored in dialog.js.
Call g.audio.play(recnum) with the resource identifier of a music asset, and it will be played. If it is background music, it will loop, and if it is a sound effect, it will not. Use a resource number of -1 to stop the current BGM.
Call g.audio.setvol(vol) with a number between 0 and 1 to set the global volume to that level. The current background music volume will be adjusted, but sound effects will continue to play unchecked.
The user interface has a couple of setmes:
g.ui.volrecis the resource number of the spritesheet to use for volume control. Should have four sprites, one for each volume level: 0, 1/3, 2/3, and full.g.ui.optrecis the resource number of the spritesheet to use for the options buttons.g.ui.controlsrecis the resource number of the spritesheet to use for the controls button. Should be a spritesheet that will be animated one frame per frame before the user clicks on it once, and will stop animating and sit on frame 0 once the user clicks it.g.ui.aboutdlgis the dialog name to use for the> Abouttext in the options menu.g.ui.controlsdlgis the dialog name to use for the controls button. The first line of this will be overwritten by the description for the currently selected controls. (see the controls section)
There are two types of input recognized by Jwalker, keyboard input (stored in g.k) and mouse clicks/touches on multitouch devices. (stored in g.p) My school of thought is that there should not be a difference in programming for mobile devices and desktops, so there is no easy way to track mouse motion while not clicked.
All controls are completely flexable and can be defined per-game, using the g.controls.keysets and g.controls.buttons setme.
In Jwalker, there exists a distinction between a "button" and a "key". A key is a key on the keyboard that the user can press, and a button is a designation for a boolean value for whether or not a key is pressed. The two are linked together with something called a "Keyset", or a linkage between raw user input and the buttons as they will be read in-code. To support mobile devices, a keyset can be a "mobile" type and draw buttons and sliders to the screen that can be interacted with and read from to trigger button presses.
Set g.controls.buttons to an array of strings, where each string is the name of the button as it will appear in g.k.
Set keysets to an array filled with all the different keysets that the user should be able to use. The default control set will be the zeroth one. For the format of a keyset, see the formats section.
To read out button-based input, it's okay to read directly from g.k. It will contain a boolean value under the name of each string you set in g.controls.buttons, true if the button is pressed, false if it is not.
However, you should never have to read directly from g.p. Instead, call g.controls.istouch or g.controls.istouchfinger. Both take three arguments, (rect, isframe, finger) only the first of which is requred. rect is the Rectangle that can be interacted with, isframe should be true if you only want to capture taps on their first frame, and finger is the index of the only finger that the function should bother checking. istouch will return true or false depending on whether or not the area is being pressed, while istouchfinger will return the index of the finger it finds touching that spot, -1 otherwise. Both functions will mark each finger as used after they find it, so that one touch cannot be used for two different items in the same frame.
It should be noted that Jwalker treats the mouse exactly like a touch, and will always be in index zero of g.p.
Set g.debug.enabled to true to enable debugging, or just find the option for it in the menu. As of now, that just means that a bunch of staistics and data are drawn to the screen, and you get to measure dimentions onscreen by drawing rectangles.
In case of emergency, you can execute kill() to clear the timeout calling g.tick. To start it back up again, call birth(). It is safe to call either of these functions when they are not applicable.