Nighthawk.dk

Scripting med SED

Skrevet d. 7. Aug. 2016 af Claus Nielsen.

Nu skal vi i gang med den egentlige programmering! Vi tager udgangspunkt i template-koden, så du allerede kan rende rundt i dit level og skyde på forskellige modstandere, og laver her en række mindre scripts som kan gøre dit spil lidt mere spændende.

I gang med scripting i SED

Vi skal nu i gang med at se på noget programmering, men for at starte blødt ud, vil vi bygge ovenpå template-koden. Senere lærer du at programmere et helt spil fra bunden - men ikke i dette afsnit.

Start med at åbne scriptet (kode-filen) for dit level i 3D Gamestudios Script EDitor, SED: det er .c-filen med samme navn som dit level, der ligger i samme mappe som dit level (f.eks. hedder mit level level.wmb, og mit script hedder derfor level.c). Scriptet ser nogenlunde således ud:

////////////////////////////////////////////////////////////////////////
// Template main script:
// Created by WED.
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
// entry: Start Level
// entry_help: Name of the level loaded at start
char* t_levelname = "level.wmb";

////////////////////////////////////////////////////////////////////////
#include <acknex.h>
#include <default.c>
#include "t_player.h"
#include "t_weapons.h"
#include "t_enemies.h"
#include "t_doors.h"
#include "t_elevators.h"

/////////////////////////////////////////////////////////////////
// The main() function is started at game start
function main()
{
// entry: Warning Verbosity (0,1,2,3,4)
// entry_help: Sets sensitivity of warnings (0 = none, 1 = some, 4 = all).
	warn_level = 2;	

#ifndef startup_h 
// load the level
	level_load(t_levelname);
#endif
}

Bemærk: De markerede linier kan variere: de afhænger af hvilke scripts du har tilføjet i WED's Project Manager, og i hvilken rækkefølge.

Linierne der starter med #include fortæller, at en række andre kode-filer skal inkluderes - bl.a. al template-koden. Derudover har vi function main(), efterfulgt af en kode-blok mellem { og } - den vender vi tilbage til senere.

Kommentarer i koden

Mange af linierne i kodefilen starter med //, og netop disse to skråstreger har en speciel betydning: resten af linien, efter skråstregerne, bliver ignoreret. Dvs. man kan bruge // til at skrive kommentarer i sin kode.

Man kan også skrive kommentarer der fylder flere linier, ved at skrive dem mellem /* og */.

Lad os komme i gang med at skrive noget kode - tilføj denne kode efter kodeblokken til function main (dvs. nederst i kodefilen, efter }):

action min_forste_action()
{
	set(my,TRANSLUCENT);
}

Som du måske har gættet, så har vi nu lavet en action: gem kodefilen, og åben dit level i WED. Sæt en entity (f.eks. en model) ind i din bane, højre-klik og åben Properties, og tryk Choose Action. Du skulle nu kunne finde min_forste_action på listen - ellers kan du enten prøve at genstarte WED, eller selv skrive min_forste_action i tekstfeltet over listen. Build, Run, og find din entity med din nye action i dit level - her har jeg brugt ball.mdl:

Modellen med den nye action er blevet gennemsigtig! Lad os nu se nærmere på koden: I første linie, action min_forste_action(), bruger vi ordet action til at fortælle, at vi gerne vil lave en ny action. Navnet på vores nye action står lige bagefter, og bemærk at det har et par parenteser på - det er fordi en action er en speciel form for funktion, og funktioner defineres altid med paranteser (vi kommer senere ind på funktioner, og hvad disse paranteser egentlig kan bruges til). Husk derfor paranteserne når du laver en action!

