YACE64 Logo

The Commodore 64 Emulator with the extra dimension

Home Introduction Features Screenshots Download Links Help About
Commodore 64
www.icons8.com

Tutorials

Tutorial 07 - Sprite manipulations

This tutorial shows how sprites can be manipulated and explains the different styles. This is the same like manipulating the character display.
It is also shown, how the Commodore keyboard input can be captured in the script: Pressing the 0 to 4 button changes the displaymode.

This is the "Tutorial07" included in the Tutorial-Download.

Instructions

The main point of this "magic" is the OnSprite() function, which is calles for every sprite, that is to be displayed.
When this function is not present or just returns true, a default manipulation is applied as seen at the start of the video.

The spriteInfo gives us detailed information about the sprite (see SpriteInfo). In this case only the spriteIndex is needed to distinguish between our two sprited; in some cases we maybe also need the graphics address or the color (e.g. when sprite multiplexing is done).

Our first sprite just gets a static view mode. The second will depend on the variable voxelMode, which is set in the OnKeyboard() function and so controlled by the user.
Also the second sprite gets a special depth map, which has "ghost" shape. The depth-map defines the depth factor for each pixel of the sprite (or character).
A pixel with depth of 0.5 is just half as depth as a pixel with factor 1.0. The back and front side of a sprite (or character) can have different depth-map, so the shape of the sprite can be different for front and back-side.

const int MapEmptyId = 1;
grid<float> mapEmpty = { {0.0, 0.0},
{0.0, 0.0} };

const int MapGhostId = 2;
grid<float> mapGhost = { // a 24x21 depth-map
.... CODE REMOVED (see Tutorial-Download) ...
};

const int startProgramTimerId = 1;

float spriteRotationY = 0.0;
float spriteRotationYDir = -1.0;
float spriteRotationX = 0.0;
float spriteRotationXDir = -1.0;

bool enableSpriteManipulation = false;

Voxel voxelMode = Voxel::HardEdge;

// This is called, when we loaded the script-file
void OnLoad(bool reloaded)
{
    string scriptPath = APP.GetScriptPath();

    APP.ClearDepthMaps(); // Clear all loaded depth maps
    APP.ClearModels(); // Clear all loaded model

    // Set the view mode to the new 3D
    APP.ViewMode = ViewMode::Ext3D;

    // Set the ambient light color and brigtness
    APP.SetAmbientLight(0.3, 0.3, 0.3);
    // Set the direction of the light (from the middle off the scene (x), from top to bottom (y) and back to forth (z))
    APP.SetLightDir(0.0, -1.0, +1.0);

    // Position the scene, so we have the top/left in the middle of the screen
    E3D.SetScenePosition(0.44, -0.20, +0.01);
    // Rotate the scene a little bit, so we can see the 3D effect
    E3D.SetSceneOrientation(-25.0, -28.0, 0.0, 0.0); // yaw, pitch, roll in degree, speed

    if (!reloaded)
    {
        // Load our "game"
        APP.Load(scriptPath + "Sprites.prg", 0);
        SetTimer(startProgramTimerId, 120, false);
    }
}

bool OnSprite(int mode, SpriteInfo& spriteInfo, BlockManipulation& spriteManipulation)
{
    if (!enableSpriteManipulation)
        return(true);

    spriteManipulation.ambient = 0.35;

    if (spriteInfo.index == 0)
    { // Ghost 1
        spriteManipulation.voxelMode = Voxel::SmoothEdge;
        spriteManipulation.depthMapIndex = 0; // 0 is the default flat depth map
        spriteManipulation.reflective = 0.5;
    }
    else if (spriteInfo.index == 1)
    { // Ghost 2
        spriteManipulation.voxelMode = voxelMode; // Set the display mode to the user selected (see OnKeyboard())
        spriteManipulation.depthMapIndex = MapGhostId; // Use a special depth-map. Map is request with OnGetDepthMap()
        spriteManipulation.reflective = 16.0; // Set the right sprite to more shiny
    }

    spriteManipulation.zScale = 5.0; // some more depth for the sprites

    spriteManipulation.rotationAxis1 = Rotation::Y; // some animated rotation (see OnFrameEnd())
    spriteManipulation.rotation1 = spriteRotationY;
    spriteManipulation.rotationAxis2 = Rotation::X;
    spriteManipulation.rotation2 = spriteRotationX;

    spriteManipulation.zTranslation -= 0.08; // translate the sprites a little mit more in foreground

    return true;
}


void OnFrameEnd()
{
    // Just some simple rotation around x and y axis
    if (spriteRotationY < -80.0 && spriteRotationYDir < 0.0)
        spriteRotationYDir = 0.7;
    else if (spriteRotationY > 80.0 && spriteRotationYDir > 0.0)
        spriteRotationYDir = -0.7;

    spriteRotationY += spriteRotationYDir;

    if (spriteRotationX < -15.0 && spriteRotationXDir < 0.0)
        spriteRotationXDir = 0.1;
    else if (spriteRotationX > 15.0 && spriteRotationXDir > 0.0)
        spriteRotationXDir = -0.1;

    spriteRotationX += spriteRotationXDir;
}

void OnTimer(int timerId)
{
    if (timerId == startProgramTimerId)
    {
        APP.SendASCII("RUN\n");
    }
}

grid<float>& OnGetDepthMap(int mode, int index, bool front)
{
    switch (index)
    {
        case MapGhostId: return(mapGhost); // The only depth map we use
        default: break;
    }
    return(mapEmpty);
}

void OnKeyboard(bool pressed, Keys cbmKeyboard)
{
    if (pressed)
    { // Change the display mode of our second sprite by the key-input (keys 0 to 4)
        if (cbmKeyboard == Keys::N0)
        {
            enableSpriteManipulation = false;
        }
        else if (cbmKeyboard == Keys::N1)
        {
            enableSpriteManipulation = true;
            voxelMode = Voxel::HardEdge;
            GUI.ShowInfoMessage("\"Hard edge and block-style surface\" mode");
        }
        else if (cbmKeyboard == Keys::N2)
        {
            enableSpriteManipulation = true;
            voxelMode = Voxel::SmoothEdge;
            GUI.ShowInfoMessage("\"Smooth edge and block-style surface\" mode");
        }
        else if (cbmKeyboard == Keys::N3)
        {
            enableSpriteManipulation = true;
            voxelMode = Voxel::SmoothSurface;
            GUI.ShowInfoMessage("\"Hard edge and Smooth surface\" mode");
        }
        else if (cbmKeyboard == Keys::N4)
        {
            enableSpriteManipulation = true;
            voxelMode = Voxel::SmoothSurfaceEdge;
            GUI.ShowInfoMessage("\"Smooth edge and Smooth surface\" mode");
        }
    }
}