Skip to main content

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

4. Useful and reusable scripts Sticky

A topic by geod created Aug 20, 2020 Views: 1,966 Replies: 7
Viewing posts 1 to 12
(3 edits) (-1)

Useful scripts that have been being reused many times in different 3dprofiles.

Adding meta data

function Start()     
    Profile.Maker = "Geod" -- your name     
    Profile.Title = "Super Mario Bros." -- Game Name     
    Profile.GameDev = "Nintendo" -- Game Developer 
end
(3 edits)

Invisible effect. 

When a NES game want to temporary hide an on-screen object it often modify all colors of this object palette to the background one. The trick doesn't work really well with 3dSen because of depth, lighting and shadow are always still there. To regenerate this effect, you can link the target shape pattern with the following script:

function UpdateS()
    local p = shape.Palette
    local c1 = Palette.FrameColorIndex(p * 4)
    local c2 = Palette.FrameColorIndex(p * 4 + 1)
    local c3 = Palette.FrameColorIndex(p * 4 + 2)
    local c4 = Palette.FrameColorIndex(p * 4 + 3)
    shape.Enable = c1 ~= c2 or c2 ~= c3 or c3 ~= c4
end
(2 edits)

Rotation effect.

In many situations, we will want to add a little animation to the object by make it self-rotate. We can do it with the below script:

function UpdateS()
    shape.Rot.x = time * shape.CData[0]
    shape.Rot.y = time * shape.CData[1]
    shape.Rot.z = time * shape.CData[2]
end

Tag the pattern we want to self-rotate and set the angular speed via three axes in CData 0, 1, 2

(6 edits)

Multi-layer interpretation solution

NES games have only one background layer. Therefore in games with multiple graphical layers object in the front layer will completly hide the one in the back layer or both objects apperance will be mixed in the same tiles. However we can interpolate the missing graphic of the back layer based on surround shapes to create the correct multi-layer representation. To do it we use a pair of script tagged "bg" and "bg1".  The surround shapes will be tagged "bg" and the target 3dShape will be tagged "bg1" and play the role of a "place holder". 

- "bg" script:  pattern parameters used in the script:

  • CData[1]: if the value is different than 0 then the shape texture will not be used as background  texture in case there is different shapws in the bg region/slot
  • CData[2] : the tolerance delta deternimes if a background shape belongs to a backgroun region/slot. If this value is -1 then the shape will be treated like the sole bg region in the current frame
function Start()
    bgSlot = {}
    MaxSlot = 10
    for i = 1, MaxSlot do
        bgSlot[i] = {}
    end
end
function End()
    bgSlot = nil
end
function Update()
    bgCount = 0
    bgOne = 0
end
function Connect(s, slot, delta)
    local r = s.Palette == slot.shape.Palette and s.tStart.x <= slot.ex + delta and slot.sx <= s.tEnd.x + delta and s.tStart.y <= slot.ey + delta and slot.sx <= s.tEnd.x + delta;
    return r;
end
function UpdateS()
    if bgOne == 1 then
        return
    end
    if shape.Bg then
        if shape.CData[2] == -1 then
            bgOne = 1
            bgCount = 1
            local slot = bgSlot[1]
            slot.sx = -1
            slot.sy = -1
            slot.ex = 33
            slot.ey = 31
            slot.delta = 0            
            slot.shape = shape
            return
        end
        for i = 1, bgCount do
            local slot = bgSlot[i]
            local delta = max(slot.delta, shape.CData[2])
            if Connect(shape2D, slot, delta) then
                slot.sx = min(slot.sx, shape2D.tStart.x)
                slot.sy = min(slot.sy, shape2D.tStart.y)
                slot.ex = max(slot.ex, shape2D.tEnd.x)
                slot.ey = max(slot.ey, shape2D.tEnd.y)
                slot.delta = delta
                if slot.shape.CData[1] ~= 0 then
                    slot.shape = shape
                    slot.delta = delta
                end
                return;
            end            
        end
        if bgCount < MaxSlot then
            bgCount = bgCount + 1;
            local slot = bgSlot[bgCount]
            slot.shape = shape
            slot.sx = shape2D.tStart.x
            slot.sy = shape2D.tStart.y
            slot.ex = shape2D.tEnd.x
            slot.ey = shape2D.tEnd.y
            slot.delta = shape.CData[2]
        end
    end
