singe/testScripts/scene90.singe
2026-09-14 22:32:53 -05:00

246 lines
6.2 KiB
Text

-- Forge tutorial 8, "Into 3D": a scene3d layer, meshes for floors and a ledge, the kit's fox on
-- the character controller with a camera that follows, the 3D viewport, and a prize that is a
-- trigger. Starts from New game.
TUTORIAL = { number = 8, name = "08-scene", tag = "TUTORIAL8" }
dofile("testScripts/tutorialDrive.singe")
local frames = 0
local stage = 0
local game = nil
tutorialBegin(nil)
local function lightType(name)
local names = {}
local from, to = 1, 1
for typeName in pairs(FORGE.game.types) do
names[#names + 1] = typeName
end
table.sort(names)
for at, typeName in ipairs(names) do
if typeName == FORGE.typeName then
from = at
end
if typeName == name then
to = at
end
end
for _ = 1, math.abs(to - from) do
key((to > from) and SCANCODE.DOWN or SCANCODE.UP)
end
end
local function ruleGo(index, part)
local guard = 0
while ((FORGE.rule ~= index) or (FORGE.part ~= (part or 0))) and (guard < 60) do
local past = (FORGE.rule > index) or ((FORGE.rule == index) and (FORGE.part > (part or 0)))
key(past and SCANCODE.UP or SCANCODE.DOWN)
guard = guard + 1
end
end
-- A type with a mesh look: a box or a sphere of a size and a colour.
local function meshType(name, shape, w, h, d, r, g, b)
key(SCANCODE.MAIN_2)
key(SCANCODE.A)
form("name", name)
form("look", "mesh")
form("look.shape", shape)
form("look.w", w)
if h then
form("look.h", h)
end
if d then
form("look.d", d)
end
form("look.r", r)
form("look.g", g)
form("look.b", b)
end
-- An entity of the lit type, added and its place typed: in a scene, positions are world units.
local function placeAt(id, x, y, z)
key(SCANCODE.MAIN_1)
key(SCANCODE.A)
form("id", id)
form("x", x)
form("y", y)
form("z", z)
end
local function rule(note, on, filters)
key(SCANCODE.N)
form("note", note)
key(SCANCODE.E)
pick(on)
for _, pair in ipairs(filters or {}) do
form(pair[1], pair[2])
end
end
local function part(partKey, name, fields)
key(partKey)
pick(name)
for _, pair in ipairs(fields or {}) do
form(pair[1], pair[2])
end
end
local function step()
if stage == 1 then
key(SCANCODE.MAIN_1)
selectEntity("hero")
key(SCANCODE.DELETE)
selectEntity("ground")
key(SCANCODE.DELETE)
key(SCANCODE.MAIN_2)
lightType("hero")
key(SCANCODE.DELETE)
lightType("ground")
key(SCANCODE.DELETE)
key(SCANCODE.MAIN_3)
ruleGo(1, 0)
key(SCANCODE.DELETE)
key(SCANCODE.DELETE)
key(SCANCODE.DELETE)
key(SCANCODE.MAIN_1)
forgeSelect(nil)
form("title", "Field")
form("layers", "scene3d, overlay")
form("scene3d.fov", 55)
tutorialShot("scene")
elseif stage == 2 then
meshType("floor", "box", 30, 0.4, 12, 90, 110, 90)
form("behaviours", "solid")
meshType("ledge", "box", 4, 0.4, 4, 110, 90, 70)
form("behaviours", "solid")
meshType("prize", "sphere", 0.6, nil, nil, 255, 200, 60)
form("behaviours", "trigger")
tutorialShot("meshes")
elseif stage == 3 then
key(SCANCODE.A)
form("name", "hero")
form("look", "model")
form("look.file", "fox.glb")
form("look.scale", 0.008)
form("look.w", 0.6)
form("look.h", 0.9)
form("look.d", 0.6)
form("behaviours", "keys, character, animator")
form("keys.left", "LEFT")
form("keys.right", "RIGHT")
form("keys.up", "UP")
form("keys.down", "DOWN")
form("character.speed", 4)
form("character.jump", 6.5)
form("character.radius", 0.3)
form("character.height", 0.9)
form("animator.idle", "Survey")
form("animator.walk", "Walk")
key(SCANCODE.A)
form("name", "camera")
form("look", "none")
form("behaviours", "camera")
form("camera.mode", "follow")
form("camera.target", "hero")
form("camera.x", 0)
form("camera.y", 3)
form("camera.z", 7)
form("camera.lookY", 0.6)
form("camera.lag", 0.2)
tutorialShot("hero")
elseif stage == 4 then
lightType("floor")
placeAt("floor", 0, -0.2, 0)
key(SCANCODE.MAIN_2)
lightType("ledge")
placeAt("ledge", 6, 1.2, 0)
key(SCANCODE.MAIN_2)
lightType("prize")
placeAt("prize", 6, 2, 0)
key(SCANCODE.MAIN_2)
lightType("hero")
placeAt("hero", -4, 0.5, 0)
key(SCANCODE.MAIN_2)
lightType("camera")
placeAt("camera", -4, 3, 7)
-- The viewport orbits what is selected: a few steps round, for the picture.
selectEntity("hero")
key(SCANCODE.L)
key(SCANCODE.L)
key(SCANCODE.Y)
tutorialShot("viewport")
elseif stage == 5 then
key(SCANCODE.MAIN_3)
rule("Walking is a state the animator plays", "frame", { { "each", "hero" } })
part(SCANCODE.C, "test", { { "expr", "self.dx != 0 or self.dy != 0" } })
part(SCANCODE.T, "setState", { { "state", "walk" } })
rule("Standing still", "frame", { { "each", "hero" } })
part(SCANCODE.C, "test", { { "expr", "self.dx == 0 and self.dy == 0" } })
part(SCANCODE.T, "setState", { { "state", "idle" } })
rule("Jump, with ground underfoot", "frame", {})
part(SCANCODE.C, "keyPressed", { { "key", "SPACE" } })
part(SCANCODE.C, "onGround", { { "entity", "hero" } })
part(SCANCODE.T, "jump", { { "entity", "hero" } })
rule("The prize is taken", "enter", { { "a", "prize" }, { "b", "hero" } })
part(SCANCODE.T, "destroy", {})
part(SCANCODE.T, "addScore", { { "amount", 100 } })
part(SCANCODE.T, "playSound", { { "file", "coin.wav" } })
rule("Fallen off the world", "frame", {})
part(SCANCODE.C, "test", { { "expr", "hero.y < -5" } })
part(SCANCODE.T, "moveTo", { { "entity", "hero" }, { "x", -4 }, { "y", 0.5 }, { "z", 0 } })
tutorialShot("rules")
elseif stage == 6 then
key(SCANCODE.S)
local built = tutorialEnd()
local mine = onOverlayUpdate
dofile(built)
game = onOverlayUpdate
onOverlayUpdate = mine
authorKeyDown(0, SCANCODE.RIGHT.value)
elseif stage == 14 then
tutorialShot("playing")
local hero = authorEntity("hero")
local x, y, z = authorPosition(hero)
debugPrint(string.format("%s the fox is at %.1f, %.1f, %.1f in state %s", TUTORIAL.tag, x, y, z, tostring(hero.vars.state)))
if (x < -3) or (hero.vars.state ~= "walk") then
debugPrint(TUTORIAL.tag .. " FAIL holding RIGHT should walk the fox along")
end
elseif stage == 15 then
debugPrint(TUTORIAL.tag .. " RESULT done")
singeQuit()
end
end
function onOverlayUpdate()
frames = frames + 1
if frames % 12 == 0 and stage < 15 then
stage = stage + 1
step()
end
if game ~= nil then
return game()
end
forgeDraw(nil, nil)
return OVERLAY_UPDATED
end