Herefter kommer en kodeblok, afgrænset af { og }. Koden i denne kodeblok bliver kørt så snart en entity med din action bliver "lavet" - dvs. så snart spillet starter, når det gælder entities du har sat ind i WED. Den eneste kommando som bliver kørt her, er set(my,TRANSLUCENT);, som gør din model gennemsigtig: alle entities har en række egenskaber (kaldet flags) som kan være slået til eller fra, og kommandoen set slår et flag til. set-kommandoen skal have to parametre: først skal den have at vide, hvem der skal have slået et flag til, og derudover skal den vide hvilket flag, der skal slås til. Den første parameter sættes til my, fordi det er den entity, som har fået vores action, der skal gøres gennemsigtig. Den næste parameter sættes til TRANSLUCENT, dvs. vi vælger det flag, der bestemmer om en entity er gennemsigtig.

Navngivning når du programmerer

Når du programmerer er du ofte nødt til at finde på navne: i eksemplet ovenfor døbte vi en action min_forste_action. Her skal man passe på, for ikke alle bogstaver og tegn kan indgå i navnet! Som hovedregel bør du holde dig til bogstaverne i det engelske alfabet, samt underscore.

Som i dette eksempel, hvor vi vil kalde vores action for "min første action", er vi først nødt til at fjerne eller erstatte danske bogstaver og mellemrum: f.eks. kan man bruge navnene min_forste_action eller MinForsteAction.

Som næste skridt kan vi vælge hvor gennemsigtig vores entity skal være, ved at sætte dens alpha-værdi:

action min_forste_action()
{
	set(my,TRANSLUCENT);    // Gør mig gennemsigtig
	my.alpha = 20;    // Bestem hvor gennemsigtig jeg skal være (på en skala fra 0 til 100)
}

Her angiver 20 hvor gennemsigtig din model skal være (0 er helt usynlig, og 100 er ikke længere gennemsigtig). Læg mærke til at der bliver brugt semikolon, ;, for hver kommando skal afsluttes med semi-kolon, når det ikke efterfølges af en kode-blok. Bemærk at alpha-værdien kun kan bruges hvis du har slået TRANSLUCENT til.

Andre flags du kan sætte er f.eks. INVISIBLE (usynlig) og PASSABLE (så man kan gå lige igennem). Udover flags kan du også prøve at sætte lightrange (f.eks. my.lightrange = 300;, her kan du bruge alle positive tal), samt red, green og blue (farve-værdierne skal ligge mellem 0 og 255).

I toolbaren i SED kan du bruge Run current script-knappen (den første af de tre trekanter) til at køre dit spil, i stedet for at bruge Run i WED. Når du kører dit spil kan du trykke Tab for at få en "konsol"/tekstfelt frem, som du kan skrive kommandoer i: her kan du ikke bruge my som i din action, da konsollen ikke ved hvilken entity du mener - men den entity man styrer når man spiller har et bestemt navn: player. Derfor kan du f.eks. sætte player.lightrange = 300; i konsollen.

Desuden skal det nævnes, at du kan slå et flag fra igen med reset-kommandoen, der bliver brugt ligesom set-kommandoen.

Betingelser og skills

For at vi kan lave noget, som egentlig kan bruges til noget, skal vi have introduceret nogle kontrolstrukturer, og vi starter med betingelser (conditions).

En betingelser bruges til at udføre kommandoer hvis et udsagn er sandt. Lad os tage et eksempel:

action min_forste_action()
{
	// Tjek om skill1 er sat til en værdi i WED, som er større end 0
	if(my.skill1 > 0)
	{
		set(my,TRANSLUCENT);	// Gør mig gennemsigtig
		my.alpha = 20;			// Bestem hvor gennemsigtig jeg skal være (på en skala fra 0 til 100)
	}
}

Som det ses her, bruger vi ordet if til at lave en betingelse, og i parantesen efter if skriver vi vores udsagn, som enten kan være sandt eller falskt. Efter if og udsagnet har vi en kodeblok (og derfor intet ;!), og koden i denne blok bliver kun kørt hvis udsagnet er sandt. Udsagnet er i dette tilfælde my.skill1 > 0, dvs. kode-blokken bliver kun kørt, hvis den værdi, som skill1 er sat til i WED, er større end 0.