end
function min(a,b)
    if a < b then
        return a
    end
    return b
end
function max(a,b)
    if a > b then
        return a
    end
    return b
end

- "bg1" script: pattern parameters used in the script:

  • CDatap[0] : bg texture will have this offset while applying to this place holder
function In(shape2D, bgSlot)
    return shape2D.tStart.x + 1 >= bgSlot.sx and shape2D.tStart.y + 1 >= bgSlot.sy and shape2D.tEnd.x <= bgSlot.ex + 1  and shape2D.tEnd.y  <= bgSlot.ey + 1
end
function Start()
    bg1List = {}
    bg1Count = 0
end
function Update()
    for i = 1,bg1Count do
        bg1List[i] = nil
    end
    bg1Count = 0
end
function UpdateS()
    if shape.Bg then
        bg1Count = bg1Count + 1
        bg1List[bg1Count] = shape
    end
end
function LateUpdate()
    for j = 1,bg1Count do
        local select = nil
        local shape = bg1List[j]
        for i = 1, bgCount do
            local _bgSlot = bgSlot[i]
            if  shape.Shape2D ~= _bgSlot.shape.Shape2D and In(shape.Shape2D, _bgSlot) and (not select or (select.ey - select.sy > _bgSlot.ey - _bgSlot.sy)) then
                select = _bgSlot
            end            
        end
        if select then
            shape.Enable = true            
            local bgShape = select.shape            
            shape.customPalette = bgShape.Palette
            shape.Layer = bgShape.Layer
            shape.Offset.z = bgShape.Offset.z
            shape.CastShadow = bgShape.CastShadow
            shape.RecvShadow = bgShape.RecvShadow
            shape.slot = bgShape.slot
            shape.texOffset.x = shape.CData[0]
            shape.Scale.z = bgShape.Scale.z * bgShape.sizeZ / shape.sizeZ
            shape.Alpha = bgShape.Alpha
            shape.Pivot.z = 0    
        else 
            shape.Enable = false        
        end
    end
end
(1 edit)

Multi-depth platforming solution

There are games like SMB3 or Chip&Dale Rescue Ranger where it makes more sense to position platforms at different depths in 3d scene. How to calculate all the platform depths and moreover characters's depth who are sitting on top of those platforms? Obviously this is a dynamic problem that only could be solved after having the 2d output of each scene. The addopted solution of 3dSen is addopted in the video bellow with the help of two script tagged "pf" and "char"

- "pf" script:  pattern parameters used in this script

  • DeformSpeed.m00 : the z offset of the upper platform compared to its lower one
function Start()
    pfList = {}
end
function End()
    pfList = nil
end
function Update()
    for i = 1, #pfList do
        pfList[i] = nil
    end
    pfs = Frame:GetShapesWithTag("pf")
    for i = 0, pfs.Count - 1 do
        pfList[i + 1] = pfs[i]
        pfList[i + 1].Pivot.z = 0
    end
    if #pfList > 0 then
        UpdatePf()
    end
end
function Compare(s1, s2)
    if s1.TopRight.y < s2.TopRight.y then
        return true
    else 
        return false
    end
end
function max(a,b)
    if a > b then
        return a;
    else
        return b;
    end
