Grafik i DrRacket AV TOMMY KARLSSON
Upplägg Grundläggande grafik i racket Frame% Kodexempel! Generella problemlösarstrategier Grafisk kodstruktur Button% Pane% & Panel% Canvas% Bitmap% 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! )) ; 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! 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!
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%- kommandon Ritande 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 startradians 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 skalning Exempel 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änning Canvas% 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 Jämför med: 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 Hämta element r ur matrisen Hämta element k ur det => Element (r,k)! 3,0 3,1 3,2 3,3
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: Ex: Fysik (som är viktigare) kan uppdateras oftare. Kortare evalueringstid Fysik och grafik måste inte evalueras samtidigt! ; 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.