Kollision auf einer 2d Tilemap
Übersicht

![]() |
DivineDominionBetreff: Kollision auf einer 2d Tilemap |
![]() Antworten mit Zitat ![]() |
---|---|---|
Ich sehe hier dutzende Kollisionsfragen auftauchen, und das macht mir Angst... Ich dneke daher, dass es für mich an der Zeit ist, euch allen zu zeigen, wie sowas verdammt einfach mit einem Array gelöst wird ![]() Also, steigt ein. Teil 1 - die Map Ich gehe davon aus, dass ihr eure Maps in einem Array speichert: Code: [AUSKLAPPEN] Dim map( hoehe, breite )
Das ist die einfachste Methode für Tilemaps; ein Array. Warum erst die Höhe und dann die Breite? Das ist, eigentlich, erst einmal total Wurscht. Ich kann nur aus Erfahrung sprechen und euch sagen: gewöhnt euch lieber an "erst Y und dann X"-Denken, was dem üblichen Koordinatenkram ja völlig widerspricht, denn es kommt öfter vor, dass man nach "Zeilen" sortieren will (also die Y-Achse entlang) als nach "Spalten". Zum Beispiel, wenn ihr auf ein Tile einen großen Baum setzt, der nur dieses eine Tile reserviert, und wenn der Spieler drunterher laufen soll, dann ist es wichtiger, zeilenweise zu vergleichen. Bei BlitzBasic ist das noch recht egal, unter Umständen verlangen andere Sprachen, dass ihr erst alle Zeilen (Y) definiert und die dann mit je einer Zeile Inhalt befüllt, also Arrays verschachtelt. Da wird's erst richtig relevant. Auch, wenn euch das jetzt wohl total egal ist, ich schreibe hier und ich bestimme das halt so! ![]() Für diesen Beitrag nehme ich einfach mal eine Tilegröße von 16x16 Pixel, weil ich das so schön finde ![]() Code: [AUSKLAPPEN] Const TILESIZE = 16
Für das Beispiel ist die Karte 100x100 Tiles groß. Code: [AUSKLAPPEN] Const MAP_WIDTH = 100
Const MAP_HEIGHT = 100 Jetzt gehts ans Eingemachte. Ein wenig Theorie jetzt: Teil 2 - Layer Die meisten die sowas wie Kollision verwenden machen entweder ein Rollenspiel oder ein Jump&Run. Für Letzteres sind mehrere Layer (zu Deutsch: "Ebene" oder meinetwegen auch "Schicht") nicht zwingend notwendig, aber gewiss ne nette Sache. Darum gehe ich vom komplexeren Fall aus, einem Rollenspiel - oder einem beliebigen anderen Spiel mit mehreren Ebenen und draufsicht; WiSim oder Strategie z.B. Eine Ebene enthält die Werte (Nummern der Frames) unserer Bodentiles. Die ist Flächendeckend und kann deswegen mit dem etwas schnelleren Befehl DrawBlock gemalt werden. Darauf malen wir die Figuren. Eine andere Ebene, diesmal ÜBER den Figuren, soll den KRam dadrunter verdecken. Man kann drunter herlaufen und durch Transparenz sieht man durch die Tiles durch. Z.b. schräge Häuserdächer, Baumkronen, Laternenpfähle und so weiter. Die Map muss daher um eine dritte Dimension erweitert werden, die die Ebenen angibt. Man entwickelt also "Tiefe". Für die Maße verwende ich mal die Konstanten von oben: Code: [AUSKLAPPEN] Dim map( MAP_HEIGHT, MAP_WIDTH, 1 ) ;0 und 1 = 2 Ebenen
Das ganze könnte man z.B so malen: Code: [AUSKLAPPEN] ;Boden, Layer Nr. 0
For y = 0 to MAP_HEIGHT For x = 0 to MAP_WIDTH DrawBlock Bild, x * TILESIZE, y * TILESIZE, map( y, x, 0 ) Next Next ;Spieler malen DrawImage Spieler, PlayerX * TILESIZE, PlayerY * TILESIZE ;Drüberzeichnen, Layer Nr. 1 For y = 0 to MAP_HEIGHT For x = 0 to MAP_WIDTH DrawBlock Bild, x * TILESIZE, y * TILESIZE, map( y, x, 1 ) Next Next Ich gehe davon aus das ihr es schon wisst, aber trotzdem: Das multiplizieren (*) mit TileSizebewirkt, dass eine Position, die in Tiles gemessen wird, für die Grafiken umgerechnet wird. Wir speichern in dem Map-Array 100*100 Tiles und nicht 100*100 Pixel! Darum müssen wir mit dem Multiplizieren den passenden Abstand zwischen den Tiles erst mal schaffen, um die Karte überhaupt darstellen zu können. Beim Spieler ist es genau das selbe. Der bewegt sich nicht flüssig sondern hüpft von Tile zu Tile. Das ist gut. Dann kann man nämlich einfach die Spielerposition zum "navigieren" auf der Karte verwenden ohne vorher zurückrechnen zu müssen und es entstehen so keine Probleme mit der Position AUF einem Tile und der Kollision bla bla bla. Ist eben einfacher zu erklären ![]() Okay, also haben wir jetzt was zum malen. Auf in die Kollision! Teil 3 - die Kollision Wir müssen das Array erweitern. Ein neuer Layer kommt drauf: Code: [AUSKLAPPEN] Dim map( MAP_HEIGHT, MAP_WIDTH, 2 ) ;0, 1, 2 = 3 Layer
Die neue Ebene enthält keine Tiledaten, sondern 0 und 1 - False und True. Das wird unsere Kollision. Wir markieren das gesamte dargestellte Tile als "blockiert", wenn wir den Wert der neuen Ebene auf 1 oder true setzen (kommt das gleiche bei raus) - und das ist auch gut so, denn die Kollision durch Abfrage der Framenummer (if map(y,x,2) < 4 then Kollision = false - so etwa, igitt!) ist nicht sehr schlau. Erstmal ändern sich die Tilesets ständig und zweitens... Ist es eben unschön ![]() Also haben wir in "Layer 2" lediglich die Daten für die Kollision. Das ist gewiss eine verflucht große Platzverschwendung wenn die Map erstmal wächst, aber es fällt eben nicht auf - nicht einmal bei 100*100 Tiles großen Karten. Und nur durch diesen Layer den RAM zu sprengen müsst ihr erst mal schaffen - es taugt also durchaus für ein vollwertiges Spiel! Teil 4 - Kollisionsabfrage Jetzt muss das ganze auch funktionieren. Also schnell ein paar neue Kosntanten am Anfang einfügen, für Tastendruck, damit man den Code wörtlich lesen kann und nicht so viele Zahlen hat: Code: [AUSKLAPPEN] Const KEY_UP = 200
Const KEY_DOWN = 208 Const KEY_LEFT = 203 Const KEY_RIGHT = 205 Spielerposition schonmal global machen, wenn wir schon am Anfang des Codes sind: Code: [AUSKLAPPEN] Global PlayerX, PlayerY
Okay, die Spielerbewegung ist dann ganz einfach. Wir bewegen den Spieler ja bloß von Tile zu Tile und müssen keine Pixelbrechberechnungen machen, drum einfach die Position um 1 erhöhen. Code: [AUSKLAPPEN] If KeyDown( KEY_UP )
PlayerY = PlayerY - 1 Endif If KeyDown( KEY_RIGHT ) PlayerX = PlayerX + 1 EndIf If KeyDown( KEY_DOWN ) PlayerY = PlayerY + 1 EndIf If KeyDown( KEY_LEFT ) PlayerX = PlayerX - 1 Endif Der Spieler läuft jetzt. Weil keine Begrenzung drin ist und das ganze in Tilesprüngen gemacht wird, flitzt er verdammt schnell auf der Karte umher. Aber mit einem beherzten Timer in der Hauptschleife oder eine schicken Animation später, dann geht das wohl. Jetzt haben wir die Bewegung. Nun kommt die Abfrage. Nur für die etwas Planlosen unter uns noch einmal aufgeführt: Über dem Spieler: y = PlayerY - 1 Unter dem Spieler: y = PlayerY + 1 Links vom Spieler: x = PlayerX - 1 Rechts vom Spieler: x = PlayerX + 1 Das sollte aber wirklich nicht nötig gewesen sein ![]() Nun fragen wir also mal schnell ab. Der abzufragende LAyer ist der dritte, hat also die Nummer "2" (man zählt ja von 0 an beim Programmieren und so). Also einfach nur den Wert (True oder 1, False oder 0) in eine temporäre Variable speichern. Diese Abfrage muss VOR die Tastenabfragen! Code: [AUSKLAPPEN] ;Werte (True/1 bzw. False/0) auslesen
KollisionOben = map( PlayerY - 1, PlayerX, 2 ) KollisionRechts = map( PlayerY, PlayerX + 1, 2 ) KollisionUnten = map( PlayerY + 1, PlayerX, 2 ) KollisionLinks = map( PlayerY, PlayerX - 1, 2 ) Damit haben wir die Tiles, die den Spieler direkt umgeben, abgefragt und deren Wert gespeichert. Jetzt kombinieren wir das ganze: Code: [AUSKLAPPEN] If KeyDown( KEY_UP )
If KollisionOben = False PlayerY = PlayerY - 1 Endif Endif If KeyDown( KEY_RIGHT ) If KollisionRechts = False PlayerX = PlayerX + 1 EndIf EndIf If KeyDown( KEY_DOWN ) If KollisionUnten = False PlayerY = PlayerY + 1 EndIf EndIf If KeyDown( KEY_LEFT ) If KollisionLinks = False PlayerX = PlayerX - 1 EndIf Endif Damit wäre die Kollision Abgefragt. Und somit eine Bewegung in die entsprechende Richtung geblockt! Ihr könnt nun gegen Wände laufen - ganz offiziell! ![]() Ich hoffe es half einigen von euch vielleicht noch zu einem Einstieg, einem Geistesblitz oder sonstwas. Kommentare gern gesehen. Extra für Fortgeschrittene - Kollision in einer Variable speichern Mir persönlich wären die vier Variablen für Kollision oben, unten etc. zu blöd ![]() Das sind: And, Or und Xor. Das Prinzip ist vereinfach folgendes: Code: [AUSKLAPPEN] ;Wert zuweisen
a = 16 ;Werte draufrechnen. 2 und 4 kommen auf unsere 16 b = a Or 2 Or 4 ;Werte entfernen wenn vorhanden. Wenn nicht werden sie draufgerechnet, wie bei Or c = a Xor 16 ;Werte abfragen. Die Zahl enthält noch immer "2" und "4", die 16 ist jetzt raus. Gibt also einen Wert ungleich 0 d = c And 4 Vielleicht anfänglich etwas schwer zu verstehen und es liegt nicht an mir euch zu erklären wie das Prinzip geht, mit den 0en und 1en, den Bits, aber ich kann euch die Schritte rasch aufzählen. Eine genaue ERklärung gibt es auf www.blitzbase.de Ihr nehmt 0 und 1 als Startwert und verdoppelt dann immer: 0, 1, 2, 4, 8, 16, 32, ... Warum? Ganz einfach: Addiert mal alle Werte bis VOR die 32, also 1+2+4+8+16. Das ergibt 31. Die nächste zahl ist 32. Addiert mal alle Werte bis VOR die 8, also 1+2+4. Das ergibt 7. Wir haben so Zahlen die man addieren kann und so die Abstände zwischen den einzelnen Stationen füllen. Wozu der ganze Mist? Ich wollte die Richtungen in EINER Variable speichern. Darum definiere ich 4 Konstanten am Anfang des Codes, die die merkmale der Bit-Schritte von oben aufweisen, also entsprechende Abstände haben: Code: [AUSKLAPPEN] Const OBEN = 1
Const RECHTS = 2 Const UNTEN = 4 Const LINKS = 8 Damit kann ich das auch besser veranschaulichen. Gelten alle 4 Kollisionen, ist der Spieler also umzingelt, rechnen wir nach dem obigen Beispielen: Code: [AUSKLAPPEN] Umzingelt = OBEN Or RECHTS Or UNTEN Or LINKS
Das Ergebnis gleicht einer Addition: Es ist 15. Ist der Spieler nun von Links und Rechts blockiert, wäre das LINKS Or RECHTS, ergibt 10. 10 ist eine Zahl, die von keiner Richtung verwendet wird (darum ja diese Bit-Schrittweite zwischen den Werten - damit sowas NICHT passiert). Man muss also nur prüfen, welche Zahlen in 10 rein passen ohne einen Restwert zu ergeben. Das sind eben nur RECHTS und LINKS. Alle anderen passen nicht genau. Das ist das Prinzip der ganzen Geschichte. Hier also die Kollisionsabfrage mit Bits ![]() Code: [AUSKLAPPEN] ;Werte (True/1 bzw. False/0) auslesen
KollisionOben = map( PlayerY - 1, PlayerX, 2 ) KollisionRechts = map( PlayerY, PlayerX + 1, 2 ) KollisionUnten = map( PlayerY + 1, PlayerX ) KollisionLinks = map( PlayerY, PlayerX - 1 ) Kollision = 0 ;Zurücksetzen If KollisionOben Then Kollision = Kollision Or OBEN If KollisionRechts then Kollision = Kollision Or RECHTS If KollisionUnten Then Kollision = Kollision Or UNTEN If KollisionLinks Then Kollision = Kollision Or LINKS Wir sparen keine einzige Zeile, ich weiß. Ist auch nur zum Veranschaulichen und ich dachte mir, dass es vielleicht ganz praktisch ist, wenn man das mal kurz anführt. Warum? Weil man so nicht nur die 4 Kollisonsrichtungen in einer Variable speichern könnte, sondern auch andere Informationen, wie z.B. ob dort ein Gegner steht (der einen dann in einen Kampf verwickelt etc). Das macht dann aber nur Sinn, wenn man diese Informationen auf diese Art und Weise auch in der Map speichert. Um zusätzliche Layer zu sparen - ich benutze das Prinzip im Spiel "RUNEndemo", was ich für den 2D Contest gemacht habe, um Effekte in einer einzigen Variable zu speichern und die dann in die Map zu schreiben. Jetzt prüfen wir aber erstmal, ob man damit kollidiert. Wir blockieren nicht mehr nach den tmeporären Variablen sondern mit einer Bit-Operation als Abfrage ("Abfragen" mit And, wie oben im Beispiel). Das muss dann aber in Klammern, weil BB sonst denkt dass wir zwei Bedingungen haben und nicht die Zahl "prüfen" wollen. Code: [AUSKLAPPEN] If KeyDown( KEY_UP )
If ( Kollision And OBEN ) = False PlayerY = PlayerY - 1 Endif Endif If KeyDown( KEY_RIGHT ) If ( Kollision And RECHTS ) = False PlayerX = PlayerX + 1 EndIf EndIf If KeyDown( KEY_DOWN ) If ( Kollision And UNTEN ) = False PlayerY = PlayerY + 1 EndIf EndIf If KeyDown( KEY_LEFT ) If ( Kollision And LINKS ) = False PlayerX = PlayerX - 1 EndIf Endif Das müsste prächtig funktionieren ![]() Wie gesagt, eigentlich ist die Verwendung hierfür nicht so prall. Die temporären Variablen reichen vollkommen aus und lassen sich auch besser lesen. anwenden würde ich das dann eher so: Code: [AUSKLAPPEN] Const KOLLISION = 1
Const EFFEKT = 2 Const EFFEKTBLA = 4 Kollision_Aktiv = map( y, x, 2 ) And KOLLISION Effekt_An = map( y, x, 2 ) And EFFEKT Effektbla_An = map( y, x, 2 ) And EFFEKTBLA Setzen kann man diese sogenannten "Flags" (hier: Eigenschaften für das Tile) mittels Xor ganz gut. Dann wird das nämlich immer an und ausgeschaltet, je nachdem. So, das war's jetzt aber wirklich ![]() Update 2008-09-20 Ich hatte ursprünglich Y und X in der Definition und bei Abfragen von Werten aus dem "map"-Array vertauscht, muss wohl betrunken gewesen sein damals … |
||
christian.tietze@gmail.com - https://christiantietze.de
macOS |
- Zuletzt bearbeitet von DivineDominion am Sa, Sep 20, 2008 22:10, insgesamt 2-mal bearbeitet
Blitzkrieg Bop |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Nicht komplett durchgelesen, aber sehr schön, das mal jemand sowas macht. Izewind's Tut's sind ja net so der Burner(vom Code her) ![]() *Edit: Wie wäre es dann gleich mit ner kleinen Geschichte zum Scrolling? Dann wäre das sehr gut. |
||
~ Hey Ho Let's Go ~ |
![]() |
DivineDominion |
![]() Antworten mit Zitat ![]() |
---|---|---|
Teil 5 - Pixelweise Bewegung des Spielers
Es gibt Tage, da möchte man einfach mal ein Spiel bauen, bei dem man NICHT von Kachel zu Kachel springt. Oben sah es so in etwa aus: Code: [AUSKLAPPEN] PlayerX = PlayerX + 1 ;... DrawImage Spieler, PlayerX * TILESIZE, PlayerY * TILESIZE Das ist aber nun mal sehr weit hinter'm Mond heutzutage. Also sollte man die Bewegungen etwas weicher machen, nicht hüpfen sondern gehen! Das Geheimnis ist eigentlich keins. Die meisten von euch tun es nämlich sowieso schon: Code: [AUSKLAPPEN] PlayerX = PlayerX + 2
;... DrawImage Spieler, PlayerX, PlayerY Die Position wird also nicht durch das "* TILESIZE" umgerechnet sondern bleibt Pixelgenau bestehen. Genau wie bei Mousebewegungen und sowas eben ![]() Die Kollision dann wieder zugreifen zu lassen ist dabei kein so schweres Stück Arbeit, wie manche denken. Es wird die Position einfach wieder in Tiles umgerechnet, damit man mit den Werten wieder in dem map-Array arbeiten kann. Und das geht sehr fix folgendermaßen: Code: [AUSKLAPPEN] PlayerTileX = PlayerX / TILESIZE
PlayerTileY = PlayerY / TILESIZE So. Schon fertig ![]() Was nun fehlt, ist die Kollision anders zu schreiben: Code: [AUSKLAPPEN] ;Werte (True/1 bzw. False/0) auslesen
KollisionOben = map( PlayerTileY - 1, PlayerTileX, 2 ) KollisionRechts = map( PlayerTileY, PlayerTileX + 1, 2 ) KollisionUnten = map( PlayerTileY + 1, PlayerTileX, 2 ) KollisionLinks = map( PlayerTileY, PlayerTileX - 1, 2 ) Was ist anders? Eigentlich nur die Variablen, die wir nutzen, um auf das Array zuzugreifen. Mit PlayerX, dem Pixelwert, kann man nicht viel anfangen. Hat man eine 10x10 Map wäre die Größe bei TILESIZE=16 immerhin 160x160 Pixel! Wenn man jetzt den Spieler bewegt und PlayerX=10 ist, dann ist er sichtbar kaum bewegt. Er ist nicht einmal vom ersten Tile runter! Aber der Wert ist, da es eben Pixel sind, hoch genug um beim nächsten Schritt das Array zu sprengen. Darum nutzt man nicht die Pixelposition in einem Tilearray. |
||
christian.tietze@gmail.com - https://christiantietze.de
macOS |
![]() |
Rob_ |
![]() Antworten mit Zitat ![]() |
---|---|---|
Hi divine!
Ich hab dein Tut mal durch gearbeitet weil ich mit den JumpTut auf der robsite nicht klargekommen bin, ![]() Ich habs ja extra gesucht! Und das ist dabei rausgekommen: BlitzBasic: [AUSKLAPPEN] Graphics 320,240,32,2 Aber ich kriege eine fehler meldung: "Array index out of bounds" und der gebugger zeigt mir hier drauf: BlitzBasic: [AUSKLAPPEN]
Wieso den? Ich hab mich schon gefreud das ich endlich ein gutes Kollisions Tut für jumpinruns gefunden habe! |
||
AMD Athlon 64 3500+ | Infineon 1 GB DDR RAM | nVidia Geforce 7800 GTX |
Dreamora |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Das ist normal
Player_x und Player_y sind 0 -> -1 <0 was ein nicht erlaubter Array Index ist. Musst also überprüfen ob +- 1 bei player_x und player_y erlaubte Werte sind und nur dann auslesen. |
||
Ihr findet die aktuellen Projekte unter Gayasoft und könnt mich unter @gayasoft auf Twitter erreichen. |
![]() |
otta |
![]() Antworten mit Zitat ![]() |
---|---|---|
hm...also irgendwie hab ich hier jetzt was nicht kapiert...!
Also die Frage die sich mir jetzt stellt ist folgende: wenn ich jetzt meine tilemap da fertig hab und will den zweiten Layer drüber legen und dann dritten für die Kollisoionen, muss ich für jeden Lyer neue map dataen anlegen oder liesßt der die Daten aus dem einen map data abschnitt? Wenn ja dann Frag ich mich halt wie ich die "true/false" Werte für die map festlegen.... danke für die aufmerksamkeit! |
||
![]() |
Jan_Ehemaliger Admin |
![]() Antworten mit Zitat ![]() |
---|---|---|
otta hat Folgendes geschrieben: wenn ich jetzt meine tilemap da fertig hab und will den zweiten Layer drüber legen und dann dritten für die Kollisoionen, muss ich für jeden Lyer neue map dataen anlegen oder liesßt der die Daten aus dem einen map data abschnitt? Wenn ja dann Frag ich mich halt wie ich die "true/false" Werte für die map festlegen....
Ok, deine Frage ist etwas schwer verständlich. Ich hoffe mal, ich kann dir Helfen, Du legst in dem Arrey Map, ein 3Dimensionales Feld an. Zitat: Dim map( MAP_WIDTH, MAP_HEIGHT, Layer_anzahl-1 ) Und dann greifst du darauf zu. BlitzBasic: [AUSKLAPPEN]
Man beachte, das 0 der 1. Layer ist 1 der 2. Layer und 2 der 3. Layer nun, rechnen wir einen Layer für die Kollisionen dazu, BlitzBasic: [AUSKLAPPEN]
= BlitzBasic: [AUSKLAPPEN]
so, nun wollen wir ja kollisionen eintragen: BlitzBasic: [AUSKLAPPEN]
so, True und false sind allso nur 1 und 0. --> wir können ne Menge dort in den Kollisionslayer eintragen z.B. Wasser, Wand ,.... BlitzBasic: [AUSKLAPPEN]
Das liest du aber am besten aus einer Map datei, oder zur not ausm Datafeld. Ich hoffe, ich konnte helfen. |
||
between angels and insects |
![]() |
otta |
![]() Antworten mit Zitat ![]() |
---|---|---|
ja gut danke für die Hilfe trotz meiner umständlichen Fragestellung. Aber eine Frage um noch mal sicherzugehen, die Kollisions daten werden dann quasi in einem neuen Dim Feld eingetragen das dann einfach über das oder die beiden anderen drüber gelegt wird?!? | ||
![]() |
Jan_Ehemaliger Admin |
![]() Antworten mit Zitat ![]() |
---|---|---|
Nein, ein Dim FEld, hat Belibig viele Dimensionen.
--> du machst 2D, für den X,Y des Tiles. und 3D, damit du noch mehrere Ebenen Speichern kannst. So geht das: Dim map(1D,2D,3D,4D,5D,6D) darum brauchen wir 3D: Dim map(breite,höhe,tiefe) und die anderen Tiles Werden dann in die Tiefe gespeichert, auch der Kollisionslayer z.B.Wir wollen ein Tile, mit Gras, Zaun, und darüber Blätter eines Baumes. und wegen dem Zaun, die Kollisionen auf true. Ich sage jetzt mal, der Kollisionslayer ist die nummer 5 in der Tiefe. map(x,y,0)=Gras_image map(x,y,1)=Zaun_image map(x,y,2)=Blätter_image map(x,y,5)=1; Für Kollisionen |
||
between angels and insects |
![]() |
otta |
![]() Antworten mit Zitat ![]() |
---|---|---|
aahhh, ok jetzt ist mir das Licht aufgegangen, danke ![]() |
||
AthlonXP 2800+, Radeon 9600XT,160GBHDD,512 MB DDR Ram (neuer Rechner neues Glück;))
Aktuelles Projekt: momentan Projektlos |
hc_tom |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
hi bei mir funktioniert das mit der kollision nicht...
1) die layers sind alle korrekt daran kann es nicht liegen 2) es kann nur noch an der if-abfrage liegen, die kollisionspunkte berechnet er korrekt 3) ich habe mir überlegt, ob es nicht schon zu spät dafür ist, die kollision erst nach dem tastendruck zu prüfen, aber nach einer änderung, hat es auch nix gebracht 4) vielleicht sieht ein außenstehender den fehler eher, ich find ihn nicht. danke ![]() BlitzBasic: [AUSKLAPPEN] Include \"inc_scancodes.bb\" |
||
![]() |
Living Dead |
![]() Antworten mit Zitat ![]() |
---|---|---|
Jetz hätt ich aber auch mal ne Frage:
Also im Tutorial wurde die Map ja mit dem X Wert in der ersten Dimension und dem Y Wert in der zweiten Deklariert. Nun steht da aber auch folgendes: BlitzBasic: [AUSKLAPPEN] ;Werte (True/1 bzw. False/0) auslesen Dabei wird in der ersten Dimension der Y-Wert des Spielers verrechnet und in der zweiten der X-Wert. Wieso geht das und was bringt das ![]() |
||
Das Leben ist der Weg.
Der Tod ist das Ziel. |
Tanatos |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Habe es mal mit der Pixel by Pixel Bewegung + kollision versucht allerdings funktioniert das ganze ein wenig sonderbar... aber schaut es euch selbst am besten mal an:
Code: [AUSKLAPPEN] Const XMAX=640 Const YMAX=480 Const ESC=1 Const AUF=200 Const AB=208 Const LINKS=203 Const RECHTS=205 Const TILESIZE=20 Global posx=2*20 Global posy=18*20 Dim map(19,19) Restore level For y=0 To 19 For x=0 To 19 Read map(x,y) Next Next Graphics XMAX,YMAX,0,2 SetBuffer BackBuffer() Repeat drawmap() moveplayer() Flip Cls Until KeyHit(ESC) End Function moveplayer() If KeyDown(AUF) And map(posx/20,posy/20-1)=0 Then posy = posy - 1 If KeyDown(AB) And map(posx/20,posy/20+1)=0 Then posy = posy + 1 If KeyDown(LINKS) And map(posx/20-1,posy/20)=0 Then posx = posx - 1 If KeyDown(RECHTS) And map(posx/20+1,posy/20)=0 Then posx = posx + 1 Color 125,0,0 : Oval posx,posy,TILESIZE,TILESIZE End Function Function drawmap() For y=0 To 19 For x=0 To 19 If map(x,y)=0 Then Color 0,0,0 : Rect x*TILESIZE,y*TILESIZE,TILESIZE,TILESIZE If map(x,y)=1 Then Color 255,255,255 : Rect x*TILESIZE,y*TILESIZE,TILESIZE,TILESIZE Next Next End Function .level Data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1 Data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 |
||
Düsi |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
So... auch wenn das nun schon etwas älter es, es ist ja noch immer ungeklärt. ![]() Damit die Collision so nicht klappen kann ist eigentlich klar. ![]() Die Position der Figur liegt oben links, wenn also diese ecke in ein freies Feld geht, die restliche Figur jedoch noch in einem CollisionsTile ist, denkt das Programm damit der Weg frei ist, da ja die Ecke mit der Position frei ist. Demnach musste der Code so lauten: Code: [AUSKLAPPEN] Const XMAX=640
Const YMAX=480 Const ESC=1 Const AUF=200 Const AB=208 Const LINKS=203 Const RECHTS=205 Const TILESIZE=20 Global posx=2*20 Global posy=18*20 Dim map(19,19) Restore level For y=0 To 19 For x=0 To 19 Read map(x,y) Next Next Graphics XMAX,YMAX,0,2 SetBuffer BackBuffer() Repeat drawmap() moveplayer() Flip Cls Until KeyHit(ESC) End Function moveplayer() Local TileX Local TileY Local TileX2 Local TileY2 If KeyDown(AUF) Then TileX = posx/TILESIZE TileY = posy/TILESIZE TileX2 = (posx+TILESIZE)/TILESIZE TileY2 = posy/TILESIZE ElseIf KeyDown(AB) Then TileX = posx/TILESIZE TileY = (posy+TILESIZE)/TILESIZE TileX2 = (posx+TILESIZE)/TILESIZE TileY2 = (posy+TILESIZE)/TILESIZE ElseIf KeyDown(LINKS) Then TileX = posx/TILESIZE TileY = posy/TILESIZE TileX2 = posx/TILESIZE TileY2 = (posy+TILESIZE)/TILESIZE ElseIf KeyDown(RECHTS) TileX = (posx+TILESIZE)/TILESIZE TileY = posy/TILESIZE TileX2 = (posx+TILESIZE)/TILESIZE TileY2 = (posy+TILESIZE)/TILESIZE EndIf If KeyDown(AUF) Then If map(TileX, TileY) = 0 Then If map(TileX2, TileY2) = 0 Then posy = posy - 1 EndIf EndIf ElseIf KeyDown(AB) If map(TileX, TileY) = 0 Then If map(TileX2, TileY2) = 0 Then posy = posy + 1 EndIf EndIf ElseIf KeyDown(LINKS) Then If map(TileX, TileY) = 0 Then If map(TileX2, TileY2) = 0 Then posx = posx - 1 EndIf EndIf ElseIf KeyDown(RECHTS) Then If map(TileX, TileY) = 0 Then If map(TileX2, TileY2) = 0 Then posx = posx + 1 EndIf EndIf EndIf Color 125,0,0 Oval posx,posy,TILESIZE,TILESIZE End Function Function drawmap() For y=0 To 19 For x=0 To 19 If map(x,y)=0 Then Color 0,0,0 : Rect x*TILESIZE,y*TILESIZE,TILESIZE,TILESIZE If map(x,y)=1 Then Color 255,255,255 : Rect x*TILESIZE,y*TILESIZE,TILESIZE,TILESIZE Next Next End Function .level Data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1 Data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 Prüfen, ich welche Richtung man möchte und dann jeweils die Position der zwei ecken diese in diese Richtung liegen nehmen und diese dann auf eine Collision prüfen. ![]() MfG |
||
![]() |
BigPingu |
![]() Antworten mit Zitat ![]() |
---|---|---|
@Düsi
Leider kann man weder nach in keine Richtung wenn man eine Wand berührt nur in die entgegengesetzte. Bsp: Man ist Links an einer wand, man kann nicht anch oben und nicht nach unten, nur nach rechts. |
||
Meine Projekte:
Blockhead : 80% VideoGameTycoon : 40% |
Phlox |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Ich hab eine Frage:
Wieso wird die ganze Map jedesmal neu gezeichnet? Man könnte ja nur die animierten Teile neu zeichnen und den Rest als Image speichern. |
||
![]() |
DivineDominion |
![]() Antworten mit Zitat ![]() |
---|---|---|
@BigPingu/Düsi/Tanatos:
Ich habe BlitzBasic leider nicht mehr ohne weiteres zur Hand und kann das leider nicht testen. Mich wundert dein Ansatz auch etwas, Düsi, denn mir fiel eher folgendes auf: Code: [AUSKLAPPEN] If KeyDown(AUF) And map(posx/20,posy/20-1)=0 Then posy = posy - 1
Einmal übersetzt in viele Variablen: Code: [AUSKLAPPEN] TileX = posx/20 TileY = posy/20 KollisionOben = False If map(TileX, TileY - 1) > 0 Then KollisionOben = True If KeyDown(AUF) And KollisionOben = False Then posy = posy - 1 Erst einmal wird die Position des Spielers in Tiles umgerechnet und dann wird im Tile darüber nachgeschaut, ob der Weg frei ist. Die Idee ist gut, nur: befindet sich der Spieler von Anfang an an "Y = 35", also an "TileY = 1", auf diesem Tile aber 15 Pixel unter dem oberen Rand, so kann er trotzdem nicht mehr hinauf gehen. Ich würde stattdessen beim Test bereits einen Pixel abziehen und diese zu testende Position in Tiles umrechnen (und so am Grenzpixel durch "(TestY=posy-1)/20" auf das Tile darüber kommen). Ob das jetzt wirklich das Problem ist bzw. zur Lösung beiträgt – ich hoffe es einfach mal ![]() @Phlox Deine Frage ist nicht unberechtigt. Diese Vorgehensweise ist bei zunehmender Mapgröße nicht sehr effizient. Wenn noch mehr Layer dazukommen, dann prost Mahlzeit. Fällt heute rechentechnisch zwar kaum auf, aber man verschenkt da am Ende wahrscheinlich nötige Ressourcen. Stattdessen würde ich den Zeichenbereich auf den Bildschirm eingrenzen (ich nenne das mal "Viewport" oder auch Schaufenster, meine damit aber nicht den meiner Meinung nach völlig unnützen Befehl von BB!). Du kannst die Maße der Auflösung dafür ja hernehmen (ScreenWidth() und ScreenHeight(), wenn ich mich nicht irre) und durch TILE_SIZE teilen. Schon weißt du, wie viele Tiles auf den Bildschirm passen und kannst die Schleife entsprechend eingrenzen. Eine Map, die größer ist als der Bildschirm, ist aber sowieso bloß sinnvoll, wenn du Scrollen kannst, sich also der Spieler bewegt und die "Kamera" (der Mittelpunkt des Viewports) ihn verfolgt. Dann musst du für die For-Schleifen beim zeichnen auch ScrollX/TILE_SIZE und ScrollY/TILE_SIZE als Startpunkt nehmen und die Bildschirmmaße aufaddieren, um den sichtbaren Bereich richtig einzugrenzen. So. Dieses Ergebnis könntest du jetzt in einem großen Bild speichern. Das schon vorher ohne Grenzen zu tun, wäre keine gute Idee: würdest du die Map komplett in ein Bild malen, verschenkst du Ressourcen, und ein Riesenbild zu zeichnen dauert immer länger als nur so viel, wie auf den Monitor passt. Auch, wenn es viele viele kleine Tiles und Objekte sind. Nur den aktuellen Bildschirmausschnitt zu speichern (was ja, wie wir uns geeinigt haben ( ![]() Wo so eine Technik Anwendung finden könnte, das ist beim Hauptmenü, bei Einkauf-Menüs bei Rollenspielen o.ä. |
||
christian.tietze@gmail.com - https://christiantietze.de
macOS |
![]() |
Dottakopf |
![]() Antworten mit Zitat ![]() |
---|---|---|
ist zwar schon älter, ...allerdings wurde die pixel by pixel bewegung hier letztendlich nicht "wirklich" bugfrei gezeigt/niedergeschrieben.
Daher möchte ich diesen Code posten: Code: [AUSKLAPPEN] ;infos ;Verantwortlich für den kas= Dottakopf ;tile size = 32 ;map size 12x12 ;Pixel to Pixel bewegung Graphics 800,600,16,2 SetBuffer BackBuffer() timer = CreateTimer(60) ;globals Global hero_px = 64 Global hero_py = 64 Global hero_speed = 2 ;arry Dim tile_map(20,20) For y = 0 To 11 For x = 0 To 11 Read tile_map(x,y) Next Next ;//////////////////////////////////////////////////// MAIN LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Repeat Cls draw_map() update_hero() WaitTimer(timer) Flip Until KeyHit(1) ;//////////////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Function update_hero() ;ermittelt welche umliegenden tiles der spieler besetzt ty1 = Floor(hero_py/Float(32)) ;tile abgerundet ty2 = Ceil(hero_py/Float(32)) ;tile aufgerundet tx1 = Floor(hero_px/Float(32)) ;tile abgerundet tx2 = Ceil(hero_px/Float(32)) ;tile aufgerundet ;OBEN If KeyDown(200) Then If tile_map( tx1 , (hero_py-hero_speed) /32) = 0 And tile_map( tx2 , (hero_py-hero_speed) /32) = 0 Then hero_py = hero_py - hero_speed Else hero_py = hero_py + hero_speed hero_py = (hero_py/32)*32 End If End If ;UNTEN If KeyDown(208) Then If tile_map( tx1 , (hero_py+hero_speed)/32+1) = 0 And tile_map( tx2 , (hero_py+hero_speed)/32+1) = 0 Then hero_py = hero_py +hero_speed Else hero_py = hero_py - hero_speed hero_py = (hero_py/32+1)*32 End If End If ;links If KeyDown(203) Then If tile_map( (hero_px -hero_speed)/32 , ty1) = 0 And tile_map( (hero_px-hero_speed)/32 , ty2 ) = 0 Then hero_px = hero_px -hero_speed Else hero_px = hero_px+hero_speed hero_px = (hero_px/32)*32 End If End If ;rechts If KeyDown(205) Then If tile_map( (hero_px+hero_speed )/32+1 , ty1) = 0 And tile_map( (hero_px+hero_speed)/32+1 , ty2 ) = 0 hero_px = hero_px +hero_speed Else hero_px = hero_px - hero_speed hero_px = (hero_px/32+1)*32 End If End If ;hero malen Color 0,0,255:Rect hero_px,hero_py,32,32,1 End Function ;---------------------------------------------------------------------------------------------------------------------------- Function draw_map() Color 200,200,200 For x = 0 To 11 For y = 0 To 11 If tile_map(x,y)<>0 Then Rect x*32,y*32,32,32,tile_map(x,y) Next Next End Function ;---------------------------------------------------------------------------------------------------------------------------- .level Data 1,1,1,1,1,1,1,1,1,1,1,1 Data 1,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,1,0,0,1,0,0,0,1 Data 1,0,0,0,0,1,1,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,0,0,1 Data 1,0,0,0,0,0,0,0,0,1,0,1 Data 1,0,0,1,1,1,0,0,0,1,1,1 Data 1,0,0,0,0,1,0,0,0,1,0,1 Data 1,0,0,0,0,1,0,0,0,0,0,1 Data 1,1,1,1,1,1,1,1,1,1,1,1 Die Geschwindigkeit ist varriabel , allerdings geht das hin und wieder auf die kosten der Genauigkeit. Bugs : hero_speed = 3 mag er an engen druchgängen irgendwie nicht so richtig. Schafft man es das der Spieler und das hinderniss sich genau an den Kanten berühren, springt der Spieler manchmal etwas seltsam. Gruß Dottakopf |
||
Rechtschreibfehler gelten der allgemeinen Belustigung! |
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group