Game Lab – Turtle (Spor A/B) · kom i gang & opgaver
Denne side guider eleverne til at oprette et nyt Game Lab-projekt, indsætte undervisningsskelettet (v4) og arbejde i ét samlet
Svar-område nederst i filen: exerciseAnswer(). Ingen øvelsesvælger – Clear genindlæser altid elevens svar.
Skelettet har to spor: Exercise (en turtle med spor og trin-kørsel) og Play (flere turtles uden spor).
Nyhed: Vælg hvad der er “frem” for hver sprite (setForwardAxis()/setForwardOffset()).
1) Opret nyt Game Lab-projekt
- Gå til code.org og log ind.
- Åbn Create / Lav og vælg Game Lab.
- Skift til fanen Text (hvis du er i Blocks).
- Ryd al standardkode i editoren (marker alt og slet).
2) Tilføj “turtle” i Animation-fanen
Skelettet forventer en hvid figur kaldet "turtle".
- Klik fanen Animation (øverst).
- Tryk + og vælg/gem et billede – fx en hvid silhuet.
- Giv billedet præcis navnet:
turtle. - Gå tilbage til Code-fanen.
Hvis din sprite peger “op” eller “ned” som frem: Det håndteres i koden via setForwardAxis("up"|"down"|"left"|"right") eller setSpriteVisualOffset(±90, 180) for visuel finjustering.
3) Indsæt undervisningsskelettet (v4, med Svar-område)
Kopier hele blokken herunder og indsæt i din Game Lab-editor:
// === Game Lab Turtle – undervisningsskelet (Spor A/B) – v4 (Svar-område) ===
// Eleverne skriver KUN i exerciseAnswer() nederst. Clear/Start bygger køen fra det svar.
// Per-turtle "forward axis": vælg "frem" pr. sprite (setForwardAxis / setForwardOffset).
// Forudsætning: Animation-fanen har en HVID figur kaldet "turtle".
// • MODE vælges KUN herunder (ingen tast, ingen knap):
var MODE = "exercise"; // "exercise" eller "play"
// Skærmstørrelse (Game Lab default)
var SCREEN_W = 400, SCREEN_H = 400;
// --- Forward axis mapping ---
var AXIS_TO_OFFSET = { up:-90, right:0, down:90, left:180 };
var DEFAULT_FORWARD_AXIS = "down";
function axisToOffset(axis){
if (AXIS_TO_OFFSET.hasOwnProperty(axis)) return AXIS_TO_OFFSET[axis];
return AXIS_TO_OFFSET.up;
}
// -------------------------------------------------
// Turtle "klasse"
// -------------------------------------------------
var turtles = [];
function newTurtle(x, y, penColor, forwardAxis) {
var t = {
x: x, y: y, angle: 0,
penDown: true, penColor: penColor || "black", penWidth: 2,
sprite: createSprite(x, y),
path: [],
forwardOffset: axisToOffset(forwardAxis || DEFAULT_FORWARD_AXIS),
spriteVisualOffset: 0
};
t.sprite.setAnimation("turtle");
t.sprite.scale = 0.12;
t.sprite.tint = t.penColor;
t.sprite.rotation = t.angle + t.forwardOffset + t.spriteVisualOffset;
t.forward = function(dist){
var oldX = this.x, oldY = this.y;
var r = Math.PI * (this.angle + this.forwardOffset) / 180;
this.x += dist * Math.cos(r);
this.y += dist * Math.sin(r);
if (this.penDown) this.path.push({x1:oldX,y1:oldY,x2:this.x,y2:this.y,color:this.penColor,width:this.penWidth});
this.sprite.x = this.x; this.sprite.y = this.y;
this.sprite.rotation = this.angle + this.forwardOffset + this.spriteVisualOffset;
};
t.turnRight = function(deg){ this.angle += deg; this.sprite.rotation = this.angle + this.forwardOffset + this.spriteVisualOffset; };
t.turnLeft = function(deg){ this.angle -= deg; this.sprite.rotation = this.angle + this.forwardOffset + this.spriteVisualOffset; };
t.setHeading= function(deg){ this.angle = deg; this.sprite.rotation = this.angle + this.forwardOffset + this.spriteVisualOffset; };
t.penUp = function(){ this.penDown = false; };
t.penDownFn = function(){ this.penDown = true; };
t.setPenColor = function(c){ this.penColor = c; this.sprite.tint = c; };
t.setPenWidth = function(w){ this.penWidth = w; };
t.goTo = function(nx, ny){ this.x=nx; this.y=ny; this.sprite.x=nx; this.sprite.y=ny; };
t.home = function(){ this.goTo(SCREEN_W/2, SCREEN_H/2); this.setHeading(0); };
t.clearPath = function(){ this.path = []; };
t.setForwardAxis = function(axis){ this.forwardOffset = axisToOffset(axis); this.sprite.rotation = this.angle + this.forwardOffset + this.spriteVisualOffset; };
t.setForwardOffset = function(deg){ this.forwardOffset = deg; this.sprite.rotation = this.angle + this.forwardOffset + this.spriteVisualOffset; };
t.setSpriteVisualOffset = function(deg){ this.spriteVisualOffset = deg; this.sprite.rotation = this.angle + this.forwardOffset + this.spriteVisualOffset; };
turtles.push(t);
return t;
}
function drawPath(t){
for (var i=0;i<t.path.length;i++){
stroke(t.path[i].color); strokeWeight(t.path[i].width);
line(t.path[i].x1, t.path[i].y1, t.path[i].x2, t.path[i].y2);
}
}
// -------------------------------------------------
// UI – simple canvas-knapper
// -------------------------------------------------
var buttons = [];
function makeBtn(label,x,y,w,h,onClick){ buttons.push({label:label,x:x,y:y,w:w,h:h,onClick:onClick}); }
function drawBtn(b){ noStroke(); fill("#f4f4f4"); rect(b.x,b.y,b.w,b.h,6);
stroke("#999"); noFill(); rect(b.x,b.y,b.w,b.h,6);
noStroke(); fill("#111"); textSize(12); textAlign(LEFT, CENTER); text(b.label, b.x+8, b.y+b.h/2); }
function mouseIn(b){ return mouseX>b.x && mouseX<b.x+b.w && mouseY>b.y && mouseY<b.y+b.h; }
var mouseLatch=false;
function handleButtons(){
var click=false;
if (mouseWentDown()) mouseLatch=true;
if (mouseLatch && !mouseDown()) { click=true; mouseLatch=false; }
for (var i=0;i<buttons.length;i++){
drawBtn(buttons[i]);
if (click && mouseIn(buttons[i])) buttons[i].onClick();
}
}
// -------------------------------------------------
// Exercise – kø, step & svarbygning
// -------------------------------------------------
var exT = null;
var queue = []; // {op:"fd|rt|lt|col|wd|jump", val:number|string|{x,y}}
var running = false;
var opsPerFrame = 1;
var currentExercise = "";
function q(op,val){ queue.push({op:op,val:val}); }
function stepOnce(){
if (queue.length===0){ running=false; return; }
var cmd = queue.shift();
if (cmd.op==="fd") exT.forward(cmd.val);
else if (cmd.op==="rt") exT.turnRight(cmd.val);
else if (cmd.op==="lt") exT.turnLeft(cmd.val);
else if (cmd.op==="col") exT.setPenColor(cmd.val);
else if (cmd.op==="wd") exT.setPenWidth(cmd.val);
else if (cmd.op==="jump"){
var p=cmd.val, was=exT.penDown;
exT.penUp(); exT.goTo(p.x,p.y); if (was) exT.penDownFn();
}
}
function stepFrame(){ if (!running) return; for (var i=0;i<opsPerFrame;i++) stepOnce(); }
// Byg køen fra elevens svarområde
function loadExerciseByConfig(){
queue=[]; exT.clearPath(); exT.home(); exT.penDownFn();
exerciseAnswer(); // udfylder queue via q(...)
currentExercise = "custom-answer";
}
// -------------------------------------------------
// Play – flere turtles uden spor (demo)
// -------------------------------------------------
var playTs = [];
function resetPlay(count){
turtles = [];
playTs = [];
for (var i=0;i<count;i++){
var axis = DEFAULT_FORWARD_AXIS;
var t = newTurtle(randomNumber(40,360), randomNumber(40,360), randColor(), axis);
t.penUp(); t.setHeading(randomNumber(0,359));
t.speed = randomNumber(1,3);
playTs.push(t);
}
}
function updatePlay(){
for (var i=0;i<playTs.length;i++){
var t = playTs[i];
t.forward(t.speed);
if (t.x < 10 || t.x > SCREEN_W-10) { t.turnRight(180 - randomNumber(0,20)); clampInside(t); }
if (t.y < 10 || t.y > SCREEN_H-10) { t.turnRight(180 - randomNumber(0,20)); clampInside(t); }
}
}
function setPlayForwardAxis(axis){ for (var i=0;i<playTs.length;i++) playTs[i].setForwardAxis(axis); }
function setPlaySpriteOffset(deg){ for (var i=0;i<playTs.length;i++) playTs[i].setSpriteVisualOffset(deg); }
function clampInside(t){ if (t.x<10) t.goTo(10,t.y); if (t.x>SCREEN_W-10) t.goTo(SCREEN_W-10,t.y); if (t.y<10) t.goTo(t.x,10); if (t.y>SCREEN_H-10) t.goTo(t.x,SCREEN_H-10); }
function randColor(){ var cs=["#e53935","#1e88e5","#43a047","#fb8c00","#8e24aa","#00897b"]; return cs[randomNumber(0,cs.length-1)]; }
// -------------------------------------------------
// Setup + UI
// -------------------------------------------------
function buildButtons(){
buttons = [];
if (MODE==="exercise"){
makeBtn(running?"Pause":"Run", 8,8,68,24, function(){ running=!running; });
makeBtn("Step", 84,8,56,24, function(){ stepOnce(); });
makeBtn("Clear", 148,8,64,24, function(){ exT.clearPath(); exT.home(); queue=[]; running=false; loadExerciseByConfig(); });
makeBtn("Speed -", 8,40,68,24, function(){ opsPerFrame = Math.max(1, opsPerFrame-1); });
makeBtn("Speed +", 84,40,68,24, function(){ opsPerFrame = Math.min(20, opsPerFrame+1); });
} else {
makeBtn("Reset Play", 8,8,100,24, function(){ resetPlay(8); });
makeBtn("Axis: up", 8,40,76,22, function(){ setPlayForwardAxis("up"); });
makeBtn("right", 88,40,60,22, function(){ setPlayForwardAxis("right"); });
makeBtn("down", 152,40,60,22, function(){ setPlayForwardAxis("down"); });
makeBtn("left", 216,40,60,22, function(){ setPlayForwardAxis("left"); });
makeBtn("img +90°", 8,68,76,22, function(){ setPlaySpriteOffset(90); });
makeBtn("img 0°", 88,68,60,22, function(){ setPlaySpriteOffset(0); });
makeBtn("img -90°", 152,68,60,22, function(){ setPlaySpriteOffset(-90); });
makeBtn("img 180°", 216,68,60,22, function(){ setPlaySpriteOffset(180); });
}
}
function handleKeys(){
if (MODE==="exercise"){
if (keyWentDown(" ")) running=!running;
if (keyWentDown("s")) stepOnce();
if (keyWentDown("c")) { exT.clearPath(); exT.home(); queue=[]; running=false; loadExerciseByConfig(); }
if (keyWentDown("z")) opsPerFrame = Math.max(1, opsPerFrame-1);
if (keyWentDown("x")) opsPerFrame = Math.min(20, opsPerFrame+1);
} else {
if (keyWentDown("r")) resetPlay(8);
}
}
function setupExercise(){
turtles = [];
exT = newTurtle(SCREEN_W/2, SCREEN_H/2, "#111", DEFAULT_FORWARD_AXIS);
exT.setPenWidth(2);
exT.clearPath();
loadExerciseByConfig(); // bygger fra elevens svarområde
}
// Init
if (MODE==="exercise") { setupExercise(); } else { resetPlay(8); }
buildButtons();
// ===============================================
// ELEVENS SVAR-OMRÅDE — skriv KUN herunder
// Brug q("fd"/"rt"/"lt"/"col"/"wd"/"jump", værdi)
// Eksempler:
// q("fd", 60); q("rt", 90);
// q("col", "red"); q("wd", 3);
// q("jump", {x:200,y:240});
// ===============================================
function exerciseAnswer(){
// Eksempel: A1 — kvadrat 60 px (udskift med din opgave)
q("wd", 3); // pen WiDth
q("col", "red"); // COLor
q("fd", 60); // ForwarD
q("rt", 90); // Right Turn, 90 deg
q("fd", 60);
q("rt", 90);
q("fd", 60);
q("rt", 90);
q("fd", 60);
q("jump", {x:150,y:200}) // JUMP, hop, uden at tegne.
}
// -------------------------------------------------
// DRAW LOOP
// -------------------------------------------------
function draw(){
background("white");
handleButtons();
handleKeys();
noStroke(); fill("#555"); textSize(12); textAlign(LEFT,TOP);
var status = (MODE==="exercise"
? (running?"RUNNING":"PAUSED")+" · speed="+opsPerFrame+" ops/frame · ex=answer"
: "PLAY · "+playTs.length+" turtles · no trails");
text(status, 8, (MODE==="exercise"?72:64));
if (MODE==="exercise"){
stepFrame();
drawPath(exT);
drawSprites();
} else {
updatePlay();
drawSprites();
}
}
4) Vælg MODE & Svar-område
var MODE = "exercise";
// "exercise" eller "play"
- Arbejd i Exercise for opgaver: skriv/ret dit svar i funktionen
exerciseAnswer()nederst i filen. - Tryk Clear (eller C) for at genindlæse lærredet og bygge programkøen fra dit Svar-område.
- Skift til Play for at eksperimentere med flere turtles (uden spor) og aksestyring.
Opgave - Kvadrat (side = 60 px)
Målet er et kvadrat. Du må gerne starte hvor som helst – formen afgør.
// Hint (turtle): brug et loop
q("wd", 3); q("col", "red");
for (var i=0;i<4;i++){ q("fd", 60); q("rt", 90); }
Opgave - Lige-sidet trekant (samme vinkeldrej: 120°)
Drej den samme vinkel hver gang (120°) og brug samme sidelængde.
// Hint (turtle): 3 gentagelser, 120° højredrej
q("wd", 3); q("col", "seagreen");
for (var i=0;i<3;i++){ q("fd", 70); q("rt", 120); }
Opgave - Retvinklet trekant (start ved en spids)
Start i en spids (ikke det retvinklede hjørne). Brug to rette ben, den tredje side lukker trekanten.
// Hint (turtle):
// 1) Gå langs første katete a
// 2) Drej 90° (rt 90) og gå katete b
// 3) Drej mod start og luk (hypotenuse)
q("wd",3); q("col","#1e88e5");
q("fd", 90); // a
q("rt", 90); q("fd", 50); // b
// Luk: drej mod start (beregn evt. vinkel selv eller prøv dig frem)
q("rt", 143); q("fd", 103);
Opgave - Retvinklet trekant (start ved det retvinklede hjørne)
Start i det retvinklede hjørne. Gå først langs én katete, drej 90°, gå anden katete, og luk med hypotenusen.
// Hint (turtle): to 90°-ben, luk med hypotenuse
q("wd",3); q("col","#8e24aa");
q("fd", 90); // katete 1
q("rt", 90); q("fd", 55); // katete 2
// Luk (hypotenuse)
q("rt", 141); q("fd", 106);
Opgave - Stjerne (5 takker)
Klassisk 5-takket stjerne tegnes ved at dreje 144° samme vej for hver tak.
// Hint (turtle): 5 gentagelser, 144° højredrej
q("wd",3); q("col","#fb8c00");
var s = 70;
for (var i=0;i<5;i++){ q("fd", s); q("rt", 144); }
Opgave - Juletræ (3 trekanter + stamme)
Byg træet af tre ligesidede trekanter (øverst lille, nederst størst) og afslut med en stamme.
// Hint (turtle): hjælpere + jump mellem lag
q("wd",3); q("col","#2e7d32");
function tri(side){ for (var i=0;i<3;i++){ q("fd", side); q("rt", 120); } }
// Øverste lag
q("jump",{x:200,y:120}); tri(40);
// Midterste lag
q("jump",{x:200,y:160}); tri(60);
// Nederste lag
q("jump",{x:200,y:200}); tri(90);
// Stamme (lille rektangel)
q("col","#6d4c41"); q("jump",{x:200,y:245});
for (var i=0;i<2;i++){ q("fd", 20); q("rt", 90); q("fd", 10); q("rt", 90); }
Tip: Justér jump-punkter, så lagene overlapper pænt.
Opgave - Fri tegning
Tegn en frit valgt figur.
Tips & fejlfinding
- Ingen sprite? Sørg for at Animation-fanen har et asset kaldet
"turtle", og at koden kørerdrawSprites(). - Sprite roteret forkert? Brug
exT.setSpriteVisualOffset(90)(eller −90/180) for at rette billedets visuelle retning uden at ændre bevægelsesfysikken. - Vil du definere “frem” som fx “up”? Kald
exT.setForwardAxis("up")(eller"down"/"left"/"right"). Bevægelsen bruger altid den valgte frem-akse. - For hurtigt/langsomt i Exercise? Brug Z/X eller knapperne Speed −/+ for at ændre opsPerFrame.
- Vil du se det “live” trin for trin? Tryk Step flere gange. Hvert tryk udfører én kø-kommando.
- Play-mode demo: Sæt
MODE="play", klik Reset Play, og eksperimentér med knapperne Axis: up/right/down/left og img ±90°/180°.
Tip: Gem ofte. Brug Remix til nye versioner, så du bevarer en fungerende baseline.