#41211 - sgeos - Tue Apr 26, 2005 1:50 pm
I was pondering OO stages a while back. While thinking about it, it seemed to me that script based stages would be really neat. I made a 3D cursor driven prototype that supports the following commands (and variations, like movex x):
move x y z
setpos x y z
select x y z
setselect x y z
draw tile
palette color
script name
I wrote a simple script for it. This script can called from other scripts with the script room.
Code: |
draw wall
move 1 1 0
select -2 -2 0
draw floor
select 2 2 0
move -1 -1 0 |
It just creates the outline of a wall and resets the selection. Has anybody else played around with or thought about script based stages? A point and click tool that autogenerates the required drawing code would clearly have to be made. Undo and redo could basically be implemented for free. Patching stages becomes free (write a patch and call it), and each script represtents a stage object.
It seems to me that for this to be really useful, the scripts need to be able to hold variables and accept parameters. It seems that I might just want to export the scripts to C functions. Any thoughts on this and the script based approach in general?
-Brendan
#41230 - sajiimori - Tue Apr 26, 2005 6:09 pm
Whoa, you lost me. Are you talking about map data representation?
#41236 - ScottLininger - Tue Apr 26, 2005 8:15 pm
Brendan,
I think it's a really interesting idea from a data compression point of view. Of course, it assumes that your map can be described in this way, but if it can, it could really lower your ROM size.
For example, the iso engine we used for Star Fortress Dendron stored every tile of every map. If an isometric room were described as a series of scripted "walls", you could probably store the maps for a fraction of the cart space...
One disadvantage of a computer "drawn" wall or floor is the regularity of the tiles. A human artist can take a small tile set and intuitively use them to create surfaces that don't look tiled. But then again, with a properly organized tile set, one might be able to write an algorithm that "breaks up" the tiles using a random seed...
The other thing I like about it is that once the scripting engine is in place, it would open up interesting possibilities for dynamically created levels. You know, random dungeon generation, etc.
-Scott
#41242 - sajiimori - Tue Apr 26, 2005 9:55 pm
Declarative representations ("what it is") are usually simpler and more compact than imperative ones ("how to make it"). In practice, good use of abstraction will tend to make the imperative version look almost like the declarative version. For instance, here's some well-abstracted imperative code for drawing a circle inside a rectangle:
Code: |
drawPicture()
{
// drawRect takes the left, right, top, and bottom edges.
drawRect(-1, 1, -1, 1);
// drawCircle takes the center and the radius
drawCircle(0, 0, 1);
}
|
And here's how you might describe that picture declaratively:
Code: |
Picture = Circle(0, 0, 1) `onTopOf` Rect(-1, 1, -1, 1)
|
Note that I'm not implying that the declarative version is shorter. The first version could have been written like this:
Code: |
picture() { rect(-1, 1, -1, 1); circle(0, 0, 1); }
|
The point is that the declarative version is more like describing data, and that approach will tend to make your representations more natural and concise on average. The picture is a circle on top of a rectangle, rather than you make the picture by drawing a rectangle and then a circle.
#41300 - poslundc - Wed Apr 27, 2005 8:15 am
This seems to me to be analogous the ancient conflict between vector graphics and raster graphics, basically Illustrator vs. Photoshop.
If the analogy holds, then the answer is simply there's a time and place for both approaches.
That said, the raster (or, to borrow sajimori's terminology, "declarative") approach makes the most sense for the vast majority of applications on a platform like the GBA, because the data doesn't need to be post-processed in order to work with the hardware. Your tool encodes the data in a compatible format, and you know that you can load it with a constant level of complexity. It is the "simple" way of doing things, and unsurprisingly in most cases simple is best.
The advantages pointed out for the vector/imperative method are also valid. But I would want to see an application that really needs those features before I would trade in the more elegant effectiveness of a straightforward representation.
To address some more specific points that were made:
- Undo/redo in the tool seems to me to be a weak case for the "vector" approach. These are features that serve the tool, not the end result. The tool should be working for you, not the other way round.
- The compression advantage is undeniable. But remember there are other methods of compression as well. Other advantages of the vector approach (such as easy transformations like scaling) are harder to find positive applications for when you are coding for the GBA, where you just need pretty much everything "rendered" already.
- I've actually used something not wholly dissimilar to this for the random terrain generator I created for my Mode 7 engine at one point. So there clearly are cases where such things are useful. But it should be noted that while the savings were obvious (I didn't have to consume any ROM space with maps, just the rules for creating them) the processing time was not negligible, and I had to cut a lot of corners and optimize the crap out of it and even still required an "enter battle" transition effect to kill time while the level was being generated. The lesson is to always bear in mind the target when making such design decisions!
Dan.
#41318 - tepples - Wed Apr 27, 2005 3:02 pm
Advanced technique
If you're only going to call a given piece of code rarely, but it needs to run quickly when it is called, it might be a good idea to compile the function as ARM and position-independent (-marm -mthumb-interwork -fPIC) so that you can just 1. allocate an array on the stack, 2. copy the code into the array, and 3. execute from the stack. I believe one of the JPEG decoders (by Burton Radons?) does this.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#41327 - sajiimori - Wed Apr 27, 2005 5:11 pm
I wasn't talking about raster versus procedural, but rather procedural versus procedural -- two different kinds of it. Both kinds are a lot like code, but one is more like imperative code and one is more like functional code.
I've heard tepples describe the map format used in Super Mario World, and that more closely matches the declarative functional style I'm suggesting. The map data says what the map is by describing its components, and those components needn't be all the same size with the same properties.
One of the primary goals when designing such a format is to find the shortest way to describe what you want, and all things being equal, "what it is" tends to be shorter and more direct than "how to make it".
BTW, stole the idea from "The Haskell School of Expression", which describes a functional declarative graphics library.
Edit: I agree with Dan about the raster approach being simpler, with an additional qualification. The eventual goal is to have a ROM binary that meets your requirements, and it's desirable to reduce the effort needed to reach that goal. Since the volume of tool code often meets or exceeds the volume of engine code, it's reasonable to consider approaches that make tool authoring easier.
#41513 - sgeos - Fri Apr 29, 2005 10:42 am
sajiimori wrote: |
Whoa, you lost me. Are you talking about map data representation? |
Yes. I'm talking about representing maps as a series of cursor, selection and drawing instructions.
sajiimori wrote: |
I've heard tepples describe the map format used in Super Mario World, and that more closely matches the declarative functional style I'm suggesting. The map data says what the map is by describing its components, and those components needn't be all the same size with the same properties. |
I was pondering the object based map formats when I came across the idea of script based map format. Assuming that one script can call another, and that the called scripts can be passed parameters, then each subscript becomes a new map object. Even if each cell is unique, once the map object has been created it can be reused an infinite number of time. First call is "expensive", every further call takes once instruction.
ScottLininger wrote: |
I think it's a really interesting idea from a data compression point of view. Of course, it assumes that your map can be described in this way, but if it can, it could really lower your ROM size. |
It is difficult to describe art heavy maps using this approach. The more unique tiles maps have, the less appropriate it becomes. Such scripts are going to look like:
move 1 0 0
draw fountain_of_doom_0
move 1 0 0
draw fountain_of_doom_1
...
move 1 0 0
draw fountain_of_doom_31
Art heavy maps are still appropriate if art heavy objects are reused. 99% of the scripts will be generated by user mouse clicks.
ScottLininger wrote: |
For example, the iso engine we used for Star Fortress Dendron stored every tile of every map. If an isometric room were described as a series of scripted "walls", you could probably store the maps for a fraction of the cart space... |
The larger the library of subscripts (generic map objects) becomes, the smaller stages become.
The map format used it Star Fortress Dendron was the simple brute force format. Everything considered I think that either a 3D array of conceptual tiles, or a 2D array with a height map is a nicer format. Harder to put in place (and potentially less flexible), but nicer.
ScottLininger wrote: |
One disadvantage of a computer "drawn" wall or floor is the regularity of the tiles. A human artist can take a small tile set and intuitively use them to create surfaces that don't look tiled. But then again, with a properly organized tile set, one might be able to write an algorithm that "breaks up" the tiles using a random seed... |
Keep in mind that there is going to be a human driver behind all stages made with a system like this. "All look same" can be handled with seed numbers and wieghted lists of tiles.
40% Floor
30% Rusty Floor
20% Cracked Floor
10% Rubble
It can be useful to include a NULL tile in certain tables. (Ie don't draw anything.) Useful for patching stages.
40% NULL
30% Leaves
10% Water
10% Acid
9% Flower
1% Carnivorous Flower
For something really interesting, the map could be seeded with time on every entry. =) These tables remind me of old role playing games, like Traveller.
Although they were not part of the prototype, my original design contained weighted tables and multiple draw shapes (circle, arc, square, hollow box, etc).
ScottLininger wrote: |
The other thing I like about it is that once the scripting engine is in place, it would open up interesting possibilities for dynamically created levels. You know, random dungeon generation, etc. |
I had also thought of that. I think that before one starts randomly generating stages, one wants to get a feel for what the "engine" can do by manually creating stages.
poslundc wrote: |
The advantages pointed out for the vector/imperative method are also valid. But I would want to see an application that really needs those features before I would trade in the more elegant effectiveness of a straightforward representation. |
Do you want to save on ROM by compressing the map, or save RAM and runtime processing by preprocessing the map? It really doesn't matter what you end up putting on the cart. I think the important thing is to have your stages stored in an easy to edit format.
poslundc wrote: |
- Undo/redo in the tool seems to me to be a weak case for the "vector" approach. These are features that serve the tool, not the end result. The tool should be working for you, not the other way round. |
Undo/redo is not a case for the "vector" approach. I just thought it was interesting that an infinite level of undo/redo comes free (in programmer time) with this approach. To undo, one merely redraws the map from scratch and executes one fewer commands.
sajiimori wrote: |
One of the primary goals when designing such a format is to find the shortest way to describe what you want, and all things being equal, "what it is" tends to be shorter and more direct than "how to make it". |
The proposed current format, first decribes "how to make things". Once you know how to make things, it's a matter of placing orders.
sajiimori wrote: |
Edit: I agree with Dan about the raster approach being simpler, with an additional qualification. The eventual goal is to have a ROM binary that meets your requirements, and it's desirable to reduce the effort needed to reach that goal. Since the volume of tool code often meets or exceeds the volume of engine code, it's reasonable to consider approaches that make tool authoring easier. |
Undo/redo comes free (in programmer time). I don't yet have an easy way to handle declared variables and parameters. Parameters could probably be handled with a stack.
-Brendan
#41518 - sgeos - Fri Apr 29, 2005 12:22 pm
The names will probably change, but this is the current list of commands I plan to use. The following are editor commands that won't be saved when the user enters them:Undo
Redo
Save
SaveAs filename
Load filename
New x y z
Quit
The follow are commands that will be saved when the user enters them. Alternatively, they may be autogenerated by the frontend.Orgin
MoveAbsolute x y z
MoveRelative x y z
Deselect
SelectAbsolute x y z
SelectRelative x y z
Unrotate
RotateXY degrees
RotateXZ degrees
RotateYZ degrees
SetDrawShape mode
SetDrawAxes axes
SetDrawIn tile
SetDrawLine tile
SetDrawFace tile
SetDrawOut tile
SetFont font
SetPalette palette
DrawAbsolute
DrawFont
Cut tile
Copy
Paste
SeedRNG seed
SeedRNGTime
Script name
"tile" refers to either a simple tile, or a weighted list.
Rotation rotates the selection. Draw shapes will be things like circle, square, arc, etc. The draw axes determine what plane to draw the shape in. Something like this would draw a pillar of ice.Select 8 8 8
SetDrawAxes XY
SetDrawShape Circle
Draw Ice
Clearly drawing a cube or a sphere would not pay any attention the to axes. The shape is drawn using the DrawLine, DrawFace, DrawIn, and DrawOut values. Not all shapes use all the values. DrawOut will usually be NULL, but setting it another value would be an easy way to draw something like a circle inside of a square.
A font is a nifty way of thinking about tiles. Instead of drawing tiles using the global absolute tileset, one draws tiles using the font. Something like this would draw a castle room and then a cave room.SetFont CastleFont
Script room
Move ...
Select ...
SetFont CaveFont
Script room
The room script uses DrawFont wall and DrawFont floor to do it's job.
I currently don't have a clean way to implement variables and subscript parameters in the PC side tool. Suggestions are welcome. The format would probably look something like this. Parameter based script calls, parameter declaration, variable declaration, and variable assignment respectively:Script name parameters
Parameter #name
Variable $name
$variablename Command parameters
Parameters are read only and are declared at the top of a script. All variables and parameters are ints. Assuming that support for variables is added, creating accessors would be fairly simple.
Thoughts and feedback in general are welcome.
-Brendan