Level 4PXT: Snake

Skrevet av: Håvard Nygård Jakobsen

Oversatt av: Stein Olav Romslo

Kurs: Microbit
Tema: Elektronikk, Blokkbasert, Spill
Fag: Matematikk, Programmering
Klassetrinn: 5.-7. klasse, 8.-10. klasse, Videregående skole
Bilde av BokmålPå Bokmål

Introduksjon

Ein eller annan variant av Snake har eksistert på datamaskiner heilt sidan slutten av 1970-talet. Mange vaksne kjenner spelet frå Nokia sine mobiltelefonar, medan mange born kjenner det frå moderne versjonar som slither.io.

I spelet styrer me ein slange rundt på skjermen, og slangen må unngå å krasje i kanten av skjermen og seg sjølv. Slangen veks når den et mat som dukkar opp tilfeldige stader, og spelet går fortare og fortare etter kvart som slangen veks.

I denne oppgåva brukar me engelske namn på klossar og variablar. Dette er mellom anna for at det skal vere enklare å finne att innebygde funksjonar når me byttar mellom klossprogrammering og Javascript, sidan funksjonane har engelske namn i Javascript. Det er veldig vanleg at programmerarar brukar engelske namn på funksjonar og variablar. Dette gjer det mellom anna enklare å poste kode på internettforum og få hjelp frå heile verda.

Denne oppgåva er ganske lang, men me tek det steg for steg og forklarar undervegs. La oss setje i gong.

Bilete av korleis Snake-spelet ser ut

Steg 1: Teikne slangen

Det fyrste me treng er ein liten kodesnutt som teiknar slangen vår. Skjermen består av 5x5 ledlys. Desse kan me skru av og på som me vil med litt kode. For å teikne slangen treng me noko som kan passe på kor me skal teikne den. Til det skal me bruke eit array, ein type variabel som inneheldt ei liste med verdiar. I lista vår brukar me to verdiar for å teikne ein bit av slangen. Den fyrste verdien seier kva kolonne me skal teikne i (X), og den neste seier kva rad me skal teikne i (Y). Saman får me ein koordinat (X, Y) for ledlyset som me skal skru på.

Bilete av koordinatar på micro:bit

checkSteg for steg

  • Bilete av kode for å setje lista

No treng me kode for å teikne slangen. For å gjere det enklare å halde oversikt over programmet vårt, så gjer me dette med ein funksjon.

Kvifor startar me på 0 og går til lengda minus éin?

Tenk deg at du har ein stabel med ark, til dømes ei oppgåve som denne. Kor mange gonger må du bla for å lese den fyrste sida? Kor mange gonger må du bla for å lese alle arka? Slik fungerer eit array, du startar i posisjon 0, og må bruke antal minus éin for å bla gjennom alle. Viss det er vanskeleg å forstå, så kan du prøve å tenke "slik er det berre" no, og så blir du vant til det seinare.

Bilete av kode for å teikne slangen
Bilete av hovudløkka, steg 1

flagTest prosjektet

Prøv spelet i simulatoren for å teste koden så langt. Sjekk at det blir teikna to punkt.

Eigentleg er det me skriv noko som heiter Typescript. Det er ein variant av Javascript. I vanleg Javascript treng me ikkje bruke type på variablar, men det må ein nokre stader i Typescript. Til dømes må me spesifisere at ein variabel som skal innehalde tal skal gjere det med let x: number.

Då har me det me treng for å teikne slangen. Men det er litt keisamt at den berre står stille. Det må me gjere noko med!

Steg 2: Slithering snake

No skal me få slangen til å bevege seg. Sidan slangen kan bevege seg opp, ned, til venstre og til høgre, så treng me ein variabel som seier kva retning den er på veg akkurat no. Me beveger slangen ved å leggje til eit nytt punkt på starten, og å ta bort det siste punktet på halen.

Tips

I denne oppgåva brukar me både klossprogrammering og handskrive kode. Før me byttar mellom klossprogrammering og Javascript kan det vere lurt å lagre spelet. Det brukar å gå fint å bytte mellom desse sjølv om nokre av klossane kan bli grå. Det hender at Makecode ikkje forstår at noko kode forsvinn, då er det veldig greitt å ha eit lagra punkt å gå tilbake til. Men du treng ikkje å bry deg viss det berre er nokre variablar som flyttar seg litt på skjermen.

checkSteg for steg

