246 lines
6.2 KiB
Text
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
|