Slices Tutorial

From OHRRPGCE-Wiki
Jump to: navigation, search

This article is the documentation for slices, the system used for customisable graphical display in the OHRRPGCE.

As an alternative introduction to slices, see also BMR's more detailed but less comprehensible Simple Slices Tutorial:


What is a slice?[edit]

A slice is a rectangular thing drawn to the screen. There are several types, some of which are just invisible containers of other slices. Each has an X/Y position, a width and a height, and is attached either to the screen (the "root" slice) or can be attached to any other slice.

Map layers, NPCs, and textboxes are examples of things drawn with slices, which allows their appearance to be customised using scripts.

Slice Collection Editor[edit]

A great way to get used to what slices are capable of is to try out the Slice Collection Editor in Custom. The slice collection editor lets you build complex collections of slices using a graphical interface. You can load these collections into your scripts, but even if you will be creating all your slices manually in your plotscript, the slice collection editor is still a great place to test out slices and get a feel for how they work and what options they have.

You can also access the editor while playing a game by pressing the Ctrl+F4 debug key.

Family Metaphor[edit]

You will notice that slices use a lot of family metaphors, especially "parent" and "child". You can think of the slice tree as a family tree.

parent[edit]

A parent slice is any slice that has one or more children. If you move the parent, all of its children (and grandchildren) will move too. Resizing parent slice can affect the children too, depending on how they are attached to the parent.

A parent slice is always displayed behind its children.

children[edit]

Every slice (except the root slice) is a child of some other slice. Child slices are positioned relative to their parent, so when you want to move around a whole group of slices, you can just move their parent, you don't have to bother to move each individual child.

Child slices are always displayed in front of their parent.

siblings[edit]

Sibling slices are any two slices that share the same parent. Slices are displayed in order from the first (oldest) slice to the last (youngest) slice. That means that younger siblings will overlap older siblings. There are commands that let you rearrange the order of siblings, so you can easily make a specific slice appear behind or in front of another slice.

one big happy family[edit]

