Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

2. Scripting System Sticky Locked

A topic by geod created Aug 19, 2020 Views: 1,326
This topic was locked by geod Aug 22, 2020
Viewing posts 1 to 1
(27 edits)

Overview

Basic Concepts

  • Shape: in-game meaningful graphical object - mario, cloud, pipe, bullet, brick etc...
  • 2dShape: the 2d representation of Shape, that’s what we see in the original game. It is equivalent to a grid of 8x8px tiles.
  • 3dShape: the 3d representation of Shape in real world which will created and rendered in the emulation process. 3dShape is not a unique 3d object, it is a set of 3d object(3dTile), each one correspond to a 8x8px tile (2dTile)
  • Pattern: first of all, a Pattern is a Shape without a fixed position.
    • It will be compatible with a class of Shape which contains the same tileset and have the same tile constraint set. Examples of diffrent shapes belong to the same pattern in SMB: pipes with different height, clouds with diffrent sizes. 
    • Tile constraint set is the info of which tile is located on the right/bottom of which tile.
    • A pattern contains a 2dPattern(2dShape) and a set of 3dPattern(3dShape). Normally a 2dShape will be interpreted as a 3dShape but in some cases it will be interpreted as some 3dShapes. 
  • A shape will be compatible with a pattern if its tileset is a sub set of 3dPattern tileset and 
  • 2dPattern: is a 2dShape without a fixed position.
  • 3dPattern: 3dShape without a fix position. 3dPattern is not defined by a set of parameters - with it we can create a set of 3dTile procedurally from a 2dPattern. Given a 3dPattern each tile(2dTile) from the 2dPattern will be linked to a 3dTile. It also contains a list of tags. 
  • Script: a script is  set of Lua function with a name and a list of tags. If a script and a 3dShape has a common tag then they are linked together and we can use this script to customize the 3dShape each frame.
  • 3d Profile: a list of pattern and script work together to guide the 3dification process of a specific game.

Scripting System

A script is a  set of special Lua function which is executed to customize 3dShape by modifying its parameters. A script is identified by a name and has a list of tags.

To hook itself into the 3dification process, a scripts must implement one or some of following special functions:

  • Start(): after loading the rom or the save state, all Start functions will be executed.
  • End(): right before finishing the emulation process of a given game, all End function will be executed.

At each frame, following functions will be executed in order:

  • Update: all Update functions will be executed first.
  • UpdateS: if a script is linked with a  3dShape (share a same tag) then the global variables shape and shape2D will be assigned to this 3dShape and its corresponding 2dShape then the UpdateS function of this sceipt will be executed.
  • LateUpdate: all LateUpdate function will be executed  

The special tag "*" means that this UpdateS function will be linked to all 3dShape of the current frame.

Script code can access and modify setting and the current list of 3dShape via various global variables:

  • variable shape of type Pattern3D (shape3D and pattern3D have the same type)  and variable shape2D of type Shape. shape and shape2D are only  determinist in UpdateS scope.
  • variable Script of type ScriptManager
  • variable Frame of type FrameManager (contains all 3dShapes of the current frame)
    • variable frame - the frame counter, shortcut of Frame.frameCounter
    • variable time – current game time in second, shortcut of Frame.time
  • variable Setting of type SettingManager
    • variable Light - shortcut of Setting.Light
    • variable Layer - shortcut of Setting.Layer
    • variable Clipping - shortcut of Setting.Clipping
  • variable Pattern of type PatternManager
  • variable Palette of type PaletteManager
  • variable Camera of type CameraControl
  • variable gamepad, gamepad1, gamepad2 of type Nespad3D. gamepad1 and gamepad2 contain the state of respective gamepads, gamepad state will return true if either gamepad1’s or gamepad2’s state return true.  

Lua variables are the wrappers of C# objects. To access a field or a property, we use the dot syntax:

 Variable.FieldName

To invoke a method, we use the ':' syntax

Variable:MethodName()

To invoke a static method, we use the normal dot syntax:

Variable.MethodName()

For example:

Setting.BgColor = false;
Setting:Save();
Pattern.GetPatternWithTag("character");

Scripting API

Utility

public struct Vector3 { public float x, y, z;}
public class PVector3 {public int x, y, z;}
public class PVector4 {public float x, y, z, w;}
public class PMatrix4x4 { public float m00, m01,m02, m03, m10, m11,m12, m13, m20, m21,m22, m23, m30, m31,m32, m33;}
public class Color {public float r, g, b, a;}
public enum ZLayer { UNIDENTIFIED, UI, L1, L2, L3, L4, COUNT };
public enum Geo3DType
{
    Default,
    HCYLINDER,
    VCYLINDER,
    CUBE,
    HALFHCYLINDER,
    HALFVCYLINDER,
};
public enum SelectionMode { Shape3D, Tile };
public enum Alignment { FRONT, CENTER, BACK };
public enum RenderAlgo { Greedy, Marching };

