Vlayer

From MZXWiki
Jump to navigation Jump to search

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.

  1. 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
    Now, draw the basic graphics for your status bar, including a long horizontal bar for health (empty), and a small area to hold an ammo counter. You should also draw, not on the status bar itself, a health bar of the same length to represent full health. The biggest thing to keep in mind here is not to use the space character, 32, for anything you actually want to show up on screen. The vlayer doesn't draw it, just like the overlay. Other than that, go wild, draw whatever you like.
  2. 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
    This corresponds to a status bar that looks something like this:
    [ 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.
  3. 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
    The line about "spr0_static" is simply to make the sprite behave like a static overlay, so that it will remain at the bottom of the screen no matter where the player scrolls to. The coordinates 15,24 center the status bar with the dimensions I've provided on the bottom of the screen; you may need to change these numbers (or better, use an expression) to achieve the same effect if you've done yours differently.
  4. 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"
    We'll do the first part right now. Redrawing is easy, we simply reload the base vlayer image on top of everything again.
    : "#redraw"
    put "@vlayer.mzm" Image_file p02 at 0 0
    goto "#return"
  5. 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)
    Now, we need to copy a block of that width from the full health bar to the empty one, on the vlayer. Fortunately you've learned the commands to do that.
    : "#health"
    copy block at "#&health_srcx&" "#&health_srcy&" for "('health'*'health_width'/200)" 1 to "#&health_x&" "#&statbar_y&"
    goto "#return"
  6. 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&"
    The ampersands are necessary because the string set command takes what is literally written in the second argument (after interpolation is applied), and will not take the value of a counter. So, now that we have a string, we can loop through it and use its length and index features, along with the vchX,Y counter, to set the vlayer accordingly.
    : "#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"
    31 is color c1f, white on dark blue. Change this as you see fit. And see the loopcount article if you don't understand why it's necessary to subtract 1 from the string length.

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"