All slices are part of one big family tree. Every slice is connected to every other slice, as children, grand children, great-grand-children, parents, grand parents, great-grand-parents, siblings, cousins, second cousins, aunts, great aunts, nieces, great nieces (slices are always female, don't you know?)

why does family matter?[edit]

The familial relations of slices matter for how they overlap each other on the screen, and how they move together in groups. If slices don't overlap or don't need to move together, it doesn't really matter how they are related.

Slice types[edit]

The types of slices are:

sprites[edit]

These display a frame of a sprite set. For example, you can draw the attacking frame of hero spriteset 2 to screen. Unlike the other types, these have fixed width and height.

Sprite slices have different subtypes, each created by a different command:

It's not possible to load a tileset or font as a sprite slice.

One subtype can be turned into another with the replace ... sprite commands, eg replace backdrop sprite.

rects[edit]

These are rectangles (with optional box borders), like the back of a text box, or the health meters in battle.

text[edit]

These are invisible boxes containing text. The text can be configured to wrap around to stay inside the box.

containers[edit]

These are invisible. Containers are not special, they are basically plain slices. Every other slice type can do everything that they can (with the exception that sprite slices, and sometimes text slices, can't be resized).

grids[edit]

Grids are special containers that will automatically arrange their children into a grid. You specify the number of rows and columns, and if there are more than rows * columns children then the last ones are not displayed.

ellipses[edit]

Ellipse slices draw an oval to the screen which has maximum width and height equal to the slice width and height. The oval can be optionally filled, and the border is one pixel thick. The "major" and "minor" axes of the ellipse are always aligned with the X and Y axes.

scroll[edit]

A scroll slice is a special container that automatically displays scrollbars when its child slices overlap its edges.

select[edit]

A Select slice is a convenient way to have a set of slices, just one of which is visible at a time. The visible child is the "selected" child. A select slice is like a container slice, with the addition of a "selected index". For example if the index is 1 then the second child is visible. If the index is invalid, e.g. -1, then no child is selected/visible.

panel[edit]

A panel slice is a special container that automatically resizes and repositions its first two children. The children will each take up a portion of the panel slice's space. It can divide space horizontally or vertically, and can be configured to share space according to different percentages or fixed numbers of pixels. If it has more than 2 children, the additional children will always be hidden.

special slices[edit]

These are slices created automatically, such as the 'textbox layer' slice. They are just containers. You can't create or destroy them, and there are no special commands for any of them.

root slice[edit]

The root of the slice tree is its own unique type. It's actually just a container.

map slices[edit]

Each map layer is a slice. These are a different type of special slice that represents the map. You can't create or destroy them, and there are no special commands for this slice type.

Slices and Variables[edit]

Anyway, the main way you will use slices is with plotscripting, so on to coding examples! Suppose you want to put a large enemy sprite on the screen.

 variable(sl)
 sl := load large enemy sprite(0)

We have now added a slice containing a copy of large enemy sprite #0 to the slice tree. The slice tree is always drawn; all we need to do is add things to it and move them around. Here is how we move a slice:

 set slice x(sl, slice x(sl) + 10)

That moves the sprite 10 pixels to the right.

If you want to load a collection of slices that you designed in the Slice Collection Editor, use this command:

 variable(sl)
 sl := load slice collection(0)

Lookup codes[edit]

A slice lookup code is a name for a slice. (Multiple slices can have the same name.) All of the engine's auto-created slices, such as map layer slices, have unique names. You can use lookup slice to search for the handle of a specific slice, either globally or just part of the slice tree, such in the collection that you just loaded. The slice collection editor allows you to assign lookup code names to your slices. Edit a slice in the Slice Collection Editor, press Enter on "Lookup Code", then go to a blank space at the bottom of the menu (press Down or End), and then type in a name like "my wonderful slice".

 variable(col, sl)
 col := load slice collection(0)
 sl := lookup slice(sli:my wonderful slice, col)


You can also use the set slice lookup command to give a slice a lookup code, perhaps one you created with a script, so that you can find it back later. First, you should to create a lookup code in the slice collection editor (even if you don't use the editor for anything else): follow the instructions above (create a new slice to edit if necessary if you don't have any and then delete it later), It is also possible to just use numbers instead of names. You must use large positive numbers like 10000. Small numbers are assigned to lookup codenames.

Then you set a lookup code like so:

 variable(sl)
 sl := load walkabout sprite(4)
 set slice lookup(sl, sli:my wonderful slice)

Handles[edit]

What is a slice handle? A slice handle is a magic number that represents a specific slice. You store this magic number in a variable. Almost every slice-related plotscripting command works with slice handles. You don't need to store a handle for every single slice. You only need to use handles for slices that you need to move, change, rearrange, or remove.

X/Y vs Screen X/Screen Y[edit]

You may notice in the plotscripting dictionary that there are two sets of functions for working with slice x and y

At first glance these might seem to be totally redundant. Indeed, if you check out the values for our newly loaded large enemy sprite, you will notice that the x/y and screen x/screen y are the same.

The difference is, the slice x/y values are relative to the slice's parent. Every slice (except the root) has a parent. When we first load a sprite, the default parent for it is layer sprite layer which is a "special" slice. The sprite layer slice has an x/y position of 0,0 and a width/height of 320,200 just the same size as the screen.

Attaching slices to parents[edit]

One of the really cool things about slices is that you can attach them to a parent. For example, let's load a small enemy sprite that resembles a top-hat and put it on the large enemy sprite we loaded before.

 variable(hat)
 hat := load small enemy sprite(0)
 set parent(hat, sl)
 set slice x(hat, 23)
 set slice y(hat, -28)

I'm just guessin' on the x/y here, because I don't know exactly where the head of your large enemy sprite will be, but the point is that we have detached the hat sprite from the screen, and attached it to the large enemy sprite. Now if we say:

 set slice y(sl, slice y(sl) + 5)

then BOTH the enemy sprite AND his hat will move together.

Looping through slices[edit]

Now suppose that in this way we have set up 5 different enemy sprites, each of them wearing a hat. That makes a total of 10 slices on screen. Does that mean that we need to keep 10 variables to store their handles? Well, not really. Sure, you could be tucking the slice handles away in a fake array using global write global , but you don't really need to.

The fact is, the children of a parent slice are already kinda like an array. So let's see how you loop through them without needing to know their handles in advance.

 variable(handle)
 handle := first child(sprite layer)
 while(handle) do, begin
   #do something to each handle here
   handle := next sibling(handle)
 end

So first we use the child first child command. This takes any slice and gives us the handle to its first child, or 0 if there are no children.

Slice handles are always non-zero, so then we can say while(handle) to do our loop.

At the end of the loop, we use the sibling next sibling command to get the next slice handle, or 0 if there are no more (and as you know, a 0 will cause the "while" loop to stop)

Now if we loop through our hat-wearing enemy sprites in this way, the while loop is going to loop through 5 times, once for each enemy.

Why not for the hats? The hats are NOT children of the layer sprite layer anymore. Remember? we re-parented them with the parent set parent command. The hats are now children of the enemy sprites (and thusly they are grandchildren of the sprite layer)

So what if we want to loop through all the hats and manipulate them?

 variable(handle, hat)
 handle := first child(sprite layer)
 while(handle) do, begin
   hat := first child(handle)
   if(hat) then, begin
     #do something to each hat here
   end
   handle := next sibling(handle)
 end

See? We can use the "first child" command on the enemy sprite's slice handle and get the hat. Now suppose we wanted to allow enemies to maybe wear more than one hat... or glasses... or gloves or something.

 variable(handle, clothing)
 handle := first child(sprite layer)
 while(handle) do, begin
   clothing := first child(handle)
   while(clothing) do, begin
     # do something with each article of clothing here
     clothing := next sibling(clothing)
   end
   handle := next sibling(handle)
 end

And yes, children can have children which can have children, and so-on, until you create a family tree of slices that is so big you run out of memory.

Deleting slices[edit]

One other thing to note is that if you delete a slice using slice free slice then all of its children will be deleted too, as if you are chopping the whole branch off of the family tree.

Layering[edit]

Layering is done oldest-to-youngest, with depth-first traversal of the tree.

  • That means that we start with the slice layer
  • Then we draw the first child. (big enemy 1)
  • Then we draw the first child of the first child (big enemy 1's hat)
  • Then we draw the second child (big enemy 2)
  • Then we draw the first child of the the second child (big enemy 2's hat)
  • Then we draw the third child (big enemy 3)

And so-on. The upshot is that younger siblings are always drawn on top of older siblings, and that children of a parent are always drawn directly on top of their parent. Once you get use to it, it is pretty easy to understand how to arrange your slices to make them draw in the order you want. If you have not done so already, this might be a good time to use the "Slice Collection Editor" tool in custom, since you can use it to experiment with the way that slices are layered, and it shows you a visual representation of the slice tree.

Containers can be great for controlling how slices layer. A container is simply an invisible slice for use as a parent to other slices that you want to group together either for looping or for controlling draw-order. You can change the width and height of a container however you want.

Alignment and Anchors[edit]

So, you already know that a slice has a relative x/y position to its parent. By default, a slice is aligned to the top left corner of its parent, and it is anchored by its own top left corner. For some purposes, positioning everything according to top left corners is all you are going to need, but sometimes it becomes more convenient to position a slice in other ways. Check out this example where I attach a four NPC sprites to a container, one on each corner

 variable(box, sl)
 box := create container(150, 150)
 
 sl := load walkabout sprite(4)
 set parent(sl, box)
 # this one starts in the top left
 
 sl := load walkabout sprite(5)
 set parent(sl, box)
 set horiz align(sl, edge:right)
 set horiz anchor(sl, edge:right)
 # put this one in the top right
 
 sl := load walkabout sprite(6)
 set parent(sl, box)
 set vert align(sl, edge:bottom)
 set vert anchor(sl, edge:bottom)
 # put this one in the bottom left
 
 sl := load walkabout sprite(7)
 set parent(sl, box)
 set horiz align(sl, edge:right)
 set horiz anchor(sl, edge:right)
 set vert align(sl, edge:bottom)
 set vert anchor(sl, edge:bottom)
 # put this one in the bottom right

Now each of these four sprites will be arranged at the corners of a 15x150 container. Notice that we did not change the x/y of any of these slices? If you use x slice x and y slice y you will see that they are still all at 0,0 but if you check with screen x slice screen x and screen y slice screen y you will see that they now all have been moved.

Now what gets really cool is if you resize the container.

 variable(i)
 for(i, 0, 49) do, begin
   set slice width(box, slice width(box) + 1)
   set slice height(box, slice height(box) + 1)
   wait(1)
 end

The sprites attached to the corners will move automatically as you resize the box!

Learning More[edit]

New chapters can be added to this tutorial.

You may also want to browse the Plotscripting Dictionary and see all the slice related commands that are available.

Here is a list of games that use slices, which include HSS files that you can look at for examples. (Note that unfortunately some of these actually get quite complicated, and might not actually be good examples unless you already have a solid understanding of plotscripting.) Some also use slice collections which are worth inspecting.

If you have written your own game using slices, and if you have included your script file for other people to look at, please add it to this list.