Skill1 er en såkaldt variabel, som er knyttet til din entity og indeholder en værdi - som udgangspunkt er har den værdien 0, med mindre du sætter en anden værdi i WED. I alt er der 100 forskellige skills (skill1, skill2, ..., skill100), men kun værdien for de første 20 kan sættes i WED. Her er et eksempel på hvordan de kan bruges:

action test_af_skills()
{
	my.skill1 = 10;	// Skill1 er nu 10
	my.skill2 = 15;	// Skill2 er nu 15
	
	my.skill3 = my.skill1 + my.skill2;	// Skill3 er nu 25
	my.skill2 = my.skill2 - my.skill1;	// Skill2 er nu 5
	
	my.skill1 -= my.skill2;	// Skill1 er nu 5
	my.skill1 += 5;	// Skill1 er nu 10
	my.skill1 *= 4;	// Skill1 er nu 40
	
	my.skill42 = my.skill1 + 2 * my.skill2; // Skill42 er nu 50
}

Læg mærke til hvordan +=, -=, osv. virker - det er "forkortelser" for f.eks. my.skill1 = my.skill1 + 5;. * bruges som gange-symbol. Lad os nu prøve at sætte alpha-værdien vha. skill1:

action min_forste_action()
{
	// Tjek om skill1 er sat til en værdi i WED, som er større end 0 og mindre end 100
	if((my.skill1 > 0) && (my.skill1 < 100))
	{
		set(my,TRANSLUCENT);
		my.alpha = my.skill1;
	}
}

Men det er ikke det eneste, der er ændret: i stedet for ét enkelt udsagn i vores betingelse, har vi nu to, og mellem de to udsagn står &&, der betyder "og". Man kan altså læse betingelsen som: Hvis udsagn 1 er sandt og udsagn 2 er sandt kør kodeblokken. På samme måde kan man bruge ||, der betyder "eller".

Udover at køre en kodeblok når en betingelse er opfyldt, kan man bruge else til at køre en anden kodeblok hvis betingelsen ikke er opfyldt:

action min_forste_action()
{
	my.lightrange = 300;
	
	if(my.skill1 == 1)
	{
		// Rødt lys
		my.red = 255;
		my.green = 0;
		my.blue = 0;
	}
	else
	{
		// Blåt lys
		my.red = 0;
		my.green = 0;
		my.blue = 255;
	}
}

Her vil lyset enten være rødt eller blåt, og kun én af kodeblokkene vil blive kørt. Læg mærke til udsagnet i betingelsen - her bruges == til at sammenligne: hvis du kun bruger = vil du ændre værdien i my.skill1 (sætte den til 1), men her vi vil kun sammenligne my.skill1 med værdien 1. Udsagnet er dermed sandt, hvis de tog værdier har samme værdi. Modsat kan man bruge != i stedet, for at tjekke om to værdier er forskellige. > og < betyder hhv. større end og mindre end, mens >= og <= betyder hhv. større end eller lig med og mindre end eller lig med.

Til sidst skal det nævnes, at man kan bruge else if til at opstille en ny betingelse, i tilfælde af at den første betingelse ikke blev opfyldt:

action min_forste_action()
{
	my.lightrange = 300;
	
	if(my.skill1 == 1)
	{
		// Rødt lys
		my.red = 255;
		my.green = 0;
		my.blue = 0;
	}
	else if(my.skill1 == 2)
	{
		// Grønt lys
		my.red = 0;
		my.green = 255;
		my.blue = 0;
	}
	else
	{
		// Blåt lys
		my.red = 0;
		my.green = 0;
		my.blue = 255;
	}
}

Her vil kun én af de tre kodeblokke blive kørt.

Loops og wait

