Grafik i
DrRacketAV TOMMY KARLSSON
Upplägg
Grundläggande
grafik i racket
Frame%
Button%
Pane% & Panel%
Canvas%
Bitmap%
Kodexempel! Generella
problemlösar-
strategier
Grafisk kodstruktur
Grafisk effektivisering
Tile-baserad grafik
Olika sätt att bestämma rörelse
Frame%; Skapar ett objekt av typen frame% (ett fönster!)
(define *a-window* (new frame%
[width 300]
[height 200]
[label "Detta är ett fönster"]))
; Säger till vår frame att den ska synas
(send *a-window* show #t)
; En frame% är en container
(man kan placera saker i den!)
Det finns en mängd olika inargument som man kan utnyttja beroende
på vad man vill att den ska göra! Se docs.racket-lang.org!
Button%; Vår procedur som bestämmer vad som ska hända
(define (button-proc button event)
(send button set-label “Klick fungerade!”))
Det finns en mängd olika inargument som man kan utnyttja beroende
på vad man vill att den ska göra! Se docs.racket-lang.org!
; Skapar ett objekt av typen button% (en knapp!)
(define *a-button* (new button%
[parent *a-window*]
[label “En knapp”]
[callback button-proc])
; En button måste ha en container (som t.ex. frame)
som parent!
Panel% & Pane%De är båda containers, så de kan fyllas med t.ex. button eller canvas
Kan användas för att få en bättre struktur!
Underklasser:
horizontal-pane%
horizontal-panel%
vertical-pane%
vertical-panel%
Se dokumentationen
hur de används!
(PS: kanterna kring
panelerna fås genom att
sätta style till ’(border)
när man skapar
panelerna!)
Canvas%; Säger vad som ska ritas när canvasen uppdateras
(define (our-render-fn canvas dc)
; Innehåller någon form av kropp!
…)
; Skapar ett objekt av typen canvas% (en målarduk!)
(define *a-canvas* (new canvas%
[parent *a-window*]
[paint-callback our-render-fn]))
; En canvas är alltså en målarduk som vi kan rita figurer
eller bilder på!
Det finns en mängd olika inargument som man kan utnyttja beroende
på vad man vill att den ska göra! Se docs.racket-lang.org!
Exempel på dc%-
kommandonRitande Kommandon:
(send dc draw-rectangle x y width height)
(send dc draw-rounded-rectangle x y width height radius)
(send dc draw-arc x y width height start-radians end-radians)
(send dc draw-ellipse x y width height)
(send dc draw-line x1 y1 x2 y2)
(send dc draw-spline x1 y1 x2 y2 x3 y3)
(send dc draw-lines list-of-points)
(send dc draw-polygon list-of-points)
(send dc draw-text text x y)
(send dc draw-bitmap source x y)
Koordinatsystemsförändrande kommandon
(send dc translate dx dy)
(send dc rotate angle)
(send dc scale x-scale y-scale)
Färginställningar och liknande
(send dc set-pen color-name width style)
(send dc set-brush color-name style)
(send dc set-alpha opacity)
(send dc set-background color)
(send dc set-font font)
Rensa skärmen
(send dc clear)
Canvas: Exempel; Skapar fönstert(define *a-window* (new frame%
[width 600][height 400][label "Detta är ett fönster"]))
; Gör fönstret synligt(send *a-window* show #t)
; Vad ska ritas på vår canvas?(define (render-fn canvas dc)
(send dc set-brush "red" 'solid) ; Ändrar färg på vår pensel(send dc set-pen "green" 10 'solid) ; Ändrar färg på vår penna(send dc set-text-foreground "blue") ; Ändrar färg på vår text(send dc draw-rectangle 10 10 50 50)(send dc draw-text "Hallå där!" 200 200)(send dc set-brush (make-object color% 1 0 0) 'solid) ; Vi skapar en egen färg istället!(send dc draw-ellipse 240 240 100 100))
; Skapar vår canvas(define *a-canvas* (new canvas%
[parent *a-window*][paint-callback render-fn]))
Det finns en mängd olika inargument som man kan utnyttja beroende
på vad man vill att den ska göra! Se docs.racket-lang.org!
Translateringar, rotationer
och skalning
Rotation kring origo
Rotation kring vårt
nya origo!
Translateringar, rotationer
och skalningExempel på koordinatsystemsberoende kommandon:
(send dc translate xValue yValue) ; Förskjuter origo I den givna riktningen
(send dc rotate angle) ; Roterar den givna vinkeln kring origo
(send dc scale xFactor yFactor) ; Skalar bilden från
Ofta vill man utgå ifrån centrum av bilden:
(send dc translate (+ xValue (/ width 2)) (+ yValue (/ height 2)))
OBS!
Glöm ej att återställa koordinatsystemet
efter att ni använt det, annars blir det
riktigt jobbigt för er!
Bitmap%
Många användningsområden!
; Skapa en bitmap av en bildfil:
(define *our-bitmap* (make-object bitmap% ”testbild.png”)
; Dimensionerna sätts till bildens dimensioner!
; Skapar en tom bitmap
(define *our-bitmap* (make-object bitmap% 100 100)
; Dimensionerna sätts till argumenten, här 100 x 100!
; Hämta DC för bitmapen så att vi kan måla precis som på canvasen!
(define *our-dc* (new bitmap-dc% [bitmap *our-bitmap*]))
; Ritar ut en bitmap med någon dc, t.ex. på en canvas!
(send *some-dc* draw-bitmap *our-bitmap*
xPos
yPos)
Tangentbords och
musavkänningCanvas% innehåller funktionalitet för att känna av mus- eller
Tangentbordsinmatning, men den är normalt sett avstängd!
För att ändra på detta utnyttjar vi arv:
(define input-canvas%
(class canvas%
; Vi lägger till ytterligare inargument
; (procedurer som vi själva måste skriva!)
(init-field keyboard-handler ; ska hantera tangentbord
mouse-handler) ; ska hantera mus
; Vid ett knapptryck, anropa vår keyboard-handler
(define/override (on-char key-event)
(keyboard-handler key-event))
; Vid musrörelse, anropa vår mouse-handler
(define/override (on-event mouse-event)
(mouse-handler mouse-event))
(super-new)))
OBS! Se vad man kan göra med argumenten key-event% och
mouse-event% i dokumentationen!
Exempel: mus + bitmap
Exempel: mus + bitmap
Timers
Timer% kan användas för att så få procedurer att upprepas vid specifika tider
(Jämför med att en itererande procedur alltid anropar sig själv direkt när den är klar!)
60 Hz => periodtid 16,666 ms
; Vi definierar en timer som kommer anropa vår procedur ”our-update”
(define *game-timer* (new timer%
[notify-callback our-update]))
; Vi startar timern och säger att den ska anropa sin procedur varje 16 millisekund
(send *game-timer* start 16 #f) ; #f säger att den inte bara ska köras en gång!
; Vi stannar timern
(send *game-timer* stop)
Exempel: mus + bitmap
Grafisk kodstruktur
Gör looparna så lättviktiga som möjligt!
• Är det några onödiga beräkningar?
• Finns det beräkningar som inte måste ske varje iteration?
• Gör det lättläst!
Flytta specifika rit-delar till respektive objekt!
• Anropa t.ex. player’s ritprocedur istället för att skriva ritproceduren
i grafik-loopen!
• Ex:
(send player draw-player dc)
Istället för:
(send dc draw-bitmap (send player get-bitmap) …)
Detta ger ökad läsbarhet och en lättare överblick över vad
som egentligen sker i grafik-loopen!
Grafisk effektivisering
Vad händer om koden är för ineffektiv?
• Programmet kan frysa
• Programmet kan ”lagga”
• Vår fysikmotor kan sluta fungera! (Beror på implementation)
Hur kan man undersöka evalueringstid?
• Ex:
(define (our-update)
(let ([startTime (current-inexact-milliseconds)])
; BODY
(displayln (- (current-inexact-milliseconds) startTime))))
OBS! Testa evalueringstiden på era loopar ibland under projektets gång
Om det är någon del som är ineffektiv så är det lätt att hitta!
Tile-baserad grafik
Lätt att rita ut!
Lätt att göra kollisionsdetektering!
Lätt att göra kartan!
Tile-baserad grafik
Exempel (rita): Element på plats [3,12] har nummer 30.
• Rita bitmapen som svarar mot siffran 30 på position:
• Y = 3 * höjd-på-bitmap
• X = 12 * bredd-på-bitmap
• Klart!
Exempel (kollision): Spelaren försöker gå till position (12, 153)
• Tillåten tile att gå på?
• Koordinaterna motsvarar element:
• Rad = 153 / höjd-på-bitmap ; Avrundat neråt
• Kolumn = 12 / bredd-på-bitmap ; Avrundat neråt
• Får spelaren gå på tiles med siffran som finns på
position [rad, kolumn] ?
Matriser
0,0 0,1 0,2 0,3
1,0 1,1 1,2 1,3
2,0 2,1 2,2 2,3
3,0 3,1 3,2 3,3
0,0 0,1 0,2 0,31,0 1,1 1,2 1,32,0 2,1 2,2 2,33,0 3,1 3,2 3,3
Jämför med:
Hämta element r ur matrisen
Hämta element k ur det
=> Element (r,k)!
Olika sätt att bestämma rörelse
Lästa textfil och bygga tile-baserad karta
• Det är lättare att redigera en txt-fil än att skriva en tile-baserad
karta direkt i racket!
Vektorbaserad rörelse
• Linjär algebra:
• Vi vill gå från punk (a.b) till (c,d).
• Normerad riktningsvektor: (c-a , d-b) / Vektorns längd
• Ny kooridnat:
• xPos = xPos + Normerad-riktningsvektor-x * ΔS
• yPos = yPos + Normerad-riktningsvektor-y * ΔS
• Klart!
Waypoints
• Ex: Lista med koordinater: ( (1.1) , (1 . 2) , (3 . 2) , …)
• Riktning: Från nuvarande position till (1.1)!
• När avståndet mellan nuvarande position och (1.1) < något värde
• Lista med koordinater = ( (1 . 2) , (3 . 2) , …)
• Börja om!
Allmänna tips! Dela upp grafik och fysik?
Fördelar:
Fysik (som är viktigare) kan uppdateras oftare.
Kortare evalueringstid – Fysik och grafik måste inte evalueras samtidigt!
Ex:
; Hantera all fysik, t.ex. rörelse och kollision(define (physics-update) ; Kan köras med högre frekvens (OBS!
; inte för hög frekvens!)
; Rita bara ut saker på koordinater som redan är uträknade(define (graphics-update) ; Körs med frekvens på 60Hz
Jämn rörelse?
Icket FPS-beroende hastighet:
Utnyttja ΔT och hastighetsformeln ΔS = V * ΔT
Börja med icket-grafik
Större delen av projektet går att skriva utan grafik
Testa programmera enkla grafiska problem innan ni börjar med grafiken i projektet.