function updateSnake(){
}
	x = snake[0]
	y = snake[1]
    if (direction == 'up') {
        y = y - 1;
    }
    if (direction == 'down') {
        y = y + 1;
    }
    if (direction == 'left') {
        x = x - 1
    }
    if (direction == 'right') {
        x = x + 1
    }
    snake.unshift(y);
    snake.unshift(x);
    snake.pop();
    snake.pop();
basic.forever(function () {
    drawSnake();
    updateSnake();
})

flagTest prosjektet

Prøv koden i simulatoren for å teste koden så langt.

  • basic.forever(function () {
    	basic.clearScreen();
    	drawSnake();
    	updateSnake();
    })
    
  • basic.forever(function () {
    	basic.clearScreen();
    	drawSnake();
    	updateSnake();
    	basic.pause(1000);
    })
    

Steg 3: Styring

No er det på tide å leggje inn styring av slangen. Men fyrst lagar me ein liten startskjerm.

checkSteg for steg

Bilete av start
Bilete av forever
Bilete av knappekode for å starte

flagTest prosjektet i simulatoren

No skal slangeikonet visast til du trykkar på ein knapp, då startar spelet. Men me treng meir kode for å styre. Når ein speler skal A-knappen styre slangen 90 grader mot venstre frå noverande retning, medan B-knappen styrer slangen 90 grader mot høgre.

checkSteg for steg

input.onButtonPressed(Button.A, function () {
    if (isPlaying) {
        if (direction == 'up') {
        	direction = 'left';
        }
        else if (direction == 'left') {
            direction = 'down';
        }
        else if (direction == 'down') {
            direction = 'right';
        }
        else if (direction == 'right') {
            direction = 'up';
        }
    } else {
        isPlaying = true
    }
})
input.onButtonPressed(Button.B, function () {
    if (isPlaying) {
        if (direction == 'up') {
            direction = 'right';
        }
        else if (direction == 'right') {
            direction = 'down';
        }
        else if (direction == 'down') {
            direction = 'left';
        }
        else if (direction == 'left') {
            direction = 'up';
        }
    } else {
        isPlaying = true;
    }
})

flagTest prosjektet

Prøv spelet i simulatoren for å teste koden så langt. Sjekk at styringa virkar.

No kan du styre slangen, men det er eit lite problem. Viss me trykkar fort to gonger på ein knapp går slangen i motsett retning. Det vil me ikkje, sidan det tyder at slangen går gjennom seg sjølv, og det blir berre tull.

checkSteg for steg

input.onButtonPressed(Button.A, function () {
    if (isPlaying) {
        if (buttonPressed) {
            return;
        }
        buttonPressed = true;
		}
})

flagTest prosjektet

Prøv spelet i simulatoren for å teste koden så langt. Viss alt stemmer no er det berre eitt knappetrykk som gjeld for kvart hakk slangen beveger seg.

Steg 4: GAME OVER!

Men me kan jo styre slangen sjølv om den køyrer utanfor skjermen. Det skal sjølvsagt ikkje vere lov!

checkSteg for steg

function checkGameOver(x: number, y: number) {
    if(x < 0 || x > 4 || y < 0 || y > 4)
    {
        direction = 'up';
        snake = [2, 1, 2, 2]

        basic.showIcon(IconNames.Skull);
        basic.pause(2000);
        basic.showIcon(IconNames.Snake);
        isPlaying = false;
    }
}

flagTest prosjektet

På tide å prøve spelet på micro:bit

Steg 5: Litt lyd, takk!

checkSteg for steg

    music.playTone(Note.C5, 20)
    music.beginMelody(music.builtInMelody(Melodies.Wawawawaa), MelodyOptions.Once);

flagTest prosjektet

Kople til hovudtelefonar eller ein høgtalar til micro:bit-en og sjekk at du får lyd. Test i simulatoren viss du ikkje har moglegheit til å kople til noko.

Steg 6: Mat

No kan me styre slangen, det blir game over, og me har lyd. På tide å leggje til mat slik at me får eit skikkeleg spel. Maten skal me generere på ein tilfeldig stad, men me må passe på at det ikkje er på slangen.

checkSteg for steg