Før vi rigtig kan gå i gang med at lave noget sjovt, er vi nødt til også at se på loops (eller løkker på dansk), der bruges til at køre en kodeblok flere gange. Der findes flere slags loops, men vi nøjes med det simpleste og vigtigste - et while-loop:

action min_action()
{
	// Start med at nulstille skill1
	my.skill1 = 0;
	
	// Kør kodeblokken indtil skill1 er 50
	while(my.skill1 < 50)
	{
		my.skill1 += 1;	// Forøg skill1 med 1
	}
	
	// Nu har skill1 værdien 50
}

Ligesom med de såkaldte if-sætninger (betingelser), bliver ordet while efterfulgt af et udsagn, som kan være sandt eller falskt. Men hvor kodeblokken for en if-sætning kun bliver kørt én gang hvis udsagnet er sandt, så bliver kodeblokken i et loop kørt så længe udsagnet er sandt. Dvs. koden i kodeblokken skal sørge for at ændre udsagnet, så det til sidst bliver falskt - ellers ender du med at få et uendeligt loop. I kode-eksemplet ovenfor lægges 1 til skill1 hver gang kodeblokken køres, og kodeblokken bliver kørt indtil udsagnet my.skill1 < 50 holder op med at være sandt (dvs. kodeblokken er kørt 50 gange når skill1 til sidst har værdien 50).

Normalt skal man passe på, at man ikke laver uendelige loops når man programmerer, da de får ens program til at gå i stå, men lige præcis med 3D Gamestudio bruger man ofte loops med udsagn der altid er sande. Grunden til dette skyldes wait-kommandoen, der sørger for at ens program ikke går i stå. Lad os se et eksempel:

action lampe()
{
	// Vent indtil "player"-pointeren er sat
	while(player == NULL) {wait(1);}
	
	// Et uendeligt loop: udsagnet (1) er en forkortelse for (1 == 1),
	// dvs. "så længe 1 er lig med 1", hvilket altid er sandt
	while(1)
	{
		// Tjek om player er inden for afstanden 200
		if(vec_dist(my.x,player.x) < 200)
		{
			// Lys hvis player er tæt på
			my.lightrange = 300;
		}
		else
		{
			// Sluk lyset hvis player længere væk
			my.lightrange = 0;
		}
		
		// Vent 1 frame. VIGTIGT når man har et uendeligt loop!
		wait(1);
	}
}

Start med at prøve koden af på en entity i dit level. Kommentarerne forklarer det meste, men enkelte dele kræver lidt mere forklaring. Funktionen vec_dist måler afstanden mellem to punkter (længden af forskellen mellem to stedvektorer), i dette tilfælde din entity's position og players position (når man skriver my.x eller player.x som parameter til en funktion, der kræver en vektor, så bliver y- og z-komponenterne automatisk sendt med).

En anden linie der kræver lidt forklaring er while(player == NULL) {wait(1);}, og det er lidt mere teknisk: Når vi længere nede i koden refererer til player, så får vi en fejl hvis vi ikke har fortalt hvilken entity, der skal være player. Dette bliver gjort med koden player = my; i action t_player (se 3DGS/include/t_player.c). Hvis du ikke har en entity med action t_player i dit level, får du derfor en fejl hvis du ikke har denne linie i action lampe. Selv hvis du har en entity med action t_player kan du få fejlen alligevel, hvis du er så uheldig at koden i action lampe bliver kørt før koden i t_player - derfor har vi denne linie i action lampe, som står og venter på at player = my; i t_player bliver kørt, før vi begynder at køre koden i vores while(1)-loop.

Som sagt bliver koden i loop'et kørt igen og igen, indtil vi lukket spillet ned, men vores wait(1); går ind og "bremser" koden: den kode som kommer efter denne kommando (dvs. næste gennemløb af loop'et), får først lov til at blive kørt i næste frame - og derfor "fryser" spillet ikke selvom vi i princippet har lavet et uendeligt loop. En frame er det billede, der bliver tegnet på skærmen - du har sandsynligvis hørt om framerate eller FPS (frames per second / billeder per sekund).

