Vlayer
The vlayer, or virtual layer, was an innovation introduced to MegaZeux in version 2.69c. Simply put, the vlayer is an invisible graphical scratch space that is common to all boards in a game. It can have data copied to and from it or directly written to it, and can be used as a reference space for sprites in lieu of using board space (an unresolved oversight in the sprite implementation currently makes it impossible to store them on the overlay). However, it can not be directly modified in the editor; typically data is pre-loaded into it at the beginning of a game using MZMs. The vlayer has the added benefit of direct counter access for character and color data stored in it, something that still requires a holdover kludge from MZXak when dealing with the overlay and the board proper. Its use as a larger storage space for data than the board itself was rendered somewhat redundant in the port, however, with the introduction of arbitrarily large boards. The vlayer is still useful for graphical storage, though, inasmuch as it is faster than copying from the board or the overlay, incurs much less performance penalty when made very large (playing on a very large board will cause a noticeable slowdown in code intensive gameplay, while a very large vlayer will not), and is a global space.
VLayer Tutorial
Working with the vlayer is not really that difficult as long as you can keep track of where you've put stuff on it. The biggest pitfall in a vlayer engine is misplacing the data on it, or accidentally writing over it. So it's a good idea when working with it in a full-fledged game to keep some sort of diagram of what's supposed to be on it and where, so you don't get lost. You can allocate as much space as you practically need to work with, but it's generally a good idea not to allocate significantly more than you need. So with that in mind, there are really only a handful of commands and counters you need to learn.
- Resizing the Vlayer
The default size of the vlayer is 32768 characters, 256 wide by 128 high. This used to be fixed, but can now be reallocated as needed, up to 16 MB worth (attempting to allocate more than this simply won't work, to avoid strangling the computer's memory). The counters to do this are:set "vlayer_size" to SIZE # Assigns the absolute size of the vlayer. Do this first. set "vlayer_width" to WIDTH # Then, set one of the dimensions to allocate the space in 2D. set "vlayer_height" to HEIGHT # The other dimension is set automatically to the largest value that will fit in the space.
- Loading Stuff onto the Vlayer
Putting large amounts of data onto the vlayer (usually images, but it can be other things too) is best done by saving it out to an MZM beforehand, and putting it at the desired location when the game or engine starts. The MZM article covers this syntax in more detail, but the command you want is: put "@filename.mzm" Image_file p02 at X Y # p02 specifically puts the MZM on the vlayer. - Copying Stuff to and from the Vlayer
The vlayer is inherently invisible to the player, and there are really only two ways to see what's on it. The first of these is to use the copy (overlay) block command to copy something on the vlayer to the board or the overlay. This is done using a hash symbol (#) as a special prefix for coordinates that are on the vlayer. This is a little complicated to explain but ends up being fairly intuitive; the most important thing is that counters have to be interpolated to work right. This is best understood through examples, rather than explanation: copy block at 1 2 for 3 4 to "#5" "#6" # Copying from the board to literal coordinates on the vlayer. copy overlay block at "#&x1&" "#&y1&" for "w" "h" to "x2" "y2" # Copying from x1, y1 on the vlayer to x2, y2 on the overlay. copy block "#1" "#2" for 3 4 to "#('x'+5)" "#('y'+6)" # Copying on the vlayer using expressions. Fun fact: you can copy directly from the board to the overlay and vice versa by using an appropriate copy block command, and a plus (+) in place of a hash to represent the opposite layer. Also, a word about behavior when copying between the board and the overlay/vlayer: copying from the board necessarily loses information, since the overlay and vlayer only store color and character information and the board can store much more. So, copying from the board to another layer will copy an image of the board exactly as it appears. Conversely, copying from the vlayer or overlay to the board will create customblocks, since all tiles on the board have to have some sort of type. - Saving Stuff from the Vlayer
Occasionally, you will want to save something on the vlayer out to an MZM file during the game. This is done using a combination of the MZM saving syntax and the vlayer coordinate reference syntax, with copy block. If you've read the MZM article it should be fairly obvious how these work together, but for completeness: copy block at "#&X&" "#&Y&" for "W" "H" to "@filename.mzm" 1 # The 1 is for a layer type MZM. - Using Sprites with the Vlayer
Sprites are covered in much more detail in their own article, but integrating them with the vlayer is extremely simple. set "spr#_vlayer" to 1 # The sprite image data will be taken from the vlayer instead of the board. - Referencing the Vlayer Directly
Like the overlay, the vlayer is essentially a 2 dimensional array of character and color data. Unlike the overlay, the vlayer can be poked at directly, without the need to go through intermediate counters. MZX provides the following two counter constructs to do this: "vchX,Y" # 'vlayer character', with X and Y as coordinate values and , as a literal separator. "vcoX,Y" # 'vlayer color', with X and Y as coordinate values and , as a literal separator. The X and Y values for each of these can be constructed using expressions and counter interpolation techniques. The counters can be read from and written to, and handle values from 0 to 255.
And that's about all you need to know to work with the vlayer.
Putting It All Together: A Vlayer Status Bar
This exercise will be similar to the one in the sprite tutorial, but much abbreviated in order to highlight the use of operations specific to the vlayer. In order to get the most out of it, you should already be familiar with the very basics of sprites, as well as expressions, MZMs, subroutines, and strings.
- The first thing we need to do is draw some graphics for our status bar. So start editing a new MZX world, and add a board called "vlayer". Probably the easiest way to "edit" the vlayer is to create a board where you can edit graphics as you desire, and add a simple robot to save the board as an MZM, so that's what we're going to do.
- copy block at 0 0 for "board_w" "board_h" to "@vlayer.mzm" 1
- You will want to keep track of where all of these things are going to be when the MZM is imported to the vlayer in the game. The easiest way to do this is to actually assign those numbers to counters in our status bar robot, so that we don't have to remember what they are every time you need to use them in the robot. Start writing your status bar robot like this:
- set "statbar_x" to 0
- set "statbar_y" to 1
- set "statbar_width" to 50
- set "statbar_height" to 1
- set "health_x" to 10
- set "health_width" to 25
- set "health_srcx" to 0
- set "health_srcy" to 2
- set "ammo_x" to 43
- set "ammo_width" to 5
[ Health: ========================= Ammo: 0____ ]
You can of course add more stuff to the status bar if you like, using the techniques demonstrated in this exercise. Just make sure to keep track of where everything is. - Now to get the statusbar displaying. Once you're happy with the graphics you've drawn, test the board with ALT+T to export the MZM. Now make a new board to play on, and put your status bar robot there. We need to have it import the MZM, set up a sprite for the status bar, and draw it on the board. So:
- put "@vlayer.mzm" Image_file p02 at 0 0
- set "spr0_refx" to "statbar_x"
- set "spr0_refy" to "statbar_y"
- set "spr0_width" to "statbar_width"
- set "spr0_height" to "statbar_height"
- set "spr0_vlayer" to 1
- set "spr0_static" to 1
- put c?? Sprite p00 at 15 24
- Keeping the status bar properly updated is going to involve doing a few separate things in a loop each cycle. We need to clear the slate and draw everything fresh, draw the health bar, and draw the ammo count (and perhaps more than that depending on your customization). The best way to tackle a large task is to divide it into smaller tasks, and the best way to do that in MZX is with subroutines. So, make a loop:
- : "loop"
- goto "#redraw"
- goto "#health"
- goto "#ammo"
- wait for 1
- goto "loop"
- : "#redraw"
- put "@vlayer.mzm" Image_file p02 at 0 0
- goto "#return"
- To do health, we want to copy a portion of the full health bar on top of the empty area on the status bar, relative to the ratio of the player's current health to the maximum possible health. To do this, we'll want to use expressions, so let's do some math. First, a ratio is just a fraction, so the number we want is 'health'/'maxhealth'. The counter 'maxhealth' isn't actually defined, though we can define it if we want. But it defaults to 200, so we'll use that for now. Then, we want to multiply this fraction by the width of the total health bar, which is stored in the counter 'health_width'. This gives us 'health'/200*'health_width'. However, MZX can only work with integers, not floats, and it performs all operations from left to right. If we leave things like this, the result will almost always be 0, since that's the result of an integer division of two numbers when the numerator is smaller than the denominator. To fix this, we need to multiply before we divide. The operation is mathematically equivalent, but makes a huge difference, since it allows us to divide a large number by a small one. This leaves us with:
- ('health'*'health_width'/200)
- : "#health"
- copy block at "#&health_srcx&" "#&health_srcy&" for "('health'*'health_width'/200)" 1 to "#&health_x&" "#&statbar_y&"
- goto "#return"
- Now for the ammo. Here, we want to write out the value of the ammo counter into the space set aside on the status bar. If the status bar were on the overlay, we could simply perform a "write overlay" command to do this, but unfortunately the vlayer doesn't support this operation. We could use math to dissect the counter digit by digit, but there's actually an easier way to get what we want using a neat feature of strings. Notably, we can convert a number into a string with a simple set command:
- set "$ammo" to "&ammo&"
- : "#ammo"
- set "$ammo" to "&ammo&"
- loop start
- set "vch('ammo_x'+'loopcount'),('statbar_y')" to "$ammo.&loopcount&"
- set "vco('ammo_x'+'loopcount'),('statbar_y')" to 31
- loop for "('$ammo.length'-1)"
- goto "#return"
And that's it for the status bar. Draw some stuff on your gameplay board to test it out, including ammo, health, and enemies. The final code for the status bar robot looks like this:
set "statbar_x" to 0 set "statbar_y" to 1 set "statbar_width" to 50 set "statbar_height" to 1 set "health_x" to 10 set "health_width" to 25 set "health_srcx" to 0 set "health_srcy" to 2 set "ammo_x" to 43 set "ammo_width" to 5 put "@vlayer.mzm" Image_file p02 at 0 0 set "spr0_refx" to "statbar_x" set "spr0_refy" to "statbar_y" set "spr0_width" to "statbar_width" set "spr0_height" to "statbar_height" set "spr0_vlayer" to 1 set "spr0_static" to 1 put c?? Sprite p00 at 15 24 : "loop" goto "#redraw" goto "#health" goto "#ammo" wait for 1 goto "loop" : "#redraw" put "@vlayer.mzm" Image_file p02 at 0 0 goto "#return" : "#health" copy block at "#&health_srcx&" "#&health_srcy&" for "('health'*'health_width'/200)" 1 to "#&health_x&" "#&statbar_y&" goto "#return" : "#ammo" set "$ammo" to "&ammo&" loop start set "vch('ammo_x'+'loopcount'),('statbar_y')" to "$ammo.&loopcount&" set "vco('ammo_x'+'loopcount'),('statbar_y')" to 31 loop for "('$ammo.length'-1)" goto "#return"