Documentation overhaul
This commit is contained in:
parent
5f33ac53b7
commit
11b369b368
6
Makefile
6
Makefile
|
@ -189,6 +189,7 @@ DIST = yugine-$(OS)$(ARCH)$(INFO)-$(COM)$(ZIP)
|
|||
DISTDIR = ./dist
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
primum: all
|
||||
all: $(BIN)/$(NAME)
|
||||
cp $(BIN)/$(NAME) .
|
||||
|
||||
|
@ -327,8 +328,9 @@ clean:
|
|||
@rm -f shaders/*.sglsl.h shaders/*.metal core.cdb jso cdb packer scripts/*.jso TAGS
|
||||
@make -C quickjs clean
|
||||
|
||||
docs:
|
||||
mkdocs build
|
||||
docs: doc/prosperon.org
|
||||
make -C doc
|
||||
mv doc/html .
|
||||
|
||||
TAGINC != find . -name "*.[chj]"
|
||||
tags: $(TAGINC)
|
||||
|
|
5
doc/Makefile
Normal file
5
doc/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
html: prosperon.org
|
||||
emacs prosperon.org -f org-html-export-to-html --kill
|
||||
mv prosperon.html index.html
|
||||
mkdir html
|
||||
cp index.html orb.gif dos.ttf org-info.js style.css prosperon_orb_horizontal.gif html
|
|
@ -1,271 +1,446 @@
|
|||
#+title: Prosperon Documentation
|
||||
#+author: John Alanbrook
|
||||
#+options: html-postamble:nil
|
||||
#+DESCRIPTION: Prosperon documentation
|
||||
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style.css" />
|
||||
#+HTML_HEAD: <script defer data-domain="prosperon.dev" data-api="https://net.pockle.world/net/event" src="https://net.pockle.world/bat/script.js"></script>
|
||||
#+HTML_HEAD: <link rel="icon" href="orb.gif" type="image/gif">
|
||||
#+INFOJS_OPT: view:showall ltoc:above path:org-info.js toc:nil
|
||||
|
||||
@@html:
|
||||
<script src="https://kit.fontawesome.com/a87f68ad0a.js" crossorigin="anonymous"></script>
|
||||
<div class="floathead">
|
||||
<nav class="floathead">
|
||||
<a href="https://prosperon.dev">
|
||||
<img height=50px src="prosperon_orb_horizontal.gif">
|
||||
</a>
|
||||
<a href="#top"><i class="fa-solid fa-table-list"></i></a>
|
||||
<a href="https://github.com/johnalanbrook/prosperon"><i class="fa-brands fa-github"></i></a>
|
||||
</div>
|
||||
<a href=#top><i class="fa-solid fa-bars"></i></a>
|
||||
<a href="https://github.com/johnalanbrook/prosperon"><i class="fa-brands fa-github"></i></a>
|
||||
<a href="https://x.com/pockleworld"><i class="fa-brands fa-x-twitter"></i></a>
|
||||
</nav>
|
||||
@@
|
||||
|
||||
* Getting Started
|
||||
|
||||
** Installation
|
||||
#+begin_scholium
|
||||
Press 'i' to see the table of contents.
|
||||
Press '?' for shortcuts on navigating this document!
|
||||
#+end_scholium
|
||||
|
||||
#+begin_src
|
||||
Window.name = "Prosperon (V0.1)";
|
||||
Window.width = 1280;
|
||||
Window.height = 720;
|
||||
var Asset = {};
|
||||
Asset.doc = {
|
||||
doc: "Functions to manage the loading and unloading of assets, like sounds and images."
|
||||
};
|
||||
#+end_src
|
||||
** Installation
|
||||
*** Linux & MacOS
|
||||
Install the most [[https://prosperon.dev/download][recent binaries]] into your ~$PATH~.
|
||||
|
||||
*** Windows
|
||||
Copy the executable into any folder and run it. If no game is deteced, it scaffolds for you.
|
||||
|
||||
** Playing your first game
|
||||
Download any of the completed example projects. Run ~prosperon play~ in the folder.
|
||||
|
||||
Poke around the example projects. You will find it refreshingly straight forward.
|
||||
|
||||
* Engine Tour
|
||||
Prosperon is built in a code-first fashion.
|
||||
|
||||
** QuickJS (Javascript)
|
||||
The scripting language used in Prosperon is Javascript, with QuickJS. It is [[https://tc39.es/ecma262/2023/][ES2023]] compliant. It is fast, and has a number of features that make it well suited for a video game engine, like ref counting. Read more about it [[https://bellard.org/quickjs/][here]].
|
||||
|
||||
#+begin_scholium
|
||||
Javascript is used here mostly to set up objects and tear them down. Although computationally expensive tasks can be done using QuickJS, Prosperon makes it easy to extend with raw C.
|
||||
#+end_scholium
|
||||
|
||||
** Script entry points
|
||||
The first way you can customize Prosperon is by adding scripts to the folder you're running it from. Any file ending with *.js* is a *script* which can be ran by Prosperon.
|
||||
|
||||
| script | When called |
|
||||
|-----------------+---------------------------------------------|
|
||||
| config.js | First script on Prosperon load |
|
||||
| game.js | Entry point for running the game |
|
||||
| editorconfig.js | Entry point for running the editor |
|
||||
| predbg.js | Called before running debug |
|
||||
| debug.js | Debug set up |
|
||||
| dbgret.js | After stopping debug mode, used for cleanup |
|
||||
|
||||
#+begin_scholium
|
||||
Try it out. Add a script called ~config.js~ to your folder, with this.
|
||||
|
||||
#+begin_src
|
||||
console.log("Hello world!");
|
||||
Game.quit();
|
||||
#+end_src
|
||||
Run ~prosperon~. You will see "Hello world!" in the console, and it shuts down.
|
||||
#+end_scholium
|
||||
|
||||
Using ~config.js~ and ~game.js~, you can write an entire game, without reaching any further. When you want to populate a world with independent actors, entities are what you will reach for.
|
||||
|
||||
** Actors
|
||||
The fundamental tool for building in Prosperon is the actor system. Actors run independently from each other. Actors are defined by a combination of code and data. All actors have a *master*[fn::I am aware of the ongoing controversy surrounding the use of this term. That is precisely why I have used it: there are a plethora of similar relationships in video games, and by using a term most teams would rather not used, I have saved left the field of avaialable ones wide open] which controls certain properties of the actor.
|
||||
|
||||
The most masterful actor is the *Empyrean*. The first actor you create will have the Empyrean as its master. Subsequent actors can use any other actor as its master.
|
||||
|
||||
*** Actor Lifetime
|
||||
When an actor dies, all of the actors that have it as their master[fn::What a mouthful!] will die as well.
|
||||
|
||||
*** Turns
|
||||
Actors get fragments of time called a *turn*. Actors which belong to different systems can have different lengths of turns.
|
||||
|
||||
*** Actor files
|
||||
Actor files end with the extension *.jso*[fn::"Javascript object".]. They list a series of functions to call on a newly formed actor. Actors have a number of useful functions which are called as defined.
|
||||
|
||||
| function | call time |
|
||||
|----------+----------------------------------------------------------|
|
||||
| start | The first function called when the actor is in the world |
|
||||
| update | Called once per turn |
|
||||
| gui | Called on GUI draw |
|
||||
| stop | Called when the actor is killed |
|
||||
| gizmo | Called by the editor when the entity is selected |
|
||||
|
||||
#+begin_scholium
|
||||
Create a new actor, then kill it.
|
||||
#+begin_src
|
||||
var act_die_call = function() {
|
||||
console.log(`Actor ${this.id} has died.`);
|
||||
}
|
||||
var act1 = Empyrean.new();
|
||||
var act2 = actor1.new();
|
||||
act1.stop = act_die_call;
|
||||
act2.stop = act_die_call;
|
||||
Empyrean.kill(); /* Error: The Empyrean cannot be killed */
|
||||
act1.kill();
|
||||
act2.kill(); /* Error: act2 has been killed because act1 was */
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
#+begin_scholium
|
||||
Now simplify by putting the code into a file named *hello.jso*.
|
||||
#+begin_src
|
||||
this.stop = function() {
|
||||
console.log(`Actor ${this.id} has died.`);
|
||||
}
|
||||
#+end_src
|
||||
Now spawn two actors using it.
|
||||
#+begin_src
|
||||
var act1 = Empyrean.new("hello.jso");
|
||||
var act2 = act1.new("hello.jso");
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
*** Actor configuration
|
||||
Actors can be created using an optional configuration file. A configuration file is one of any accepted data types. Currently, JSON or [[https://www.crockford.com/nota.html][Nota]]. Configuration files are loaded after an actor's script file, overwriting any defined values on it.
|
||||
|
||||
#+begin_scholium
|
||||
Add a name for the actor to take on using a configuration file named *hello.json*.
|
||||
#+begin_src
|
||||
{
|
||||
"name": "Actor 1"
|
||||
}
|
||||
#+end_src
|
||||
Now create *hello.jso* to use it.
|
||||
#+begin_src
|
||||
this.start = function() { console.log(`I, ${this.name}, have been created.`); }
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
** Entities
|
||||
Games are made of entities. Entities are made of components. Components can be thought of as properties that entities poses; entities are a collection of components. Components include things like drawables (textures, sprites), physical colliders, and more.
|
||||
|
||||
Entities can have control of other entities, in which case the one in control is the 'master' and the controlee the 'padawan'. When a master moves or rotates, all padawans move and rotate with it.
|
||||
Game worlds are made of entities. Entities are a type of actor with a number of useful properties. Entities can only be created on the actor named *Primum*[fn::See the Primum Mobile]. The Primum is the outermost actor with a physical space. While Actors are more abstract, Entities exist in a definite space, with a position, rotation, and so on. Entities can respond to physics and play sounds. Anything which can be thought of as having a position in space should be an entitiy.
|
||||
|
||||
#+begin_scholium
|
||||
The first and most masterful entity is the Primum. The Primum has no components, and its rotation and position are zero. It defines the center of the game.
|
||||
#+end_scholium
|
||||
|
||||
*** Scripting
|
||||
*** Adding Components
|
||||
Entities can have *components*. Components are essentially javascript wrappers over C code into the engine. Scripting is done to set the components up on entities, after which most of the work is done by the C plugin.
|
||||
|
||||
There are a number of script hooks which are ran at particular times of engine loading. When a game starts ...
|
||||
- config.js
|
||||
- game.js
|
||||
#+begin_scholium
|
||||
For example, to render an image, set up a *sprite* component on an entity and point its path to an image on your harddrive.
|
||||
#+begin_src
|
||||
var ent = Empyrean.new();
|
||||
var spr = ent.add_component(component.sprite);
|
||||
spr.path = "image.png";
|
||||
#+end_src
|
||||
Put that into your config file and run ~prosperon~. You should see the contents of "image.png" on the screen.
|
||||
|
||||
When the editor starts ...
|
||||
- editorconfig.js
|
||||
Try using an animated gif. Prosperon has native support for gif animations!
|
||||
#+end_scholium
|
||||
|
||||
F5 can be pressed in the editor to test the game. In that case ...
|
||||
- config.js
|
||||
- debug.js
|
||||
Components only work in the context of an entity. They have no meaning outside of a physical object in the world. They have no inherent scripting capabilities.
|
||||
|
||||
And when play mode is left ...
|
||||
- dbgret.js
|
||||
While components can be added via scripting, it is easier to add them via the editor, as we will later see.
|
||||
|
||||
*** Ur-types
|
||||
Ur-types are what are loaded into the game world. They are templates for creating entities. Ur-types are defined by providing the engine with .jso files.
|
||||
*** Ur system
|
||||
The ur[fn::A German prefix meaning primitive, original, or earliest.] system is a prototypical inheritence system used by the actor files. When actor files are loaded, they are stored as an ur. Entities can be created from ur types using the *spawn* function.
|
||||
|
||||
If you have a 'enemy.jso' file, then on game start you can spawn an enemy via 'Primum.spawn(ur.enemy)'. The 'ur' object holds all ur-types the game knows about.
|
||||
#+begin_scholium
|
||||
Create an ur from the *hello* files above, and then spawn it.
|
||||
#+begin_src
|
||||
ur.create("hello", "hello.jso", "hello.json");
|
||||
Primum.spawn(ur.hello);
|
||||
#+end_src
|
||||
When creating an actor from source files, all of its setup must take place. In this example, the setup happens during *ur.create*, and spawning is simply a matter of prototyping it.
|
||||
#+end_scholium
|
||||
|
||||
Ur-types are loaded on demand, or can be preloaded with 'prototypes.generate_ur()'.
|
||||
Each ur type has some useful fields.
|
||||
|
||||
*** The ECS system, revisited
|
||||
There are two distinct items in the Primum: the Entity, and the Component. Components give qualities to Entities. An Entity is any real, tangible thing in the universe, and so every entity has a position. Components do not necessarily have a position; they can be things like the image that draws where the entity is located, and colliders that allow the entity to respond with the world.
|
||||
| field | description |
|
||||
|-----------+----------------------------------|
|
||||
| instances | An array of instances of this ur |
|
||||
|
||||
*** Components
|
||||
The most "bare metal" are the components. These are essentially hooks into the engine that tell it how to do particular things. For example, to render a sprite, Javascript does no rendering, but rather tells the engine to create an image and render it in a particular spot. Javascript does the accounting to make or destroy the sprite as needed - but besides that initial startup, no scripting is done.
|
||||
*** Prototyping Entities
|
||||
Ur types are the prototype of created entities. This makes it trivial to change huge swathes of the game, or make tiny adjustments to single objects, in a natural and intuitive way. When a value is changed on an entity, it is private. When a value is changed on an ur, it propogates to all entities. Values cannot be added or removed in subtypes.
|
||||
|
||||
Components are rendered in an "ECS" style. To work, components must be installed on an entity. They have no meaning outside of a physical object in the world.
|
||||
Entities all have the following functions to assist with this:
|
||||
|
||||
AI would be components. You could have a "sensor" AI component that detects the world around it, and a locomotion AI component, etc, and reserve scripting for hooking them up, etc. Or do it all in scripting.
|
||||
| function | use |
|
||||
|---------------+---------------------------------------------|
|
||||
| clone(parent) | Create an entity prototyped from the parent |
|
||||
| dup(parent) | Create an exact duplicate of the parent |
|
||||
| revert() | Removes all local changes on the entity |
|
||||
|
||||
Components cannot be scripted; they are essentially a hardwired thing that you set different flags and values on, and then can query it for information.
|
||||
Speaking of practical experience, is best for ur prototype chains to be shallow.
|
||||
|
||||
*** Prototyping model
|
||||
All objects follow the prototyping model of inheritence. This makes it trivial to change huge swathes of the game, or make tiny adjustments to single objects, in a natural and intuitive way.
|
||||
*** Spawning
|
||||
Actor data and ur types can remember which entities were contained in it when saving. They are stored in the *objects* field. When an entity with an *objects* field is spawned, it spawns all of the objects listed in turn.
|
||||
|
||||
Components cannot be prototyped. They are fundamentally tied to the entity they are bound to.
|
||||
When an entity is spawned, it is addressable directly through its master entity. Its name is generated from its file or ur type name.
|
||||
|
||||
Entities can be prototyped out. What this means is that, when you select an object in the game, you can either make a "subtype" of it, where changes to the object trickle down to the created one, or a "sidetype" of it, which is a total duplicate of the object. Javascript handled creating entites with components that have your saved values.
|
||||
|
||||
entity.clone(parent) -> create a subtyped version of the entity
|
||||
entity.dup(parent) -> create a copy of the entity.
|
||||
entity.promote() -> promote the entity to a new Ur-type, as it currently exists.
|
||||
entity.revert() -> remove all changes of this entity so it again matches its Ur-type.
|
||||
entity.push() -> push changes to this entity to its Ur-type to it matches.
|
||||
|
||||
*** Ur-types
|
||||
An Ur-type is a thing which cannot be seen but which can stamp out copies of itself. Objects can be promoted to an ur-type, so if it is deleted, another one can later be made.
|
||||
|
||||
Ur-types have a lineage going back to the original gameobject. The ur-type lineage can be as deep as you want it to be; but it should probably be shallow.
|
||||
|
||||
Only first Ur-types can have components. Every inherited thing after it can only edit the original's components, not add or subtract. Original Ur-types must currently be defined in code.
|
||||
|
||||
Ur-types also remember the list of entities that compose the given Ur.
|
||||
|
||||
Visually it looks like this:
|
||||
|
||||
Ur-ur, the thing all Ur-types derive from
|
||||
- Ur-type 1, defined in script, with components
|
||||
- Variant 1, same component combination but different values
|
||||
- Variant 2, other different values
|
||||
- Variant 2A, overwritten values from Variant 2
|
||||
- Ur-type 2
|
||||
|
||||
All ur-types and variants can be created in the world, where they become an entity. Entities can be under entities infinitely. (Master-padawan)
|
||||
|
||||
Entities have access to their ur-type through their .ur parameter. Each ur-type and variant likewise stores a list of entities that have been created from them.
|
||||
|
||||
When an entity changes from its ur-type, it can be saved as its own variant, or merged up to its ur type. Brand new entities can be spawned via duping an already existing one (which retains its differences), or by requesting one from an ur type. Ur types are accessed through dot notation - ur.urtype.var1.vara. The 'ur' global object stores all ur types.
|
||||
|
||||
the "ur" object has a few fields.
|
||||
ur.type : the actual object
|
||||
ur.instances : instances of it
|
||||
ur.tag : the full name of it
|
||||
#+begin_scholium
|
||||
Let's take a simple RPG game.
|
||||
#+begin_src
|
||||
Primum
|
||||
level1
|
||||
orc
|
||||
goblin
|
||||
human
|
||||
sword
|
||||
ui
|
||||
#+end_src
|
||||
The orc, for example, is addressable by ~Primum.level1.orc~. The ~human~ has a ~sword~ spawned underneath it. When he is killed, his sword also disappears.
|
||||
#+end_scholium
|
||||
|
||||
*** Resources
|
||||
Assets can generally be used just with their filename. It will be loaded with default values. However, how the engine interprets it can be altered with a sidecar file, named "filename.asset", so "ball.png" will be modified via "ball.png.asset". These are typical JSON files. For images, specify gamma, if it's a sprite or texture, etc, for sound, specify its gain, etc.
|
||||
Assets can generally be used simply with their filename. Assets can be modified with a sidecar file named *filename.asset*, so, a file ~ball.png~ can have additional parameters through its ~ball.png.asset~ file.
|
||||
|
||||
Ur-types are directly related to your file directory hierarchy. In a pinball game where you have a flipper, and then a flipper that is left ...
|
||||
| sigil | meaning |
|
||||
|--------+------------------------|
|
||||
| \slash | root of project |
|
||||
| @ | root of save directory |
|
||||
| # | root of link |
|
||||
|
||||
#+begin_example
|
||||
@/
|
||||
Resources can be referenced in a relative manner by actor scripts. When it comes to actors using assets, relative filepaths are useful and encouraged.
|
||||
|
||||
#+begin_src
|
||||
/
|
||||
score.wav
|
||||
/bumper
|
||||
hit.wav
|
||||
bumper.js
|
||||
bumper.jso
|
||||
/ball
|
||||
hit.wav
|
||||
ball.js
|
||||
/flipper
|
||||
flipper.js
|
||||
flipper.json <-- Modifications applied to the flipper ur-type
|
||||
t1.json <-- A variant of flipper.js. Cannot be subtyped.
|
||||
flip.wav
|
||||
flipper.img
|
||||
left/
|
||||
flip.wav
|
||||
left.js <-- This will load as an extension to flipper.js
|
||||
#+end_example
|
||||
|
||||
This is how resources are loaded in any given ur-type. Relative file paths work. So, in 'ball.js', it can reference 'hit.wav', and will play that file when it does; when bumper.js loads 'hit.wav', it will load the one located in its folder.
|
||||
ball.jso
|
||||
#+end_src
|
||||
|
||||
An ur-type is a list of properties and functions. When the ur-type is created, an entity is spawned in the world, and the entity is pasted with all the properties of the ur-type. After that, hookup happens, for start, update, physupdate, etc, functions.
|
||||
Path resolution occurs during actor creation. In effect, a reference to *hit.wav* in *bumper.jso* will resolve to the absolute path */bumper/hit.wav*.
|
||||
|
||||
The left flipper can use the root flipper flip sound by loading "../flip.wav".
|
||||
If the asset is not found, it is searched for until the project root is reached. The bumper can reference *score.wav* and have the path resolution take place. Later, if the it is decided for the bumper to have a unique score sound, a new /score.wav/ can be placed in its folder and it will work without changing any code.
|
||||
|
||||
Absolute paths can be specified using a leading slash. The absolute path the bumper's hit sound is "/bumper/hit.wav".
|
||||
#+begin_scholium
|
||||
Caution! Because the path is resolved during object load, you will need to fresh the bumper's ur or spawn a new bumper for it to use the newly placed /score.wav/.
|
||||
#+end_scholium
|
||||
|
||||
When you attempt to load the "flipper.left" ur-type, if flipper is not loaded, the engine will load it first, and then load left.
|
||||
**** Links
|
||||
Links can be specified using the "#" sign. These are shortcuts you can specify for large projects. Specify them in the array ~Resources.links~.
|
||||
|
||||
Unloading an ur-type unloads everything below it, necessarily. flipper.left means nothing without flipper.
|
||||
An example is of the form ~trees:/world/assets/nature/trees~. Links are called with ~#~, so you can now make a "fern" with ~Primum.new("#trees/fern.jso")~.
|
||||
|
||||
Computer systems have a user configuration folder specified, where you are allowed to write data to and from. This is good for save games and configurations. It is specified with a leading "@" sign. So "@1.save" will load the file "1.save" from the folder allotted to your game by the platform.
|
||||
*** Ur auto creation
|
||||
Instead of coding all the ur type creation by hand, Prosperon can automatically search your project's folder and create the ur types for you. Any /[name].jso/ file is converted into an ur with the name. Any /[name].json/ file is then applied over it, should it exist. If there is a /.json/ file without a corresponding /.jso/, it can still be turned into an ur, if it is a valid ur format.
|
||||
|
||||
Links can be specified using the "#" sign. This is simply defined as, for example, with "trees:/world/assets/nature/trees" specified, you can easily make the ur-type "fern" with "Primum.spawn("#trees/fern")", instead of "Primum.spawn('#trees.fern')"
|
||||
The folder hierarchy of your file system determines the ur prototype chain. /.jso/ files inside of a folder will be subtyped off the folder ur name.
|
||||
|
||||
Primum will attempt to solve most resolution ambiguities automatically. There are numerous valid directory layouts you can have. Examining flipper.left ...
|
||||
Only one ur of any name can be created.
|
||||
|
||||
#+begin_src
|
||||
@/
|
||||
flipper.js
|
||||
flipper/
|
||||
left.js
|
||||
|
||||
@/
|
||||
flipper/
|
||||
_.js
|
||||
left.js
|
||||
|
||||
@/
|
||||
flipper/
|
||||
flipper.js
|
||||
left/
|
||||
left.js
|
||||
#+end_src
|
||||
|
||||
In sum, a file that is a single underscore _.js is assumed to be the given folder's ur-type. When populating the ur-type hierarchy, the _ is replaced with the name of the containing folder. if there is a folder with the same name as *.js present, the items in the folder are assumed to be ur-types of the *.js.
|
||||
~prototypes.generate_ur(path)~ will generate all ur-types for a given path. You can preload specific levels this way, or the entire game using ~prototypes.generate_ur('.')~. If your game is small enough, this can have a massive runtime improvement.
|
||||
|
||||
Asset links always follow the directory hierarchy, however, so if you want to reference an asset with a relative path, the .js file must actually be present in the same path as the asset.
|
||||
** Input
|
||||
Input is done in a highly generic and customizable manner. *players* can take control of any object (actor or otherwise) in Prosperon, after which it is referred to as a *pawn* of a player. If the object has a defined *input* object, it is a valid pawn. One player can have many pawns, but each pawn may have only one player.
|
||||
|
||||
prototypes.generate_ur(path) will generate all ur-types for a given path. You can preload specific levels this way, or the entire game if it is small enough.
|
||||
Pawns are added as a stack, with the newest ones getting priority, and handled first. It is possible for pawns to block input to lower pawns on the stack.
|
||||
|
||||
*** Spawning
|
||||
The outmost sphere of the game is the Primum, the first entity. Your first entity must be spawned in the Primum. Subsequent entities can be spawned in any entity in the game.
|
||||
#+begin_src
|
||||
/newest/
|
||||
car <== When a key is pressed, this is the first pawn to handle input
|
||||
player
|
||||
ui <== /block/ is set to true here, so editor recieves no input!
|
||||
editor
|
||||
/oldest/
|
||||
#+end_src
|
||||
|
||||
Ur-types can remember configurations of entities spawned inside of them.
|
||||
The default player can be obtained with ~Player.players[0]~. Players are all local, and the highest number is determined by platform.
|
||||
|
||||
Once entities are created in the world, they are mostly interested now in addressing actual other objects in the world. Let's look at an example.
|
||||
The *input* object defines a number of keys or actions, with their values being functions.
|
||||
|
||||
Primum
|
||||
Level 1
|
||||
Orc
|
||||
Goblin
|
||||
Human
|
||||
Sword
|
||||
UI
|
||||
*** Editor input
|
||||
The editor input style defines keystrokes. It is good for custom editors, or any sort of game that requires many hotkeys. Keystrokes are case sensitive and can be augmented with auxiliary keys.
|
||||
|
||||
When a script is running, it is completely contained. If "Human" has a "health" parameter, it can only be access through "self.health", not directly through "health". This makes it easy to run a script without fear of overwriting any parameters on accident.
|
||||
| symbol | key |
|
||||
|--------+-------|
|
||||
| C | ctrl |
|
||||
| M | alt |
|
||||
| S | super |
|
||||
|
||||
The "$" is populated with an object's children. $.sword.damage will properly get the damage of the human's sword, and will be undefined for Goblin and Orc.
|
||||
#+begin_src
|
||||
var orc = Primum.spawn(ur.orc);
|
||||
orc.inputs = {};
|
||||
orc.inputs.a = function() { ... };
|
||||
orc.inputs.A = function() { ... }; /* This is only called with a capital A! */
|
||||
orc.inputs['C-a'] = function() { ... }; /* Control-a */
|
||||
Player.players[0].control(orc); /* player 0 is now in control of the orc */
|
||||
#+end_src
|
||||
|
||||
To access the entity's owner, it is through _. For example, the human can access the orc via _.Orc.
|
||||
The input object can be modified to customize how it handles input.
|
||||
|
||||
** The REPL
|
||||
| property | type | effect |
|
||||
|----------------+----------+--------------------------------------|
|
||||
| post | function | called after any input is processed |
|
||||
| =release_post= | function | called after any input is released |
|
||||
| fallthru | bool | false if input should stop with this |
|
||||
| block | bool | true if input should stop with this |
|
||||
|
||||
The REPL lets you poke around in the game. The first accessible item is the Primum. Everything else must be child to it. When an object is selected, its children are accessible in a more friendly way. You can do commands as if you "are" that object.
|
||||
The input can be modified by setting properties on the associated function.
|
||||
|
||||
The ur-type of the object is always shown in purple. If it has no type, there is nothing shown.
|
||||
| property | type | effect |
|
||||
|----------+----------+--------------------------------------------------------|
|
||||
| released | function | Called when the input is released |
|
||||
| rep | bool | true if holding the input should repeatedly trigger it |
|
||||
| down | function | called while the input is down |
|
||||
|
||||
** Textures & images
|
||||
A sprite is a display of a specific texture in the game world. The
|
||||
underlying texture has values associated with it, like how it should
|
||||
be rendered: is it a sprite, is it a texture, does it have mipmaps,
|
||||
etc. Textures are all file based, and are only accessed through the
|
||||
explicit path to their associated image file.
|
||||
** GUI
|
||||
Game GUIs are written by registering an entity's *gui* property to a function.
|
||||
|
||||
** Finding & Addressing Objects
|
||||
The GUI system which ships with Prosperon is called *MUM*. MUM is a declarative, immediate mode interface system. Immediate to eliminate the issue of data synchronization in the game.
|
||||
|
||||
All GUI objects derive from MUM. MUM has a list of properties, used for rendering. Mum also has functions which cause drawing to appear on the screen.
|
||||
|
||||
* Editor Tour
|
||||
Prosperon's visual editor is an assistant for the creation and editing of your game entities and actors. In the editor, all ur types are loaded, and assets are constantly monitored for changes for hot reloading.
|
||||
|
||||
To initiate it, execute ~prosperon~.
|
||||
|
||||
** Editing entities
|
||||
The desktop is the topmost entity that exists in the editor. Instead of editing specific files, you simply load them into your desktop, and go from there. This makes it easier to see two different entities simultaneously so you can ensure changes to one are congruous with the vision for the others.
|
||||
|
||||
The main editor view is made up of entities. Each entity can have a number of components attached to it. When an entity is selected, its name, position, and list of components are listed.
|
||||
|
||||
Basic use of the editor involves spawning new entities, or ones from already made ur types, editing them, and then saving them as new ur types or overwriting the ones they spawned from. Specific tools have been written to make editing components and levels easier than with a text editor, and the editor is easily extendable for your own purposes.
|
||||
|
||||
Assign the entity's *gizmo* property to a function to have that function called each gui rendering frame.
|
||||
|
||||
** The REPL[fn::Read-eval-print loop]
|
||||
The REPL lets you poke around in the game. It makes iteration and experimentation fast, fun, and easy.
|
||||
|
||||
The symbol ~$~ references the current REPL entity. If no entity is selected, the REPL entity is the currently edited one. Otherwise, it is the selected entity, or group of entities, as an array.
|
||||
|
||||
#+begin_scholium
|
||||
Easily run commands on multiple entities using Javascript functions like for each.
|
||||
#+begin_src
|
||||
$.forEach(e => console.log(e.pos));
|
||||
#+end_src
|
||||
#+end_scholium
|
||||
|
||||
The REPL is a powerful tool for editing your game. Arbitrary code can be ran in it, meaning any esoteric activity you need done for your game can be done easily. Commonly used functions should be copied into your /editorconfig.js/ to be called and used at will.
|
||||
|
||||
** Playing the game
|
||||
Playing the game involves running the game from a special /debug.js/ file, or from the beginning, as if the game were packaged and shipped.
|
||||
|
||||
| key | action |
|
||||
|-------+-----------------------------------------------------|
|
||||
| f5 | Play the game, starting with entry point /debug.js/ |
|
||||
| f6 | Play the game from the beginning |
|
||||
|
||||
While playing the game, a limited editor is available that allows for simple debugging tasks.
|
||||
|
||||
| key | action |
|
||||
|-----+-----------------------------|
|
||||
| C-p | Pause |
|
||||
| M-p | One time step |
|
||||
| C-q | Quit play, return to editor |
|
||||
|
||||
** Script Editor
|
||||
Prosperon comes with an in-engine script editor. It implements a subset of emacs, and adds a few engine specific features.
|
||||
|
||||
*** Syntax coloring? ... nope!
|
||||
The editor that ships with Prosperon has *context coloring*, which is a good deal more useful than syntax coloring.
|
||||
|
||||
** Debugging
|
||||
Although intertwined, debugging functions and the editor are separate entities.
|
||||
|
||||
Debugging functions are mapped to the F buttons, and are always available during gameplay in a debug build. Pressing the F button toggles the feature; pressing it with ALT shows a legend for that feature; pressing it with CTRL shows additional info
|
||||
Debugging functions are mapped to the F buttons, and are available in any debug build of the game. Pressing the specified key toggles the feature; pressing it with /alt/ shows a legend for that feature.
|
||||
|
||||
| key | description |
|
||||
|-----+----------------------------|
|
||||
| F1 | Draw physics info |
|
||||
| F3 | Draw bounding boxes |
|
||||
| F12 | Drawing gui debugging info |
|
||||
| F12 | Draw gui info |
|
||||
|
||||
* Editor Tour
|
||||
Prosperon's visual editor is just one tool of many you use to make your game. It is not an all in one shop like in other game engines.
|
||||
* Exporting your game
|
||||
Prosperon is a multiplatform engine. Bundling your game for these platforms essentially involves three steps:
|
||||
|
||||
The main editor view is made up of objects. Each object can have a number of components attached to it. When an object is selected, its name, position, and list of components are listed.
|
||||
- Baking static content
|
||||
- Conversion of assets
|
||||
- Packing into a CDB[fn::Constant database]
|
||||
|
||||
In addition, a window showing each entity underneath that entity are shown.
|
||||
To distribute your game for a given platform, run ~prosperon build {platform}~.
|
||||
|
||||
** The desktop
|
||||
| platform |
|
||||
|----------|
|
||||
| Linux |
|
||||
| MacOS |
|
||||
| Windows |
|
||||
|
||||
The desktop is the topmost object that exists in the editor. Instead of editing specific files, you simply load them into your desktop, and go from there. This makes it easier to see two different entities simultaneously so you can ensure changes to one are congruous with the vision for the others.
|
||||
You will find your game ready to go. Rename the executable to the name of your game and run it to play. Congratulations!
|
||||
|
||||
** *'s and %'s
|
||||
** Building static content
|
||||
Static content creation involves any number of optimizations.
|
||||
|
||||
When a '*' is seen next to an entity's name, that means it is altered compared to its ur-type and is unsaved. There are a number of ways to take care of a '*'. If you do not do one of the below, something on the entity will be lost.
|
||||
- Bitmap font creation
|
||||
- Texture map creation
|
||||
|
||||
- Changes can be saved to the ur-type. This makes all other entities derived from the ur-type change.
|
||||
- Changes can be saved as a sub ur-type. This creates a brand new type that can then be reused over and over again.
|
||||
- Changes can be saved by saving the containing ur-type. Ur-types remember variances in the entities it 'owns'.
|
||||
Creation of these assets is invisible. Prosperon updates its understanding of how to pull assets based on the existance of these packed ones.
|
||||
|
||||
When an entity is different from its ur-type, but the variance is saved due to its container, its name is preceded by a '%'.
|
||||
** Converting assets
|
||||
Images, videos, and sounds, are converted to assets most suitable for the target platform. This may be for speed or simple compatability. *You do not need to do anything*. Use your preferred asset types during production.
|
||||
|
||||
The function 'revert()' can be called on any entity to make it like its ur-type again.
|
||||
** Packing into a CDB
|
||||
A *cdb* is known as a "constant database". It is a write once type of database, with extremely fast retrieval times. Packing your game into a cdb means to create a database with key:value pairs of the filenames of your game. The Prosperon executable is already packed with a core cdb. Your game assets are packed alongside it as the game cdb.
|
||||
|
||||
** Levels?
|
||||
You can create your game's cdb by running ~prosperon -b~. You will find a *game.cdb* in the root directory.
|
||||
|
||||
The concept of a 'level', a collection of spawned entities, is handled simply by creating sub ur-types of an empty entity.
|
||||
* Modding & Patching
|
||||
When an asset is requested in Prosperon, it is searched for in the following manner.
|
||||
|
||||
** Editing level, ur-types, parents, children, etc
|
||||
1. The cwd[fn::current working directory]
|
||||
2. The game cdb (not necessarily present)
|
||||
3. The core cdb
|
||||
|
||||
Game modification is trivial using this described system. By putting an asset in the same path as the asset's location in the game cdb, when that asset is requested it will be pulled from the file system instead of the game cdb.
|
||||
|
||||
- lvl1
|
||||
- tablebase
|
||||
- %flipper
|
||||
Given a Prosperon-built game, you can unpack its content into a directory by running ~prosperon unpack {game}~.
|
||||
|
||||
In this case, tablebase is saving a modified flipper.
|
||||
** Shipping
|
||||
Once a game's assets are modified, it may be desirable to ship them. Run ~prosperon patch create {game}~ to create a /patch.cdb/ filled only with the files that are different compared to those found in the /game.cdb/ in the /game/.
|
||||
|
||||
- lvl1
|
||||
- %tablebase
|
||||
- %flipper
|
||||
To update /game/ to use the new patch, run ~prosperon patch apply {patch}~, replacing /patch/ with the name of the cdb file generated above.
|
||||
|
||||
This is ambiguous. lvl1 could be storing the flipper's diff, or tablebase could be. Additionally, tablebase could have a unique flipper, and then lvl1 also alters it.
|
||||
Many patches can be bundled by running ~prosperon patch bundle {list of patches}~. This creates a patch that will update the game as if the user had updated each patch in order.
|
||||
|
||||
* Building
|
||||
|
||||
* Tutorials
|
||||
Mods can be distributed with the same idea.
|
||||
|
|
|
@ -2,8 +2,33 @@ a:link a:visited a:hover a:active {
|
|||
color: green;
|
||||
}
|
||||
|
||||
.title {
|
||||
visibility: hidden;
|
||||
#content {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-padding-top:50px;
|
||||
}
|
||||
|
||||
#text-table-of-contents {
|
||||
background-color: #ffffec;
|
||||
border: solid;
|
||||
border-width: 2px;
|
||||
border-color: #61bcd7;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.scholium {
|
||||
background-color: #F5F5DE;
|
||||
border: solid;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
border-width: 2px;
|
||||
border-color: #75FB4C;
|
||||
}
|
||||
|
||||
.scholium p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.floathead {
|
||||
|
@ -48,19 +73,27 @@ h1 {
|
|||
top: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: code;
|
||||
color: #61bcd7;
|
||||
}
|
||||
|
||||
.example {
|
||||
background-color: #FFFFEC;
|
||||
border-width: 2px;
|
||||
border-color: #61bcd7;
|
||||
margin: 20px;
|
||||
margin: 10px 20px;
|
||||
padding: 20px;
|
||||
font-family: code;
|
||||
font-size: 16px;
|
||||
line-height: 1em;
|
||||
white-space: pre-line;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "code";
|
||||
src: url(dos.ttf);
|
||||
|
@ -70,15 +103,6 @@ h1 {
|
|||
background-color: #1678b2;
|
||||
}
|
||||
|
||||
.scholium {
|
||||
background-color: #F8F8F1;
|
||||
}
|
||||
|
||||
.scholium .admonition-title {
|
||||
background-color: #75FB4C;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.navbar-brand span {
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Prosperon Engine
|
||||
A developer minded, 2D-first game engine.
|
||||
|
||||
## Installation
|
||||
Run make.
|
||||
See the documentation [here](https://prosperon.dev/doc).
|
||||
|
|
|
@ -54,7 +54,7 @@ actor.delay = function(fn, seconds) {
|
|||
this.timers.push(t);
|
||||
return function() { t.kill(); };
|
||||
};
|
||||
actor.clock = function(fn){};
|
||||
|
||||
actor.master = undefined;
|
||||
|
||||
actor.padawans = [];
|
||||
|
|
Loading…
Reference in a new issue