ScriptManager

Provide APIs to access to scripts and read, write game memory.

public class ScriptManager
{
    Script Get(string name); // get script with name
    byte ReadMem(ushort address); // read from nes memory
    void WriteMem(ushort address, byte value); // write to nes memory
}
public class Script
{
    string Name { get; } // script name
    bool Enable { get; set; } // if the script is allowed to run
    bool Error { get; } // if there is any error in script code – auto detect by 3dSen
}

Frame Manager

Provide APIs to access to 3dShapes in current frame buffer.

public class FrameManager
{
    List<Pattern3D> GetShapesWithTag(string tag); // return a list of shape having the tag “tag” in the current frame buffer
    Pattern3D GetShapeWithTag(string tag); // return the first shape having the tag “tag” in the current frame buffer
    Pattern3D GetShape(int index); // return the index-th 3DShape in the current frame buffer 
    int shapeCount { get; } // return the 3DShape count in the current frame buffer
    int frameCounter { get; } // return the current frame counter 
    float time { get; } // return time in second of current frame buffer
}

CameraControl

Provide APIs to control the camera.

public class CameraControl 
{
    public float Distance {get; set} // the distance from the camera to the root (0,0,0)
    public PVector3 Rot {get;} //the camera rotation via three axis
}

Nespad3D

Provides APIs to access gamepad state.

public class Nespad3D
{
    bool UpPressed { get; }
    bool DownPressed { get; }
    bool LeftPressed { get; }
    bool RightPressed { get; }
    bool SelectPressed { get; }
    bool StartPressed { get; }
    bool APressed { get; }
    bool BPressed { get; }
    bool RotLeftPressed { get; }
    bool RotRightPressed { get; }
    bool RotUpPressed { get; }
    bool RotDownPressed { get; }
    bool ZoomInPressed { get; }
    bool ZoomOutPressed { get; }
}

PaletteManager

Provides APIs to access color palette info of the current frame.  For each frame there are 4 color palette for sprite and 4 color palette for background tiles. Each color palette has 4 colors so totally there are 32 indexed color. Each indexed color will refer to one of 64 NES colors.

public class Nespad3D
{    
    //return the color of the ith color [0..31]
    public static Color FrameColor(int i); 
    // return the color index in the NES color table [0..63] of the ith color
    public static int FrameColorIndex(int i); 
}

Shape

Provide APIs to access properties of a 2dShape

public class Shape
{
    int Count { get; } // the number of shape3D linked with this 2dshape
    Pattern3D Shape3D(int index = 0);  // get the index-th shape3D linked with this 2DShape
    IntVector2 TStart { get; } // start (top left) position in tile unit (8 pixels)
    IntVector2 TEnd { get; } // end position (bottom right) position in tile unit
    IntVector2 TSize { get; } // shape size in tile
    IntVector2 Start {get;} // start (top left) position in pixel
    IntVector2 End; // end position (bottom right) position in pixel
    IntVector2 Size { get; } // shape size in pixel
    bool Bg { get; } // true if created by background tiles
    bool Hiden { get; } // true if one of one of its sprite tiles is hidden 
    int Palette { get; } // palette index 
    int TileCount { get; } // tile count
    bool Completed { get; } // true if the shape has every tile in the pattern tile set 
    bool Inside { get; } // true if the shape is completely located inside the screen 
    bool AtBorder { get; } // true if shape lies on a screen border
    int Age {get;} // the current amount of time - in frame unit - that the linked pattern has at least one linked shape in output frame
}

Pattern3D

Provide APIs to access properties of a 3dShape/3dPattern

public class Pattern3D
{
    Shape Shape2D { get; } // the corresponding 2dshape
    int Index { get; } // its order in its linked 2dshape's 3dshape list 
    ZLayer Layer { get; set; } // shape layer
    bool UI { get; set; } // return true if Layer is UI, otherwise return false, setting it to true corresponds setting Layer to ZLayer.UI
    PVector3 Scale { get; }
    PVector3 Rot { get; }
    PVector3 Offset { get; }
    PVector3 Pivot { get; } // the pivot of scale and rotation operation float 
    Alpha { get; set; } // shape transparency 
    PVector3 DeformAmp { get; }
    PVector3 DeformPivot { get; }
    PMatrix4x4 DeformSpeed { get; }
    PMatrix4x4 DeformOffset { get; }
    bool Enable { get; set; } // if true, the shape will be rendered 
    bool Sp { get; } // return true if this is a sprite shape
    bool Bg { get; } // return true if this is a background shape 
    bool CastShadow {get; set} // shape will cast shadow or not
    bool ReceiveShadow {get; set} // shape will receive shadow or not
    Vector3 OriPos { get; } // the original shape position, calculated based on BottomLeft, TopRight and Pivot 
    Vector3 BottomLeft { get; } // the original bottom left coordination in pixel
    Vector3 TopRight { get; } // the original top right coordination in pixel
    Vector3 GetDeform(Vector3 pos); // return the deform vector at "pos" position based on Deform parameters and formula
    bool ContainsTag(string tag); // return true if shape contains the tag
    int CData[]; // 8 custom data of each pattern
}

