Formålet med denne opplæringen er å lære 2D-spillprogrammering og C-språk gjennom eksempler. Forfatteren pleide å programmere spill på midten av 1980-tallet og var spilldesigner hos MicroProse i et år på 90-tallet. Selv om mye av det ikke er relevant for programmeringen av dagens store 3D-spill, vil det for små tilfeldige spill tjene som en nyttig introduksjon.
Implementering av slange
Spill som slange der objekter beveger seg over et 2D-felt kan representere spillobjektene enten i et 2D-rutenett eller som et enkelt dimensjons utvalg av objekter. "Objekt" betyr her ethvert spillobjekt, ikke et objekt som brukes i objektorientert programmering.
Spillkontroller
Tastene beveges med W = opp, A = venstre, S = ned, D = høyre. Trykk Esc for å avslutte spillet, f for å bytte bildefrekvens (dette er ikke synkronisert til skjermen, så det kan være raskt), tabulatortasten for å veksle feilsøkingsinfo og p for å stoppe den. Når det er satt på pause, endres bildeteksten og slangen blinker,
I slange er de viktigste spillobjektene
- Slangen
- Feller og frukt
Når det gjelder spill, vil en rekke ints inneholde hvert spillobjekt (eller del for slangen). Dette kan også hjelpe når du gjengir objektene i skjermbufferen. Jeg har designet grafikken for spillet på følgende måte:
- Horisontal Snake Body - 0
- Vertikal slangekropp - 1
- Hod i 4 x 90-graders rotasjoner 2-5
- Hale i 4 x 90-graders rotasjoner 6-9
- Kurver for veibeskrivelse. 10-13
- Apple - 14
- Jordbær - 15
- Banan - 16
- Felle - 17
- Vis slangegrafikkfilen snake.gif
Så det er fornuftig å bruke disse verdiene i en rutenettype definert som blokk [WIDTH * HEIGHT]. Siden det bare er 256 steder i rutenettet, har jeg valgt å lagre det i en enkelt dimensjonsgruppe. Hver koordinat på 16 x16 rutenett er et heltall 0-255. Vi har brukt ints slik at du kan gjøre rutenettet større. Alt er definert av #definer med WIDTH og HEIGHT begge 16. Siden slangegrafikken er 48 x 48 piksler (GRWIDTH og GRHEIGHT #defines) blir vinduet opprinnelig definert som 17 x GRWIDTH og 17 x GRHEIGHT for å være bare litt større enn rutenettet.
Dette har fordeler med hastigheten i spillet, ettersom bruk av to indekser alltid er tregere enn ett, men det betyr at i stedet for å legge til eller trekke fra 1 fra slangens Y-koordinater for å bevege seg vertikalt, trekker du WIDTH. Legg til 1 for å gå til høyre. Men når vi er lure, har vi også definert en makro l (x, y) som konverterer x- og y-koordinatene på kompileringstidspunktet.
Hva er en makro?
#definere l (X, Y) (Y * WIDTH) + X
Den første raden er indeks 0-15, den andre 16-31 osv. Hvis slangen er i den første kolonnen og beveger seg til venstre, må sjekken for å treffe veggen, før du beveger seg til venstre, sjekke om koordinat% WIDTH == 0 og for høyre veggkoordinat% WIDTH == WIDTH-1. % Er C-moduloperatøren (som klokke aritmetikk) og returnerer resten etter deling. 31 div 16 etterlater en resterende av 15.
Håndtere slangen
Det er tre blokker (int-matriser) som brukes i spillet.
- slange [], en ringbuffer
- form [] - Inneholder Snake-grafiske indekser
- dir [] - Holder retningen for hvert segment i slangen inkludert hode og hale.
Ved spillstart er slangen to segmenter lang med hode og hale. Begge kan peke i 4 retninger. For nord er hodet indeks 3, halen er 7, for østhodet er 4, halen er 8, for sørhodet er 5 og halen er 9, og for vest er hodet 6 og halen er 10. Mens slangen er to segmenter lang, er hodet og halen alltid 180 grader fra hverandre, men etter at slangen vokser kan de være 90 eller 270 grader.
Spillet starter med hodet mot nord på plassering 120 og halen mot sør på 136, omtrent sentralt. Til en liten kostnad på rundt 1600 byte lagring, kan vi oppnå en merkbar hastighetsforbedring i spillet ved å holde slangens plasseringer i slangebufferen som er nevnt ovenfor.
Hva er en ringbuffer?
En ringbuffer er en hukommelsesblokk som brukes til å lagre en kø som er av en fast størrelse og må være stor nok til å inneholde alle data. I dette tilfellet er det bare for slangen. Dataene skyves på forsiden av køen og tas av baksiden. Hvis fronten av køen treffer enden av blokken, så vikler den seg rundt. Så lenge blokken er stor nok, vil fronten av køen aldri ta igjen ryggen.
Hver plassering av slangen (dvs. den eneste int-koordinaten) fra halen til hodet (dvs. bakover) lagres i ringbufferen. Dette gir hastighetsfordeler fordi uansett hvor lang tid slangen blir, trenger bare hodet, halen og det første segmentet etter hodet (hvis det eksisterer) å byttes når den beveger seg.
Å lagre den bakover er også fordelaktig fordi når slangen får mat, vil slangen vokse når den neste blir flyttet. Dette gjøres ved å flytte hodet ett sted i ringbufferen og endre det gamle hodeposisjonen til å bli et segment. Slangen består av et hode, 0-n segmenter), og deretter en hale.
Når slangen spiser mat, er atefoodvariabelen satt til 1 og sjekket i funksjonen DoSnakeMove ()
Flytte slangen
Vi bruker to indeksvariabler, headindex og tailindex for å peke på hodet og haleplasseringene i ringbufferen. Disse starter på 1 (headindex) og 0. Så plassering 1 i ringbufferen holder plasseringen (0-255) til slangen på brettet. Sted 0 holder halen plassering. Når slangen beveger seg ett sted fremover, økes både tailindex og headindex av en, og vikler rundt til 0 når de når 256. Så nå er stedet som var hodet der halen er.
Selv med en veldig lang slange som er svingete og innviklet i si 200 segmenter. bare headindex, segment ved siden av headingx og tailindex endres hver gang det beveger seg.
Merk på grunn av veien SDL fungerer, vi må tegne hele slangen hver ramme. Hvert element trekkes inn i rammebufferen og deretter vippes slik at det vises. Dette har imidlertid en fordel ved at vi kunne tegne slangen jevn og bevegelige noen få piksler, ikke en hel nettposisjon.