-- 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