let foodX: number = 0
let foodY: number = 0
function generateFood(){
    foodX = Math.randomRange(0, 4);
    foodY = Math.randomRange(0, 4);
}
function isOnSnake(myX: number, myY: number) {
    for (let index = 0; index <= snake.length - 1; index+=2) {
        if (myX === snake[index] && myY === snake[index+1]) {
            return true;
        }
    }
    return false;
}
function generateFood() {
    foodX = Math.randomRange(0, 4);
    foodY = Math.randomRange(0, 4);
    if (isOnSnake(foodX, foodY)) {
        generateFood();
    }
}
function drawFood() {
    led.plot(foodX, foodY);
}
basic.forever(function () {
    if (isPlaying) {
        basic.clearScreen()
        drawFood();
        drawSnake();
        basic.pause(1000);
        updateSnake();
    }
})

flagTest prosjektet

Test i simulatoren for å sjekke at maten blir teikna.

No har me laga maten, då gjenstår det berre å ete den. Korleis veit me om me er på riktig stad for å ete maten? Jo, viss x og y i updateSnake() er dei same som foodX og foodY. Då skal slangen vekse med eit punkt. Og korleis gjer me det? Me let berre vere å fjerne det siste punktet på halen i updateSnake()! Smart?

checkSteg for steg

if (x == foodX && y == foodY) {
    music.beginMelody(music.builtInMelody(Melodies.BaDing), MelodyOptions.Once);
    generateFood();
} else {
    snake.pop();
    snake.pop();
}
function checkGameOver(x: number, y: number) {
    if (x < 0 || x > 4 || y < 0 || y > 4 || isOnSnake(x,y)) {
        direction = 'up';
        snake = [2, 1, 2, 2]

        music.beginMelody(music.builtInMelody(Melodies.Wawawawaa), MelodyOptions.Once);
        basic.showIcon(IconNames.Skull);
        basic.pause(2000);
        basic.showIcon(IconNames.Snake);
        isPlaying = false;
    }
}
if (x === foodX && y === foodY) {
    music.beginMelody(music.builtInMelody(Melodies.BaDing), MelodyOptions.Once);
    updateRate *= 0.95;
    generateFood();
} else {
    snake.pop();
    snake.pop();
}
buttonPressed = false
updateRate -= 5;
basic.forever(function () {
    if (isPlaying) {
        basic.clearScreen();
        drawSnake();
        drawFood();
        basic.pause(updateRate);
        updateSnake();
    }
})

flagTest prosjektet

Prøv spelet på micro:bit og sjekk at alt fungerer.

Steg 7: Litt pynt

No har me i grunnen eit fungerande spel. Men me skal pynte litt på det for å gjere det endå betre. Det kan vere vanskeleg å sjå maten nokre gonger, fordi den kan kome kor som helst, og er lik slangen. For å gjere det enklare skal me få den til å blinke. Det gjer me ved å bruke input.runningTime(). Denne funksjonen gir oss antal millisekund sidan micro:bit-en vart skrudd på.

checkSteg for steg

function drawFood() {
    const currentTime = input.runningTime();
    if(currentTime%500 > 250){
        led.plot(foodX, foodY);
    }
}
lastUpdateTime = input.runningTime();
basic.forever(function () {
    if (isPlaying) {
        basic.clearScreen();
        drawSnake();
        drawFood();
        const currentTime = input.runningTime();
        if(currentTime - lastUpdateTime >= updateRate)
        {            
            updateSnake();
            lastUpdateTime = currentTime;
        }
    }
})

flagTest prosjektet på micro:bit

Prøv spelet på micro:bit. Forhåpentlegvis fungerer det brillefint. Gratulerer, du har gjort ferdig spelet!

Her kjem eit par utfordringar!

Utfordring

Legg til score og highscore som blir vist når du døyr. Spel ein liten melodi og vis "New highscore" viss spelaren slo rekorden.

Tips: Du kan bruke lengda til snake-arrayet til å rekne ut poeng.

Utfordring

Styr slangen automagisk. Få den til å gå rundt og finne mat sjølv, samstundes som den prøver å ikkje krasje.

Tips: Du har allereie skrive kode som sjekkar om slangen køyrer utanfor eller kolliderer med seg sjølv. Du kan bruke den same koden til å sjekke posisjonen og endre retning (direction) mot høgre eller venstre viss den krasjar, finne ny posisjon og sjekke om den krasjar der òg.

For å finne maten kan du snu mot den når du kjem på same rad eller kolonne som maten.

Lisens: CC BY-SA 4.0

Forbedre denne siden

Funnet en feil? Kunne noe vært bedre?
Hvis ja, vennligst gi oss tilbakemelding ved å lage en sak på Github eller fiks feilen selv om du kan. Vi er takknemlige for enhver tilbakemelding!