TGB/PlatformerStarterKit/Parallax Layers

From TDN

Parallax Layers (Alternative Method)

I wanted to provide an alternative Parallax Layer method for use in the kit. The method that is provided (through the use of the ParallaxBehavior on a scroller) works well for small scroll values, but is a bit jittery with higher scroll speeds. This method takes advantage of TGB's GUI system and implements multiple t2dSceneWindows, whereby multiple levels can be loaded and projected.

Create a new file called "ParallaxMethods.cs" and place it into your game/gamescripts folder. Open it up and paste the following script into the document:

/// New layers can be specified in the dynamic fields of your level's scenegraph
/// FieldName:  "ParallaxLayerX", where X is an integer >= 0
/// FieldValue: <TYPE> <LEVEL_NAME> <ZOOM_LEVEL>
/// Note: Must be called AFTER a level is loaded into the main scenewindow!
function initialiseParallaxLayers()
{
    // Current scenegraph
    %sceneGraph = sceneWindow2D.getSceneGraph();

    // Clear background layers
    for (%i = 0; isObject("ParallaxBackground" @ %i); %i++)
        ("ParallaxBackground" @ %i).delete();
 
    // Clear foreground layers
    for (%i = 0; isObject("ParallaxForeground" @ %i); %i++)
        ("ParallaxForeground" @ %i).delete();
    
    // Loop through the specified fields
    for (%i = 0; %sceneGraph.getFieldValue("ParallaxLayer" @ %i) !$= ""; %i++)
    {
        // Get the properties and add the new layer
        %layerProperties = %sceneGraph.getFieldValue("ParallaxLayer" @ %i);
        newParallaxLayer(getWord(%layerProperties, 0),
                         getWord(%layerProperties, 1),
                         getWord(%layerProperties, 2));
    }
}

/// Create a new parallax layer
/// Type:      "BG" for a background layer
///            "FG" for a foreground layer
/// LevelName: Level file to load into the window
/// ZoomLevel: Camera zoom, < 1.0 for background, > 1.0 for foreground
///
/// Note: Layers are loaded in this format:
///          ** MiscBackgroundGUIControls
///          ParallaxBackgroundX
///             ...             ...
///          ParallaxBackground0
///          scenewindow2D
///          ParallaxForeground0
///             ...             ...
///          ParallaxForegroundX
///          ** MiscForegroundGUIControls
function newParallaxLayer(%type, %levelName, %zoomLevel)
{
    // Level file
    %levelFile = "game/data/levels/" @ %levelName @ ".t2d";
    if (!isFile(%levelFile))
    {
        warn ("newParallaxLayer: Unknown level file \"" @ %levelFile @ "\"");
        return;
    }
    
    // Find the canvas content
    %mainScreen = Canvas.getContent();
    
    // Set up the way we create and position the type of parallax layer
    switch$ (%type)
    {
        // Background
        case "BG" : %layerType  = "ParallaxBackground";
                    %startIndex = 0;
                    %endIndex   = %mainScreen.getCount();
        
        // Foreground
        case "FG" : %layerType  = "ParallaxForeground";
                    %startIndex = %mainScreen.getCount();
                    %endIndex   = 0;
    }
    
    // If it is unknown then warn and quit
    if (%layerType $= NULL)
    {
        warn ("newParallaxLayer: Unknown layer type \"" @ %type @ "\"");
        return;
    }
    
    // Find the layer number for that type
    %layerIndex = 0;
    while (isObject(%layerType @ %layerIndex))
        %layerIndex += 1;
    
    // New window
    %sceneWindow = new t2dSceneWindow(%layerType @ %layerIndex)
    {
        // Position and size
        Position = "0 0";
        Extent   = %mainScreen.getExtent();
        
        // Ensure it always covers the entire window
        HorizSizing = "WIDTH";
        VertSizing  = "HEIGHT";
    };
    
    // Add it to the gui
    %mainScreen.add(%sceneWindow);

    // Ensure it is positioned nicely in the list
    for (%i = %startIndex; %i != %endIndex; %i += (2 * (%endIndex > %startIndex) - 1))
    {
        //
        %guiControl = %mainScreen.getObject(%i);
        
        // Skip it if it is the one we created
        if (%guiControl == %sceneWindow)
            continue;
        
        // Find the closest window
        if (%guiControl.getClassName() $= "t2dSceneWindow")
        {
            // Order it nicely
            %mainScreen.reorderChild(%sceneWindow, %guiControl);
            
            // Reorder foreground windows to be on top
            if (%startIndex > %endIndex)
                %mainScreen.reorderChild(%guiControl, %sceneWindow);
            
            // No need to continue looping
            break;
        }
    }
    
    // Load the prefs
    %sceneWindow.setCurrentCameraZoom(%zoomLevel);
    %sceneWindow.loadlevel(%levelFile);
    
    // Create a mount for the camera
    %cameraMount = new t2dSceneObject(%layerType @ "Mount" @ %layerIndex)
    {
        // It doesn't matter what scenegraph we use, as long as it is in one
        SceneGraph = sceneWindow2D.getSceneGraph();
        // Store the scene window
        SceneWindow = %sceneWindow;
    };
    
    // Mount the new camera to the blank scene object
    // Note: The way you mount these here should be the same way you mount
    //       the normal camera to the player. Otherwise things look squiffy!
    %sceneWindow.mount(%cameraMount, "0 0", 15, false);
    
    // Create a simset to store all the mounts
    if (!isObject(ParallaxLayerMounts))
        new SimSet(ParallaxLayerMounts);
    
    // Add it to the list
    ParallaxLayerMounts.add(%cameraMount);
}