Lad os udvide koden i loop'et lidt:

	while(1)
	{
		// Tjek om player er inden for en afstanden 200
		if(vec_dist(my.x,player.x) < 200)
		{
			// Tænd langsomt for lyset
			if(my.lightrange < 300)
			{
				my.lightrange += 10 * time_step;
			}
		}
		else
		{
			// Sluk langsomt for lyset
			if(my.lightrange > 0)
			{
				my.lightrange -= 10 * time_step;
			}
		}
		
		// Vent 1 frame. VIGTIGT når man har et uendeligt loop!
		wait(1);
	}

Nu bliver der "skruet" op og ned for lyset, og der er kun en enkel ny ting: time_step. I princippet behøver vi ikke at gange med time_step, og vi kunne sagtens bruge f.eks. my.lightrange += 1; i stedet, men husk på, at denne kode bliver kørt én gang per frame. Dvs. den hastighed som lysstyrken bliver skruet op eller ned med kommer til at afhænge af din framerate, dvs. kører du med 300 FPS vil det tage 1 sekund at nå fra 0 til 300, og kører du kun med 30 FPS vil det tage 10 sekunder - når du ganger med time_step, så får du "udlignet" denne forskel, så det går lige hurtigt uanset hvilken framerate spillet kører med.

Du har nu set et while-loop, men der findes bl.a. også for-loops, der kan anvendes hvis du f.eks. ved præcis hvor mange gange en kodeblok skal gentages - men dette springer vi over her, da du oftest har brug for et while-loop i forbindelse med 3DGS (desuden kan man altid bruge et while-loop i stedet).

Flere eksempler

Med udgangspunkt i det du har lært indtil nu, er her nogle eksempler på andre actions.

Teleport / portal

Her ses en action, der flytter player til et andet sted i dit level, når man kommer tæt nok på den entity der får denne action:

action teleporter()
{
	// Vent indtil "player"-pointeren er sat
	while(player == NULL) {wait(1);}
	
	// Et uendeligt loop: udsagnet (1) er en forkortelse for (1 == 1),
	// dvs. "så længe 1 er lig med 1", hvilket altid er sandt
	while(1)
	{
		// Tjek om player er indenfor afstanden 100
		if(vec_dist(my.x,player.x) < 100)
		{
			// Tilføj en simpel effekt til vores teleporter!
			while(camera.aspect > 0.1)
			{
				camera.aspect -= 0.2 * time_step;
				wait(1);
			}
			
			// Player er tæt på vores teleporter, så flyt player til den position, som skill1-3 angiver.
			// Dette kan også gøres ved at bruge "vec_set(player.x,my.skill1);" i stedet.
			player.x = my.skill1;
			player.y = my.skill2;
			player.z = my.skill3;
			
			// Gå tilbage til "normal" tilstand
			while(camera.aspect < 1)
			{
				camera.aspect += 0.2 * time_step;
				wait(1);
			}
			
			// Vær sikker på at vi ender på præcis 1 igen, som er standardværdien.
			camera.aspect = 1;
		}
		
		// Vent 1 frame. VIGTIGT når man har et uendeligt loop!
		wait(1);
	}
}

Sørg for at sætte skill1, skill2 og skill3 på den entity, som du har givet denne action, til henholdsvis x-, y- og z-koordinaten på den position, som player skal teleporteres (flyttes) hen til. Du kan finde koordinaterne i WED, f.eks. ved at indsætte en model i WED, flytte den derhen hvor du vil have player teleporteret hen, og aflæse positionen (Local position) på Position-fanen af Object Properties-vinduet (højre-klik og vælg Properties) for denne entity (når du har aflæst koordinaterne kan du bare slette denne entity igen, hvis du ikke skal bruge den til andet).