end
function UpdatePf()
    --print(#pfList)
    table.sort(pfList, Compare)
    for i = 1, #pfList -1 do
        pfI = pfList[i]
        for j = i + 1, #pfList do
            pfJ = pfList[j]
            if pfI.TopRight.y + 16 > pfJ.BottomLeft.y and pfI.BottomLeft.x <= pfJ.TopRight.x and pfI.TopRight.x >= pfJ.BottomLeft.x then
                if pfJ.Offset.z  < pfI.Offset.z + 8 then
                    pfJ.Layer = pfI.Layer
                    pfJ.Offset.z = pfI.Offset.z + pfJ.DeformSpeed.m00
                    if pfJ.Bg then
                        pfJ.Scale.z = pfI.Scale.z - pfJ.DeformSpeed.m00 / pfJ.sizeZ 
                    end
                end
            end
        end 
    end
end

- "char" script: pattern parameters used:

  • DeformSpeed.m10 : the delta value help determine if a char in on top of a platform
function UpdateS()
    if #pfList == 0 then
        return
    end
    --print(#pfList)
    local pfNear = nil
    local yNear = 0
    local y = shape.BottomLeft.y
    local x1, x2 = shape.BottomLeft.x, shape.TopRight.x
    for i = 1, #pfList do
        s = pfList[i]
        if s.TopRight.y - s.DeformSpeed.m10 <= y and s.BottomLeft.x <= x2 and s.TopRight.x >= x1   then
            if pfNear == nil or yNear < s.TopRight.y then
                pfNear = s
                yNear = s.TopRight.y
            end
        end
    end
    if pfNear ~= nil then
        shape.Offset.z = shape.Offset.z + pfNear.Offset.z
    end
end
(4 edits)

Scene tracking & camera management script

Generally a games will contain many diffrent "scenes"  such as  "start screen" scene, cut scene,  gameplay scene, transition scene with score gain sumery ... Each scene will probably need different dynamic adjustment so we need a way to track which scene our current frame belongs to. The "scene" script below will help us with this task.

function Start()     
    scene = -1     
    sceneChanged = false     
end  
function Update()    
    _scene = 0  
end  
function UpdateS()     
    if _scene < shape.CData[7] then
        _scene = shape.CData[7]     
    end 
end  
function LateUpdate()     
    if scene ~= _scene then
         sceneChanged = true
         scene = _scene     
    else      
       sceneChanged = false     
    end 
end

For each scene determine a pattern which only appears on it and give it a "scene" tag and assign a scene id number to its CData0. That way the variable "scene" will alway refer to the current scene.

function Start()
     camRot = Camera.Rot
     oldRot, oldRot1 = {x=0, y = 0}, {x=-65,y = 0}
 end
function LateUpdate()
     if sceneChanged then
         Setting.TopDownCam = scene == 1
         if scene == 0 then
             camRot.x = oldRot.x
             camRot.y = oldRot.y
         else
             camRot.x = oldRot1.x
             camRot.y = oldRot1.y
         end
     else if scene == 0 then
             oldRot.x = camRot.x
             oldRot.y = camRot.y
         else
             oldRot1.x = camRot.x
             oldRot1.y = camRot.y
         end
     end
end 
function Save()
    PData[0] = oldRot.x
    PData[1] = oldRot.y
    PData[2] = Camera.Distance
    PData[3] = oldRot1.x
    PData[4] = oldRot1.y
 end  
function Load()
     oldRot.x = PData[0]
     oldRot.y = PData[1]
     Camera.Distance = PData[2]
     oldRot1.x = PData[3]
     oldRot1.y = PData[4]
end
locked this topic
unlocked this topic

Star Flow Effect

function Start()
    stars = {}
end
function Update()
    for i = 1, #stars do
        stars[i] = nil
    end
    minx, miny, maxx, maxy = 10000, 10000, 0, 0
end
function UpdateS()
    stars[#stars+1] = shape
    local pos = shape.OriPos
    minx = math.min(minx, pos.x)
    miny = math.min(miny, pos.y)
    maxx = math.max(maxx, pos.x)
    maxy = math.max(maxy, pos.y) 
end
function LateUpdate()
    if #stars > 3 then
        --local cx = (minx + maxx) / 2
        --local cy = (miny + maxy) / 2    
        local delta = (maxx + maxy - minx - miny) / 3
        --print(cx .. " - " ..  minx .. " - " .. maxx)
        for i = 1, #stars do
            local shape = stars[i]
            local ofs = shape.Offset
            ofs.z = ofs.z - delta
        end
    end
end
function End()
    stars = nil
end
locked this topic
unlocked this topic
(1 edit)

Overlap Script

Effect: When the platform overlaps a character or a bullet, it will become semi-transparent.

Usage: tag all platform and character/bullet you want to have this effect with the same tag of this script

function Start()
    list = {{},{}}
    list1 = list[1]
    list2 = list[2]
end
function Update()
    for i = 1,#list1 do
        list1[i] = nil
    end
    for i = 1,#list2 do
    list2[i] = nil
    end
end
function UpdateS()
    scene = 1
    local index = shape.Bg and 1 or 2
    local tlist = list[index]
    tlist[#tlist+1] = shape
end
function LateUpdate()
    if #list2 > 0 then
        --print(#list1)
        for i = 1,#list1 do
            obj1 = list1[i]
            for j = 1,#list2 do
                if obj1:Overlap(list2[j]) then
                    obj1.Alpha = 0.5
                    break
                end
            end
        end
    end
end