SettingManager

Provide APIs to access setting parameters.

public class SettingManager
{
    public SelectionMode SelectionMode {get; set;}
    public class LightConfig
    {
        public Vector3 Direction {get; set;}
        public Color Color {get; set;}
        public float Intensity {get; set;}
    }
    LightConfig Light { get;}
    bool BgColor {get; set;} //if true, use the game background color else use CustomBgColor 
    Color CustomBgColor {get; set;}
    PVector4 Clipping {get;} //amount of clipped pixel at four borders Left, Bottom, Right, Top 
    bool MapEnable {get; set;} // show or hide the mini map 
    public class LayerP
    {
        public float Offset {get; set}
        public Alignment Aligned {get; set;}
    }
    public class LayerConfig
    {
        public LayerP this[int index] {get;}
        public ZLayer DefLayer {get; set;}
        public bool Auto {get; set;} // if true, 3dSen will auto assign layer for pattern if possible
    }
    LayerConfig Layer {get;}
    int GraphicQuality {get; set;} // valid values: 0, 1, 2
    int VrMode {get; set;} // 0: None, 1: OpenVr, 2: Oculus
    int VSync {get; set;} // enable or disable vsync
    int FrameRate {get; set;} // the frame buffer count per second if VSync is false float 
    GameSpeed {get; set;} // from -4 to 4, 0 is normal speed
    float Volume {get; set;} // from 0 to 1
    RenderAlgo RenderMode {get; set;}
    bool Titling {get; set;} // camera titling effect while pressing up, down, left, right button 
    bool Shadow {get; set;} // enable or disable shadow rendering at global level bool 
    SamePalette {get; set;} // if true, tiles in a shape must have the same palette
    int SpriteTolerance {get; set;} //be default sprite tiles in a shape must be 8px aligned but we could set a tolerance value to this constraint using this property
    bool EmptyTile {get;set;} // EmptyTile = false will filter empty tiles (tiles with all zero indexed color pixels)
    string Save2String(); // save setting to string
    void Load(string s); // load setting from string
    void Save(); // save setting to the permanent storage
    void Load(); // load setting from the permanent storage
}

Examples

Super Mario Bros - UI

In Super Mario Bros we want the status bar at the top of the screen to be always visible, and perpendicular to camera view. That can be done by set every involved pattern to layer UI. The issue is when the same shape is positioned in another area , we don’t want it in UI layer at all. So the static layer assignment won’t work. What we want here is if the shape is high enough (that means it is in the status bar region), its layer should be set to UI. That can be done by a small script tagged “*”:

function UpdateS()
    if shape.BottomLeft.y > 216 then
        shape.UI = true
    end
end

Legend Of Zelda – UI

The UI in Legend of Zelda is a little bit different. Normally the UI part is located at the top but when the start button is pressed, the UI will scroll down and show its full content. That means the UI region position isn’t fixed. To deal with it, we choose a 3dPattern that only appears at the bottom of UI region and doesn’t appear anywhere else, that 3dPattern will be tagged “ui_bottom”. Then we add a script with the tag "*".

function Start()
    saveInt = Light.Intensity
    saveDir = Light.Direction
    Light.Direction = Vector3(0.3, 0.3, 1)
    Light.Intensity = 0.7
end
function Update()
    ui_bottom = Frame:GetShapeWithTag("ui_bottom");
    if ui_bottom then
        bottom = ui_bottom.BottomLeft.y
    else
        bottom = 1000
    end
end
function UpdateS()
    if shape.BottomLeft.y >= bottom then
        shape.UI = true
    end
end
function End()
    Light.Intensity = saveInt
    Light.Direction = saveDir
end

Rotation effect

The code below is a script with a “rot” tag.

function UpdateS()
    shape.Rot.y = frame * 2;
end

If we want any shape to rotate around Y axis continuously, just assign tag “rot” for it.

Day-night effect

function Update()
    f = frame % 600
    if (f < 200) then
        Light.Intensity = 0.5
    else
        Light.Intensity = 1.2
    end
end

This script changes the light intensity periodically to emulate the day/night effect.