Now open up the "GameMethods.cs" file and replace the following functions:

/// Mounts the scene camera to the object
function t2dSceneObject::mountCamera(%this)
{
    if (sceneWindow2D.getIsCameraMounted())
        dismountCamera();
    
    // Mount the camera to the objects
    sceneWindow2D.mountCamera(%this);
    
    // Mount parallax mounts
    if (isObject(ParallaxLayerMounts))
    {
        for (%i = 0; %i < ParallaxLayerMounts.getCount(); %i++)
        {
            %parallaxMount  = ParallaxLayerMounts.getObject(%i);
            %parallaxWindow = %parallaxMount.SceneWindow;
            
            // Mount the window to the mount
            %parallaxWindow.mountCamera(%parallaxMount);
            // Mount to the target
            %parallaxMount.mount(%this);
        }
    }
}

/// Dismounts the scene camera
function dismountCamera()
{
    if (!sceneWindow2D.getIsCameraMounted())
        return;
    
    // Dismount the camera from the object
    sceneWindow2D.dismount();
    
    // Dismount parallax mounts
    if (isObject(ParallaxLayerMounts))
    {
        for (%i = 0; %i < ParallaxLayerMounts.getCount(); %i++)
        {
            %parallaxMount  = ParallaxLayerMounts.getObject(%i);
            %parallaxWindow = %parallaxMount.SceneWindow;
            
            // Dismount the camera
            %parallaxWindow.dismount();
            // Dismount the mount
            %parallaxMount.dismount();
        }
    }
}

You can incorporate Parallax Layers using this method in two ways.

Firstly, add dynamic fields to your level's scenegraph (in the TGB editor) and then call the "initialiseParallaxLayers()" function. Secondly, add parallax layers manually using the "newParallaxLayer(%type, %levelName, %zoomLevel)" function.

The first method requires you to name the dynamic field in this format "ParallaxLayer<X>" where <X> is an integer greater than or equal to 0. For example "ParallaxLayer0", "ParallaxLayer1". You must load them in order from 0 to N (you can have as many as you like!) and they are loaded in order. Now that your field is named correctly, you must also format the value appropriately. The first word is the type of layer ("BG" for background, "FG" for foreground layer), the second word is the level name (does not include the directory hierarchy or the extension) and the third word must be the zoom level.

The zoom level is what gives the layer the sense of depth. Use a number < 1.0 for background objects (the smaller the zoom, the further away the layer appears) and > 1.0 for foreground layers (the larger the zoom, the closer the layer appears).

Important Note:

I would like to give a huge shout out to the guys at Syreal Games for putting me onto this method. It really does add a lot to the scene, however, I would like to give you the option to incorporate it into the kit. The pro's for this method is that it does make the scene look bigger and it also is very smooth, the con is that it is more resource hungry, so use them sparingly.