So, ich hab mal vor Monaten versprochen ein Tutorial ins netzt zu stellen, wie man eine Minimap macht. Gut, heute ist soweit. Folgende vorraussetzungen sind nötig, um das Teil in seinem Projekt einzubauen:
Gut fangen wir mal an. Eine Minimap ist ein zweidimensionales Array, welches informationen beinhaltet, wie ein Raum aufgebaut ist. Damit die Karte weiß, wie ein Raum aussieht, muß jeder Raum Informationen beinhalten, wie dieser aussieht. Sprich seine Position innerhalb der Minimap, seine Wände, seine Außgänge. Jeder Raum muß ein Script beinhalten, das so aussieht:
Dieses Script muß im Create Event eines Rooms aufgerufen werden. Man kann es direkt ins Room Create Event reingeben, jedoch zur Übersichtlicheren Gestalltung würde ich eine Funktion anlegen die ca so heißt: <SCR_CreateTeleportData_*Raumname*>.
Dieser Raum, ist in diesem Fall einen Bildschirm Breit und 4 Bildschirme Hoch. Es gibt zwei Ausgänge, an der Linken Seite.
C_ROOM_NONE ist eine Konstante. Sie hat den Wert -1. Eigentlich dient sie nur der übersichtlichkeit, die Runtime wird dadurch nicht beeinträchtigt, da ohnehin alle werte in Bytecode verwandelt werden sollten, bevor das Spiel gestartet wird (yeah, Technobabble).
Alles anzeigen
Einige Weitere Konstanten die Benötigt werden:
C_ROOM_SIZE_X 320
C_ROOM_SIZE_Y 240
C_ROOM_OFFSET 32
Das bedeutet nichts anderes, als das ein Raum 320 Pixel Breit und 240 Hoch ist, oder ein Vielfaches davon. Das Offset ist die Zusatzbreite, die man dann links, rechts, oben und unten noch dazumacht. Das hat den Grund, damit die Spielfigur den Raum etwas verlassen kann (ca. bis zur Hälfte), bevor der Teleport durchgeführt wird. Mit hilfe einer Simplen ViewControll kann man verhindern, das die Kamera diesen bereich einsieht:
Alles anzeigen
Gut, um die Teleports richtig durchzuführen brauchen wir einen Teleport Handler. Das ist ein unsichtbares, Persistentes Objekte, das überprüft ob der das Spielerobjekt einen Raum verlassen hat, und führt dann den Teleport durch.
Folgende Funktionen werden dafür benötigt:
Im Create Event:
Erklärung:
Im Create Event werden Variablen gesetzt die für das Durchführen des Teleports wichtig sind.
Im Stepevent:
Alles anzeigen
Erklärung:
Im Step Event wird überprüft ob das Spielerobjekt den Raum verläßt. Wenn dem so ist, wird festgestellt, wohin man Teleportieren muß. Das automatische Zeichnen wird deaktiviert, um ein häßliches Flackern zu verhindern, da die Spielfigur beim wiedereintritt in den neuen Raum an einer völlig falschen Position ist.
Im Begin Step Event:
Alles anzeigen
Erklärung:
Man muß ingesammt einen Step warte, damit das Room Create Event des neuen Raums aufgerufen wird, damit man die Aktuelle Position der Spielfigur weiß. Danach wird das Automatische Zeichnen wieder aktiviert.
Wer kein Interesse an einer Minimap hat, der Teil bis hierher ist alleine Lauffähig und kann als Reine Teleport Engine benutzt werden.
Gut, wems bis jetzt zu einfach war, jetzt kommt der Kompliziertere Teil :3.
Ein paar neue Konstanten die nötig sind:
C_MM_MAX_X 64 -> Maximale Größe der Minimap
C_MM_MAX_Y 64
C_MM_POS_X 284 -> Position am Bildschirm
C_MM_POS_Y 12
Gut, mal der Logische Teil der Minimap. Wie schon erwähnt, eine Minimap ist ein 2 Dimensionales Array, wobei jeder eintrag in dem Array genau einen Bildschirm darstellt. Ein Raum, der 3 Bildschirme hoch ist, beinhaltet auch 3 Felder im Array.
Ein Feld des Arrays beinhaltet eine vierstellige Zahl, welche wiefolgt aufgebaut ist
0 -> Oben
0 -> Rechts
0 -> Unten
0 -> Links
wobei Jede Stelle folgende Zustände haben kann
0 -> Nichts
1 -> Wand
2 -> Türe
Ein Raum der genau einen Bildschirm groß ist und eine Türe oben hätte sieht so aus:
2111
Um nicht wahnsinnig bei den Zuweisungen zu werden habe ich ein Trinäres Zahlensystem (ähnlich Binär, nur mit Basis 3) entwickelt, um die Zuweisungen leichter zu machen. Das ganze geht ca so: Ich jabe jetzt einen Raum, sagen wir mal mit den werten 0021. Wenn man das als Trinäre Zahl hernimmt und auf Dezimal umrechnet kommt 7 Raus. In einem Array, welchesl alle möglichen Sprites beinhaltet, um die Minimap darzustellen ist auf der Position 7 ein Sprite welches oben und rechts keine Wand hat, unten ist eine Türe und links eine Wand.
Für das Trinäre Zahlensystem benötigt man zwei Funktionen:
SCR_B3toINT
Alles anzeigen
SCR_INTtoB3
Alles anzeigen
Die Beiden Funktionen verwandeln einen Integer (eine ganzzahlige ... Zahl) in eine Trinäre Zahl und vize versa.
Diese Funktion erzeugt alle nötigen Sprites für die Minimaps. Diese Art eine Minimap darzustellen verbraucht zwar mehr speicher, aber ist um einiges Schneller zur laufzeit, anstatt alle Sprites einzeln aufzuzeichnen.
SCR_Minimap_CreateSprite
Alles anzeigen
Diese Funktion initialisiert die Minimap:
SCR_Minimap_Create
Alles anzeigen
Mit hilfe Dieser Funktion wird am Ende eines Steps überprüft ob man sich in einem Bereich des Map Arrays befindet, das noch nicht Definiert ist. Wenn dem so ist, wird ein Wert gesetzt, welcher aus den Daten der Teleporterfunktionen zusammengestellt wird.
SCR_Minimap_StepEnd
Alles anzeigen
So, jetzt gehört das ganze noch gezeichnet:
SCR_Minimap_Draw
Alles anzeigen
Die Funktionen SCR_Minimap_Create gehört ins Create Event, StepEnd ins StepEnd Event und Draw ins Draw Event eines Minimap Objekts, welches persistent ist.
Jo, das wars in etwa. Im anhang findet ihr das ganze noch mal als GML Datei.
- Gamemaker 6.1 (registriert)
- GML Wissen - Fortgeschritten
- Ausdauer, um alles durchzulesen
Allgemeines
Gut fangen wir mal an. Eine Minimap ist ein zweidimensionales Array, welches informationen beinhaltet, wie ein Raum aufgebaut ist. Damit die Karte weiß, wie ein Raum aussieht, muß jeder Raum Informationen beinhalten, wie dieser aussieht. Sprich seine Position innerhalb der Minimap, seine Wände, seine Außgänge. Jeder Raum muß ein Script beinhalten, das so aussieht:
Dieses Script muß im Create Event eines Rooms aufgerufen werden. Man kann es direkt ins Room Create Event reingeben, jedoch zur Übersichtlicheren Gestalltung würde ich eine Funktion anlegen die ca so heißt: <SCR_CreateTeleportData_*Raumname*>.
Dieser Raum, ist in diesem Fall einen Bildschirm Breit und 4 Bildschirme Hoch. Es gibt zwei Ausgänge, an der Linken Seite.
C_ROOM_NONE ist eine Konstante. Sie hat den Wert -1. Eigentlich dient sie nur der übersichtlichkeit, die Runtime wird dadurch nicht beeinträchtigt, da ohnehin alle werte in Bytecode verwandelt werden sollten, bevor das Spiel gestartet wird (yeah, Technobabble).
GML-Quellcode
- {
- global.Absolute_X = 4; //Raumposition innerhalb der Karte
- global.Absolute_Y = 1; //Raumposition innerhalb der Karte
- global.Trans_Top[0] = C_ROOM_NONE;
- global.Trans_Right[0] = C_ROOM_NONE;
- global.Trans_Right[1] = C_ROOM_NONE;
- global.Trans_Right[2] = C_ROOM_NONE;
- global.Trans_Right[3] = C_ROOM_NONE;
- global.Trans_Bottom[0] = C_ROOM_NONE;
- global.Trans_Left[0] = C_ROOM_NONE;
- global.Trans_Left[1] = ROOM_TestRealm_04;
- global.Trans_Left[2] = C_ROOM_NONE;
- global.Trans_Left[3] = ROOM_TestRealm_02;
- }
Einige Weitere Konstanten die Benötigt werden:
C_ROOM_SIZE_X 320
C_ROOM_SIZE_Y 240
C_ROOM_OFFSET 32
Das bedeutet nichts anderes, als das ein Raum 320 Pixel Breit und 240 Hoch ist, oder ein Vielfaches davon. Das Offset ist die Zusatzbreite, die man dann links, rechts, oben und unten noch dazumacht. Das hat den Grund, damit die Spielfigur den Raum etwas verlassen kann (ca. bis zur Hälfte), bevor der Teleport durchgeführt wird. Mit hilfe einer Simplen ViewControll kann man verhindern, das die Kamera diesen bereich einsieht:
GML-Quellcode
- {
- //if(instance_number(OBJ_Hero)<1)
- // exit;
- view_xview[0] = round(global.PlayerObject.x-(C_ROOM_SIZE_X/2));
- view_yview[0] = round(global.PlayerObject.y-(C_ROOM_SIZE_Y/2));
- if(view_xview[0]<C_ROOM_OFFSET)
- {
- view_xview[0] = C_ROOM_OFFSET;
- }
- else
- if(view_xview[0] > (room_width - (C_ROOM_SIZE_X + C_ROOM_OFFSET)) )
- {
- view_xview[0] = room_width - (C_ROOM_SIZE_X + C_ROOM_OFFSET);
- }
- if(view_yview[0]<C_ROOM_OFFSET)
- {
- view_yview[0] = C_ROOM_OFFSET;
- }
- else
- if(view_yview[0]> (room_height - (C_ROOM_SIZE_Y + C_ROOM_OFFSET)) )
- {
- view_yview[0] = room_height - (C_ROOM_SIZE_Y + C_ROOM_OFFSET);
- }
- }
Der Teleport
Gut, um die Teleports richtig durchzuführen brauchen wir einen Teleport Handler. Das ist ein unsichtbares, Persistentes Objekte, das überprüft ob der das Spielerobjekt einen Raum verlassen hat, und führt dann den Teleport durch.
Folgende Funktionen werden dafür benötigt:
Im Create Event:
GML-Quellcode
Erklärung:
Im Create Event werden Variablen gesetzt die für das Durchführen des Teleports wichtig sind.
Im Stepevent:
GML-Quellcode
- {
- var bs;
- if(
- (global.PlayerObject.x>=C_ROOM_OFFSET) &&
- (global.PlayerObject.x<=(room_width-C_ROOM_OFFSET)) &&
- (global.PlayerObject.y>=C_ROOM_OFFSET) &&
- (global.PlayerObject.y<=(room_height-C_ROOM_OFFSET))
- )
- return 0; //Inside the room, nothing else to do
- else
- if(global.PlayerObject.x>(room_width-C_ROOM_OFFSET))
- global.CurrentTeleport = global.Trans_Right[floor((global.PlayerObject.y-C_ROOM_OFFSET)/C_ROOM_SIZE_Y)];
- else
- if(global.PlayerObject.x<C_ROOM_OFFSET)
- global.CurrentTeleport = global.Trans_Left[floor((global.PlayerObject.y-C_ROOM_OFFSET)/C_ROOM_SIZE_Y)];
- else
- if(global.PlayerObject.y>(room_height-C_ROOM_OFFSET))
- global.CurrentTeleport = global.Trans_Bottom[floor((global.PlayerObject.x-C_ROOM_OFFSET)/C_ROOM_SIZE_X)];
- else
- if(global.PlayerObject.y<C_ROOM_OFFSET)
- global.CurrentTeleport = global.Trans_Top[floor((global.PlayerObject.x-C_ROOM_OFFSET)/C_ROOM_SIZE_X)];
- else
- global.CurrentTeleport = 0;
- JustTeleported=false;
- if(global.CurrentTeleport)
- {
- room_goto(global.CurrentTeleport);
- global.CurrentRoom = global.CurrentTeleport;
- global.JustTeleported=true;
- global.CurrentTeleport = 0;
- set_automatic_draw(false);
- global.Old_Absolute_X = global.Absolute_X;
- global.Old_Absolute_Y = global.Absolute_Y;
- }
- }
Erklärung:
Im Step Event wird überprüft ob das Spielerobjekt den Raum verläßt. Wenn dem so ist, wird festgestellt, wohin man Teleportieren muß. Das automatische Zeichnen wird deaktiviert, um ein häßliches Flackern zu verhindern, da die Spielfigur beim wiedereintritt in den neuen Raum an einer völlig falschen Position ist.
Im Begin Step Event:
GML-Quellcode
Erklärung:
Man muß ingesammt einen Step warte, damit das Room Create Event des neuen Raums aufgerufen wird, damit man die Aktuelle Position der Spielfigur weiß. Danach wird das Automatische Zeichnen wieder aktiviert.
Wer kein Interesse an einer Minimap hat, der Teil bis hierher ist alleine Lauffähig und kann als Reine Teleport Engine benutzt werden.
Gut, wems bis jetzt zu einfach war, jetzt kommt der Kompliziertere Teil :3.
Die Minimap.
Ein paar neue Konstanten die nötig sind:
C_MM_MAX_X 64 -> Maximale Größe der Minimap
C_MM_MAX_Y 64
C_MM_POS_X 284 -> Position am Bildschirm
C_MM_POS_Y 12
Gut, mal der Logische Teil der Minimap. Wie schon erwähnt, eine Minimap ist ein 2 Dimensionales Array, wobei jeder eintrag in dem Array genau einen Bildschirm darstellt. Ein Raum, der 3 Bildschirme hoch ist, beinhaltet auch 3 Felder im Array.
Ein Feld des Arrays beinhaltet eine vierstellige Zahl, welche wiefolgt aufgebaut ist
0 -> Oben
0 -> Rechts
0 -> Unten
0 -> Links
wobei Jede Stelle folgende Zustände haben kann
0 -> Nichts
1 -> Wand
2 -> Türe
Ein Raum der genau einen Bildschirm groß ist und eine Türe oben hätte sieht so aus:
2111
Um nicht wahnsinnig bei den Zuweisungen zu werden habe ich ein Trinäres Zahlensystem (ähnlich Binär, nur mit Basis 3) entwickelt, um die Zuweisungen leichter zu machen. Das ganze geht ca so: Ich jabe jetzt einen Raum, sagen wir mal mit den werten 0021. Wenn man das als Trinäre Zahl hernimmt und auf Dezimal umrechnet kommt 7 Raus. In einem Array, welchesl alle möglichen Sprites beinhaltet, um die Minimap darzustellen ist auf der Position 7 ein Sprite welches oben und rechts keine Wand hat, unten ist eine Türe und links eine Wand.
Für das Trinäre Zahlensystem benötigt man zwei Funktionen:
SCR_B3toINT
GML-Quellcode
SCR_INTtoB3
GML-Quellcode
Die Beiden Funktionen verwandeln einen Integer (eine ganzzahlige ... Zahl) in eine Trinäre Zahl und vize versa.
Diese Funktion erzeugt alle nötigen Sprites für die Minimaps. Diese Art eine Minimap darzustellen verbraucht zwar mehr speicher, aber ist um einiges Schneller zur laufzeit, anstatt alle Sprites einzeln aufzuzeichnen.
SCR_Minimap_CreateSprite
GML-Quellcode
- {
- //argument 0 = B3 Code of Map Sprite
- var SRF_Sprite;
- var i;
- var zahl;
- var MM_Array;
- var rv;
- MM_Array[0,0] = SPR_Minimap_0001;
- MM_Array[0,1] = SPR_Minimap_0002;
- MM_Array[1,0] = SPR_Minimap_0010;
- MM_Array[1,1] = SPR_Minimap_0020;
- MM_Array[2,0] = SPR_Minimap_0100;
- MM_Array[2,1] = SPR_Minimap_0200;
- MM_Array[3,0] = SPR_Minimap_1000;
- MM_Array[3,1] = SPR_Minimap_2000;
- zahl = argument0;
- SRF_Sprite = surface_create(8,8);
- surface_set_target(SRF_Sprite);
- draw_clear(make_color_rgb(128,128,128));
- draw_sprite(SPR_Minimap_0000,-1,0,0);
- for(i = 0; i < 4; i+= 1)
- {
- if(zahl mod 10)
- {
- draw_sprite(MM_Array[i,((zahl mod 10) - 1)],-1,0,0);
- }
- //show_message(string(argument0) + ':' + string(zahl mod 10));
- zahl = (zahl - (zahl mod 10))/10;
- }
- surface_reset_target();
- rv = sprite_create_from_surface(SRF_Sprite,0,0,8,8,false,false,false,true,0,0);
- surface_free(SRF_Sprite);
- return rv;
- }
Diese Funktion initialisiert die Minimap:
SCR_Minimap_Create
GML-Quellcode
Mit hilfe Dieser Funktion wird am Ende eines Steps überprüft ob man sich in einem Bereich des Map Arrays befindet, das noch nicht Definiert ist. Wenn dem so ist, wird ein Wert gesetzt, welcher aus den Daten der Teleporterfunktionen zusammengestellt wird.
SCR_Minimap_StepEnd
GML-Quellcode
- {
- global.MiniMap_x = floor(global.Absolute_X + ((OBJ_Player_WalkCircle.x-32)/320));
- global.MiniMap_y = floor(global.Absolute_Y + ((OBJ_Player_WalkCircle.y-32)/240));
- var up,down,left,right;
- if(global.Minimap[global.MiniMap_x,global.MiniMap_y]==-1) //undefined
- {
- if(
- !((OBJ_Player_WalkCircle.x>33)&&
- (OBJ_Player_WalkCircle.x<(room_width-33))&&
- (OBJ_Player_WalkCircle.y>33)&&
- (OBJ_Player_WalkCircle.y<(room_height-33)))
- )
- return 0;
- up = 0;
- down = 0;
- left = 0;
- right = 0;
- //Top of the Room
- if(floor((OBJ_Player_WalkCircle.y-32)/240)==0)
- {
- if(global.Trans_Top[floor((OBJ_Player_WalkCircle.x-32)/320)] == C_ROOM_NONE)
- {
- up = 1;
- }
- else
- {
- up = 2;
- }
- }
- //Bottom of the Room
- if((global.MiniMap_y-global.Absolute_Y)==(floor((room_height-64)/240)-1))
- {
- if(global.Trans_Bottom[floor((OBJ_Player_WalkCircle.x-32)/320)] == C_ROOM_NONE)
- {
- down = 1;
- }
- else
- {
- down = 2;
- }
- }
- //Left of the Room
- if(floor((OBJ_Player_WalkCircle.x-32)/320)==0)
- {
- if(global.Trans_Left[floor((OBJ_Player_WalkCircle.y-32)/240)] == C_ROOM_NONE)
- {
- left = 1;
- }
- else
- {
- left = 2;
- }
- }
- //Right of the Room
- if((global.MiniMap_x-global.Absolute_X)==(floor((room_width-64)/320)-1))
- {
- if(global.Trans_Right[floor((OBJ_Player_WalkCircle.y-32)/240)] == C_ROOM_NONE)
- {
- right = 1;
- }
- else
- {
- right = 2;
- }
- }
- global.Minimap[global.MiniMap_x,global.MiniMap_y] = SCR_B3toINT(up+10*right+100*down+1000*left)
- }
- }
So, jetzt gehört das ganze noch gezeichnet:
SCR_Minimap_Draw
GML-Quellcode
- {
- var i,j;
- draw_set_blend_mode(bm_normal);
- for(i = -3; i< 4; i+=1)
- for(j = -1; j < 2; j+=1)
- {
- if( ((global.MiniMap_x+i)<0) ||
- ((global.MiniMap_y+j)<0) ||
- ((global.MiniMap_x+i)>C_MM_MAX_X) ||
- ((global.MiniMap_y+j)>C_MM_MAX_Y)
- )
- {
- draw_sprite_ext(global.MinimapSpriteArray[0],
- -1,
- view_xview[0]+C_MM_POS_X+i*8,
- view_yview[0]+C_MM_POS_Y+j*8,
- 1,1,0,make_color_rgb(64,64,64),0.5);
- //1,1,0,c_white,1);
- }
- else
- if(global.Minimap[global.MiniMap_x+i,global.MiniMap_y+j] == -1)
- {
- draw_sprite_ext(global.MinimapSpriteArray[0],
- -1,
- view_xview[0]+C_MM_POS_X+i*8,
- view_yview[0]+C_MM_POS_Y+j*8,
- 1,1,0,make_color_rgb(64,64,64),0.5);
- }
- else
- {
- if((i == 0) && (j == 0))
- draw_sprite_ext(global.MinimapSpriteArray[global.Minimap[global.MiniMap_x,global.MiniMap_y]],
- -1,
- view_xview[0]+C_MM_POS_X,
- view_yview[0]+C_MM_POS_Y,
- 1,1,0,make_color_rgb(128,128,255),1);
- else
- draw_sprite_ext(global.MinimapSpriteArray[global.Minimap[global.MiniMap_x+i,global.MiniMap_y+j]],
- -1,
- view_xview[0]+C_MM_POS_X+i*8,
- view_yview[0]+C_MM_POS_Y+j*8,
- 1,1,0,make_color_rgb(64,64,255),1);
- }
- }
- }
Die Funktionen SCR_Minimap_Create gehört ins Create Event, StepEnd ins StepEnd Event und Draw ins Draw Event eines Minimap Objekts, welches persistent ist.
Jo, das wars in etwa. Im anhang findet ihr das ganze noch mal als GML Datei.
...