Læg mærke til at denne action ligner den lampe-action vi så på tidligere, hvor vi først venter indtil vi har en player, og derefter laver et uendeligt loop (som indeholder en wait-kommando!). Hver gang koden i det uendelige loop gentages (dvs. 1 gang for hver frame) tjekker vi om player befinder sig indenfor en afstand af 100 fra den entity, som har fået action teleporter. Hvis player befinder sig så tæt på, sker der flere ting: først ændres camera.aspect "langsomt", indtil det er tæt på 0, dernæst flytter vi player ved simpelthen bare at ændre x-, y- og z-koordinaterne, og til sidst ændres camera.aspect "langsomt" tilbage til 1. Bemærk dog at loopet fortsætter så længe camera.aspect < 1, og da time_step kan antage alle mulige værdier, vil camera.aspect sandsynligvis ende på noget andet end præcis 1, f.eks. 1.001, og derfor er vi nødt til bagefter at sætte den til præcis 1.

Du kan evt. prøve at lege med camera.aspect i konsollen (dvs. ved at trykke Tab når du er inde i dit spil). Skriv f.eks. camera.aspect = 0.5 og tryk Enter. Du kan bruge værdier fra -2 til 2, og standardværdien er som sagt 1. Du kan også prøve at ændre camera.arc (værdier fra 1 til 175, standardværdi 60).

Bemærk også at vi har wait(1); stående tre steder her - det ene sted (til sidst i det uendelige loop) er en nødvendighed når vores loop netop er uendeligt, men hvorfor behøver vi de to andre wait-kommandoer? Prøv evt. at fjerne dem (begge to eller kun den ene), for at undersøge hvilken forskel de gør!

Prøv evt. at ændre action teleporter() så du laver en anden effekt med camera.arc i stedet for camera.aspect. Her skal du huske, at hvor vi ændrer camera.aspect fra 1 til ca. 0.1, og dernæst tilbage til 1, skal camera.arc ændres til f.eks. 20 eller 150, og bagefter sættes tilbage til standardværdien 60.

Udover at ændre positionen på player, kan du også ændre den retning, som player kigger i efter teleport: ligesom vi ændrer players koordinater, skal du bare ændre camera.pan (det er en vinkel angivet i grader).

Fælder og Farlige Områder - Ændring og visning af player liv

Med udgangspunkt i koden for action lampe og teleporter ovenfor, er det nu relativt nemt at lave et "farligt område", hvor man mister liv hvis man kommer for tæt på (f.eks. hvis man går for tæt på noget ild - selvom dét at lave ild vil kræve enten partikel-effekter, som vi først kommer til i et senere afsnit, eller animerede sprites, som vi ikke kommer ind på).

Princippet er egentlig ret simpelt: i template-koden indeholder variablen t_players_health det liv ("health") man har. Dvs. man starter som udgangs punkt med 100 liv, og hvis t_players_health ender nede på 0 eller mindre, så dør man. Derfor kan man nemt lave en fælde eller et farligt område sådan:

action farligt_omraade()
{
	// Vent indtil "player"-pointeren er sat
	while(player == NULL) {wait(1);}
	
	while(1)
	{
		// Tjek om player er indenfor afstanden 100
		if(vec_dist(my.x,player.x) < 100)
		{
			// Player mister liv, når han er for tæt på
			t_players_health -= 5 * time_step;
		}
		
		// Vent 1 frame. VIGTIGT når man har et uendeligt loop!
		wait(1);
	}
}

Du kan også ændre koden, så player altid dør med det samme, hvis han kommer for tæt på (det ville f.eks. give mening hvis man falder ned i et dybt hul, så du sørger for, at man dør når man rammer bunden - dette kan du gøre ved at sætte en usynlig (invisible) entity derned med denne action): t_players_health = 0;

Omvendt kan du også ændre koden, sådan at man får mere (i stedet for mindre) liv af at stå tæt på - en "healing zone", eller hvad du vil kalde det. Hvis du gør dette, så bør du nok sætte en grænse for, hvor meget liv man kan få - du skal altså bruge en if-sætning mere (eller to udsagn i den if-sætning du allerede har), for at sørge for, at t_players_health ikke bliver større end f.eks. 100.

For at man kan se hvor meget liv man har, kan du bruge følgende kode:

// Et panel der viser hvor meget liv man har
PANEL* livpanel =
{
	pos_x = 5;	// X-koordinat på skærmen
	pos_y = 5;	// Y-koordinat på skærmen
	digits(0,0,5,"Arial#24b",1,t_players_health);	// Vis hvor meget liv man har
	flags = SHOW;	// Vis panelet på skærmen fra start
}

Vi ser på paneler og andre 2D-elementer i et senere afsnit.

Blinkende lamper

Forestil dig et spil som f.eks. Half-life, eller noget lignende, hvor man render rundt i nogle forladte korridorer eller laboratorier, efter et mislykket eksperiment, som har forårsaget stor skade på hele den hemmelige base, hvor du befinder dig... I sådan et tilfælde kan man sagtens forestille sig, at mange af lamperne er gået i stykker: nogle lyser måske slet ikke, mens andre måske står og blinker lidt, og kaster gnister fra sig... Her er et eksempel fra et af mine gamle projekter:

Lampe med gnister

At lave gnister fra en lampe kræver partikel-effekter, som du lærer om senere, men selve den blinkende lampe er let nok at lave med det, du har lært nu:

action blinkende_lampe()
{
	while(1)
	{
		// Sæt lysstyrke (lys-radius) til et tilfældigt tal mellem 150 og 200
		my.lightrange = 150 + random(50);
		
		// Vent mellem 0.01 og 0.51 sekunder
		wait(-0.01 - random(0.5));
		
		// Sæt lysstyrken til et tilfældigt tal mellem 0 og 100
		my.lightrange = random(100);
		
		// Vent mellem 0.01 og 1.01 sekunder, indtil loopet køres igen
		wait(-0.01 - random(1));
	}
}

Der er kun to nye ting her: først og fremmest random-funktionen, der giver dig et tilfældigt tal (f.eks. giver random(5) et tilfældigt decimaltal mellem 0 og 5), og så bliver der brugt negative tal i wait-funktionen. Hvis man kalder wait-funktionen med et positivt tal, går der det antal frames før den efterfølgende kode bliver kørt, f.eks. vil wait(5); gøre, at den efterfølgende kode først bliver kørt, når der er tegnet 5 billeder på skærmen (5 frames). Hvis du derimod kalder wait med et negativt tal, ventes i stedet dette antal sekunder, dvs. med wait(-1); ventes 1 sekund, før den efterfølgende kode køres, med wait(-5); ventes 5 sekunder, wait(-0.5); venter et halvt sekund, osv.

Du kan evt. prøve at ændre på tallene, indtil du synes, at den blinkende lampe passer til lige præcis dit spil. Samme script vil desuden også kunne bruge til fakler, der står og blafrer (men igen kræver det partikel-effekter at lave ild og røg).

Hvad kan man ellers lave?

Med det programmering, du har lært indtil nu, er det stadig lidt begrænset, hvad du kan lave. I næste afsnit introduceres du til et par andre grundlæggende koncepter i programmering, og i det efterfølgende afsnit er du for alvor klar til at lære om nogle af de lidt mere interessante ting, som f.eks. 2D-elementer, partikel-effekter, animation, lyd, bevægelse med collision-detection, ray-tracing, og meget andet.

Relaterede ressourcer
Acknex Unlimited

Mange ressourcer til 3D Gamestudio.

Se mere her

3D Gamestudio

Game-engine til udvikling af 3D-spil.

Se mere her

×