Bomberman Klon (Community Tutorial) BMAX

Kommentare anzeigen Worklog abonnieren
Gehe zu Seite 1, 2  Weiter

Worklogs Bomberman Klon (Community Tutorial) BMAX

Back to the... oh man...

Mittwoch, 27. Oktober 2010 von KirkZero
sooo...
Hab mich nun dran versucht mich wieder in meinen Code einzulesen...
Ist garnicht so einfach... will aber das Projekt nicht aufgeben.
(auch wenn es nur ein kleines Licht ist unter den ganzen großen Sachen hier)

Denke, ich brauch da noch ein wenig Zeit...
werde mein eigenes Worklog mal von unten bis oben durchgehen, denke, wenn ich dann wieder klar komme... dann hab ich mein ziel erreicht... ein kleines Tutorial *freu*

ToDo:

- Das Menü
- Multiplayer an einem Rechner
- Singleplayer

Das Menü wird einfach ausfallen...
- primitives Sound an/aus
- Vollbild an/aus
- singleplayer
- multiplayer

der multiplayer Part wird ein einfaches Deathmatch werden...

der Singleplayer eine Art "verteidige den Hügel" oder so... (was man dann auch zu zweit im coop spielen kann)

Das erstmal soweit von mir...

Gehe dann mal wieder "Code Lesen" Wink

Animation des Spielers

Mittwoch, 8. September 2010 von KirkZero
user posted image

Das ist die SpielerGrafik, sie besteht aus 20 einzelbildern zu je 32x32 Pixeln. Das allererste Bild (oben links) ist Frame 0 und das letzte (unten rechts) ist Frame 19. Daraus folgt:
das 1. Bild für Bewegung nach oben ist Frame 0 (linkes Bild 1. Reihe)
das 1. Bild für Bewegung nach rechts ist Frame 5 (linkes Bild 2. Reihe)
das 1. Bild für Bewegung nach unten ist Frame 10 (linkes Bild 3. Reihe)
das 1. Bild für Bewegung nach links ist Frame 15 (linkes Bild 4. Reihe)

Dafür nehm ich wieder einmal ein paar Konstante:

BlitzMax: [AUSKLAPPEN]
	Const ANIOBEN			:Int = 0		'StartFrame für Bewegung nach oben
Const ANIUNTEN :Int = 10 'StartFrame für Bewegung nach unten
Const ANILINKS :Int = 15 'StartFrame für Bewegung nach links
Const ANIRECHTS :Int = 5 'StartFrame für Bewegung nach rechts


user posted image

Die linken 3 Bilder jeder Reihe sind für die Bewegungsanimation zuständig, die rechten 2 Bilder werden erstmal nicht gebraucht, die kommen vielleicht noch später und wären dann für einen "Special Move" Wink

Somit müsste man also immer, wenn der Robo nach oben fährt, nacheinander frame 0, dann frame 1, und zum schluss Frame 2 anzeigen lassen.

BlitzMax: [AUSKLAPPEN]
	Const BEWEGUNGSFRAMES	:Int = 2	'Bewegung besteht aus 3 Frames (0, 1 und 2)

Field SpielerFrame :Int = 0 'Aktuelles Frame des Spielers
Field BewegungsFrame :Int = 0 'Aktuelles Frame der Bewegung


Die Konstante BEWEGUNGSFRAMES gibt an, wieviele Frames jede Richtung hat. Auch wenn dort die Zahl 2 steht, hat sie dennoch 3, da ja bei 0 angefangen wird zu zählen.

SpielerFrame ist das aktuelle Frame, welches dann auch gezeichnet wird. Es setzt sich aus einer der 4 Konstanten (ANIOBEN, ANIUNTEN,ANILINKS und ANIRECHTS) + dem BewegungsFrame zusammen.
(BewegungsFrame speichert dabei nur den aktuellen Frame der Bewegung, welcher entweder 0,1 oder 2 ist. Ist er > 2 (also > BEWEGUNGSFRAMES) dann wird er wieder auf 0 gesetzt)

BlitzMax: [AUSKLAPPEN]
BewegungsFrame:+1
If BewegungsFrame > BEWEGUNGSFRAMES Then BewegungsFrame=0


Das ganze packe ich nun in die Methode Bewegung() von TSpieler.bmx:

BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int 'Temporäre Speicher
Local y :Int
Local fx :Int 'Temporäre Speicher für zu prüfende Felder
Local fy :Int 'ist fy=-1 wird oberes vom aktuellen Feld geprüft

Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

BewegungsFrame:+1
If BewegungsFrame > BEWEGUNGSFRAMES Then BewegungsFrame=0

Select Richtung
Case OBEN
fy=-1 'oberes Feld prüfen
SpielerFrame=ANIOBEN+BewegungsFrame
Case UNTEN
fy=+1 'unteres Feld prüfen
SpielerFrame=ANIUNTEN+BewegungsFrame
Case LINKS
fx=-1 'linkes Feld prüfen
SpielerFrame=ANILINKS+BewegungsFrame
Case RECHTS
fx=+1 'rechtes Feld prüfen
SpielerFrame=ANIRECHTS+BewegungsFrame
End Select


und jetzt muss ich SpielerFrame noch in der Zeichnen() Methode unterbringen:

BlitzMax: [AUSKLAPPEN]
	Method Zeichnen()
'DrawRect (xFeld*TS,yFeld*TS,TS,TS) 'Erstmal auskommentiert... vorerst klappt ja alles Wink
DrawImage (SpielerIMG,xPos,yPos,SpielerFrame)
End Method


hund hier die Komplette TSpieler.bmx:

BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field SpielerNr :Int 'Welcher Spieler bin ich? 1,2,3 oder 4
Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2 'man bewegt sich immer um 2 Pixel pro Bewegung

Field FeuerKraft :Int = 2 'Reichweite der Explosionen einer Bombe (2 Felder in alle Richtungen)
Field BombenMenge :Int = 1 'Anzahl an Bomben, die der Spieler gleichzeitig legen kann
Field MaxBombenMenge :Int = 5 'egal wieviele Items man sammelt, man kann nie mehr legen, als hier verzeichnet
Field GelegteBomben :Int 'Wieviel hat der Spieler bereits gelegt und sind noch nicht explodiert?

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Const ANIOBEN :Int = 0 'StartFrame für Bewegung nach oben
Const ANIUNTEN :Int = 10 'StartFrame für Bewegung nach unten
Const ANILINKS :Int = 15 'StartFrame für Bewegung nach links
Const ANIRECHTS :Int = 5 'StartFrame für Bewegung nach rechts

Const BEWEGUNGSFRAMES :Int = 2 'Bewegung besteht aus 3 Frames (0, 1 und 2)

Field SpielerFrame :Int = 0 'Aktuelles Frame des Spielers
Field BewegungsFrame :Int = 0 'Aktuelles Frame der Bewegung

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 20

Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Bewegung wieder erlaubt (Zeit)
If KeyDown(Taste_Hoch[SpielerNr]) 'wenn Taste für OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(Taste_Runter[SpielerNr]) 'wenn Taste für UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(Taste_Links[SpielerNr]) 'wenn Taste für LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(Taste_Rechts[SpielerNr]) 'wenn Taste für RECHTS gedrückt wird
Bewegung(RECHTS)
End If
End If
If KeyHit(Taste_Aktion[SpielerNr]) 'wenn Taste für Aktion gedrückt wird
BombeLegen() 'dann Methode zum Bombenlegen aufrufen
End If
End Method

Method BombeLegen()
If Map.Feld[xFeld,yFeld].Inhalt = NIX 'wenn aktuelles Feld leer ist
If GelegteBomben < BombenMenge And GelegteBomben < MaxBombenMenge
'wenn bisher weniger Bomben gelegt als man kann UND als man darf
TBombe.Erstellen(SpielerNr,xFeld,yFeld,FeuerKraft) 'Bombe erstellen
GelegteBomben:+1 'um 1 erhöhen, da ja nun eine bombe gelegt wurde
End If
End If
End Method

Method Bewegung(Richtung:Int)
Local x :Int 'Temporäre Speicher
Local y :Int
Local fx :Int 'Temporäre Speicher für zu prüfende Felder
Local fy :Int 'ist fy=-1 wird oberes vom aktuellen Feld geprüft

Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

BewegungsFrame:+1
If BewegungsFrame > BEWEGUNGSFRAMES Then BewegungsFrame=0

Select Richtung
Case OBEN
fy=-1 'oberes Feld prüfen
SpielerFrame=ANIOBEN+BewegungsFrame
Case UNTEN
fy=+1 'unteres Feld prüfen
SpielerFrame=ANIUNTEN+BewegungsFrame
Case LINKS
fx=-1 'linkes Feld prüfen
SpielerFrame=ANILINKS+BewegungsFrame
Case RECHTS
fx=+1 'rechtes Feld prüfen
SpielerFrame=ANIRECHTS+BewegungsFrame
End Select

If Map.Feld[xFeld+fx,yFeld+fy].Inhalt = NIX 'wenn auf dem zu prüfenden Feld NIX ist

If fy <> 0 'ist das zu prüfende Feld oben oder unten, dann auf Autobewegung checken
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else
y=fy*Speed 'passt es genau, dann um Speed Pixel nach fy bewegen
End If
End If

If fx <> 0 'ist das zu prüfende Feld links oder rechts, dann auf Autobewegung checken
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=fx*Speed 'passt es genau, dann um Speed Pixel nach fx bewegen
End If
End If

Else If fy <> 0 And yMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
y=fy*Speed
Else If fx <> 0 And xMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
x=fx*Speed
End If

yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x

MapFeld() 'Aktuelles Map.Feld[,] wird berechnet

BewegungsTimer = MilliSecs()

End Method

Method MapFeld() 'berechnet aktuelles Map.Feld[,]
Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

If yMod < (TS/2) Then yFeld=(yPos/TS) 'wenn modulus < als 16
If yMod > (TS/2) Then yFeld=(yPos/TS)+1 'wenn modulus > als 16
If xMod < (TS/2) Then xFeld=(xPos/TS) 'wenn modulus < als 16
If xMod > (TS/2) Then xFeld=(xPos/TS)+1 'wenn modulus > als 16

End Method

Method Update()
If Map.Feld[xFeld,yFeld].Item = ITEM_AUFGEDECKT 'wenn aufgedecktes Item auf SpielerFeld
For Local Item:TItem = EachIn TItem.ItemListe 'jedes erstellte Item durchgehen
If xFeld=Item.xFeld And yFeld=Item.yFeld 'wenn richtiges Item gefunden
Map.Feld[xFeld,yFeld].Item = 0 'ItemFlag auf 0
Item.Sammler = SpielerNr 'Spieler der es gesammelt hat festlegen
End If
Next
End If
End Method

Method Zeichnen()
'DrawRect (xFeld*TS,yFeld*TS,TS,TS) 'Erstmal auskommentiert... vorerst klappt ja alles Wink
DrawImage (SpielerIMG,xPos,yPos,SpielerFrame)
End Method

Function Erstellen:TSpieler (SpielerNr:Int,xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.SpielerNr = SpielerNr 'welcher Spieler bin ich?
NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type


und schon bewegen sich die Ketten des Robos und er schaut immer in die richtige Richtung Wink

Hier nochmal ein Zitat von Xeres:

Zitat:
Was mir aufgefallen ist: Dein Spieler Bild hat 4x die Frames die es bräuchte; Für die Top-down Ansicht würde ein Frame-Strip reichen und der Rest ließe sich durch SetRotation erreichen. Um die, sagen wir "Rotationseffekte" (Drehung um die obere, linke Ecke), zu vermeiden, verwende ich eigentlich immer AutoMidHandle - also gebe immer die Mitte der zu zeichnenden Objekte an, um die auch die Drehung stattfindet. Das ist vermutlich reine Gewöhnungssache, hat aber seine Vorteile.


Ich habe das jetzt zwar anders gemacht, aber in meinen zukünftigen Projekten werde ich es mal auf die Art (SetRotation) versuchen.


Vielen Dank fürs Lesen!

Tastenbelegung und Spieler 2

Mittwoch, 8. September 2010 von KirkZero
Erstmal liegt wieder eine Fehlerkorrektur an. Danke an Hummelpups!!!
Ich habe mit der Art und Weise, mit der ich im letzten Eintrag die Items auf der Map versteckt habe, ziemlichen Mist gebaut.
Sie werden nicht wirklich schön gleichmässig verteilt.

Zitat von Hummelpups:
Zitat:
KirkZero, bei deinem "Item setz algo" hast du es dir unnötig schwer
und auch noch falsch gemacht. Deine Items laut wahrscheinlichkeitsrechnung
so gesetzt:

links dein Algo, rechts meiner
user posted image


Nun hab ich mir Hummelpups Art mal angeschaut. Die Wahrscheinlichkeit ist nicht nur schön gleichmässig, sondern die Lösung ist auch um einiges eleganter.
Somit entsteht eine Neue Methode in TKarte.bmx:

BlitzMax: [AUSKLAPPEN]
	Method ItemsVerteilen:TKarte (Item:Int,Menge:Int,Map:TKarte)
For Local Nr:Int = 1 To Menge
Local x:Int
Local y:Int
Repeat
x=Rand (1,MX)
y=Rand (1,MY)
If Map.Feld[x,y].Inhalt = SBLOCK And Map.Feld[x,y].Item = 0
'wenn auf aktuellem Feld ein Softblock ist, UND noch KEIN Item
Map.Feld[x,y].Item = Item 'Item verstecken
Exit 'Repeat... Forever... Schleife verlassen
End If
Forever
Next
Return Map
End Method


An die Methode werden (von links nach rechts) übergeben:
- das Item, welches versteckt werden soll
- die Menge, also wie viele sollen von der Sorte versteckt werden
- die Map, auf der die Items versteckt werden sollen

und zurück gibt sie die mit Items gefüllte Map Wink

ich rufe sie in der Erstellen() Funktion dann so auf:

BlitzMax: [AUSKLAPPEN]
		NeueMap.ItemsVerteilen (ITEM_BOMBE,AnzahlBomben,NeueMap)
NeueMap.ItemsVerteilen (ITEM_FEUER,AnzahlFeuer,NeueMap)


und hier die komplette TKarte.bmx:

BlitzMax: [AUSKLAPPEN]
Const NIX		:Int = 0		'Für den Inhalt der Felder
Const SBLOCK :Int = 1 'einfacher zu merken als pure Zahlen
Const BOMBE :Int = 5
Const HBLOCK :Int = 10

Type TKarte

Field Feld :TFeld[,] 'Dynamisches Array für die Map, damit keine Größenbeschränkungen entstehen
Field MX :Int 'Breite der Map
Field MY :Int 'Höhe der Map


Method Zeichnen()
For Local Y:Int=1 To MX
For Local X:Int=1 To MY
Feld[X,Y].Update(X,Y) 'Aktuelles Feld auf Richtigkeit prüfen
Feld[X,Y].Zeichnen(X,Y) 'Übergibt X und Y Werte an Feld.Zeichnen
Next
Next
End Method

Function Erstellen:TKarte(MX:Int,MY:Int,Boden:Int,HardBl:Int,SoftBl:Int)
Local NeueMap:TKarte = New TKarte 'hier wird die neue Map erstmal Temporär gespeichert

NeueMap.Feld=New TFeld[MX+1,MY+1] 'Map Dimensionieren
NeueMap.MX=MX 'Breite der Map speichern
NeueMap.MY=MY 'Höhe der Map speichern
'#############################################
'# STANDARTMAP ERSTELLEN #
'#############################################
For Local Y:Int=1 To MY 'jedes Feld von 1 bis zur Höhe der Map
For Local X:Int=1 To MX 'jedes Feld von 1 bis zur Breite der Map
NeueMap.Feld[X,Y] = TFeld.Erstellen(Boden) 'die einzelnen Felder werden erstellt
'siehe TFeld.Erstellen Funktion
If X=1 Or Y=1 Or X=MX Or Y=MY Then
NeueMap.Feld[X,Y].BlockTile =HardBl
NeueMap.Feld[X,Y].Inhalt =HBLOCK
End If
'haben X oder Y eine Wert, welcher den Rand der Map darstellt, so wird das
'BlockTile auf einen HardBlock gesetzt. Auf diese Weise zeichne ich den Rand
'der Map

If X=3 Or X=5 Or X=7 Or X=9 Or X=11 Or X=13 Then
If Y=3 Or Y=5 Or Y=7 Or Y=9 Or Y=11 Or Y=13 Then
NeueMap.Feld[X,Y].BlockTile =HardBl
NeueMap.Feld[X,Y].Inhalt =HBLOCK
End If
End If
'haben der X und der Y Wert eine Position erreicht, die wichtig für das
'Schachbrettähnliche Muster sind, so wird ebenfalls ein HardBlock an die
'Position gesetzt
Next
Next
'#############################################
'# MAP MIT 100 SOFTBLÖCKEN FÜLLEN #
'# DABEI SPIELER-STARTECKEN FREI LASSEN #
'#############################################
Local BlockZahl:Int = 100 '100 Softblöcke sollen gesetzt werden
Repeat 'eine Schleife, die solange läuft, bis 100 SoftBlöcke gesetzt worden sind
For Local Y:Int=1 To MY 'ich gehe wieder die ganze Map durch
For Local X:Int=1 To MX
Local Check:Int=0 'setze Check auf 0
If Y=2 Or Y=3 Or Y=MY-2 Or Y=MY-1 Then Check:+1 'für die StartEcken
If X=2 Or X=3 Or X=MX-2 Or X=MX-1 Then Check:+1 'wie zuvor beschrieben
If BlockZahl=0 Or (Y=8 And X=8) Then Check=2
'habe ich schon 100 Blöcke gesetzt ODER ist das aktuelle Feld grade die Mitte
'Dann Check auf 2 setzen, damit kein Block gesetzt wird.
If Check<2 Then
If NeueMap.Feld[X,Y].BlockTile=0 And Rand(0,2)=1 Then
'wenn auf aktuellem Feld noch kein Block ist und Rand(0,2)=1 dann neuen Block setzen
NeueMap.Feld[X,Y].BlockTile =SoftBl
NeueMap.Feld[X,Y].Inhalt =SBLOCK
BlockZahl:-1 'gesetzten Block abziehen (denn ich will ja nur 100 haben)
End If
End If
Next
Next
Until (BlockZahl=0) 'wenn 100 Softblöcke gesetzt wurden Schleife verlassen und Map zurück geben.
'#############################################
'# MAP MIT ITEMS FÜLLEN #
'# ITEMS DÜRFEN NUR AUF FELDER MIT SOFTBLOCK #
'#############################################
Local AnzahlBomben :Int = 10 'wieviel von welchem Item sollen vorkommen?
Local AnzahlFeuer :Int = 10

NeueMap.ItemsVerteilen (ITEM_BOMBE,AnzahlBomben,NeueMap)
NeueMap.ItemsVerteilen (ITEM_FEUER,AnzahlFeuer,NeueMap)

Return NeueMap 'hiermit gebe ich die temporär erstellte Map zurück
End Function

Method ItemsVerteilen:TKarte (Item:Int,Menge:Int,Map:TKarte)
For Local Nr:Int = 1 To Menge
Local x:Int
Local y:Int
Repeat
x=Rand (1,MX)
y=Rand (1,MY)
If Map.Feld[x,y].Inhalt = SBLOCK And Map.Feld[x,y].Item = 0
'wenn auf aktuellem Feld ein Softblock ist, UND noch KEIN Item
Map.Feld[x,y].Item = Item 'Item verstecken
Exit
End If
Forever
Next
Return Map
End Method


End Type


Nochmals danke an Hummelpups!!!


Bevor ich nun allerdings einen 2. Spieler erstelle... erstmal eine neue Datei für die Variablen, welche die Tastenbelegung der einzelnen Spieler speichern sollen:

BlitzMax: [AUSKLAPPEN]
'Tastaturbelegung

Global Taste_Hoch :Int[5]
Global Taste_Runter :Int[5]
Global Taste_Links :Int[5]
Global Taste_Rechts :Int[5]
Global Taste_Aktion :Int[5]


'Tasten Spieler 1 (Spieler[1])

Taste_Hoch [1] = KEY_W
Taste_Runter [1] = KEY_S
Taste_Links [1] = KEY_A
Taste_Rechts [1] = KEY_D
Taste_Aktion [1] = KEY_SPACE



'Tasten Spieler 2 (Spieler[2])

Taste_Hoch [2] = KEY_UP
Taste_Runter [2] = KEY_DOWN
Taste_Links [2] = KEY_LEFT
Taste_Rechts [2] = KEY_RIGHT
Taste_Aktion [2] = KEY_RCONTROL


Speichern im Ordner Includes unter dem Namen TastaturBelegung.bmx und in der MainGame.bmx includieren:

BlitzMax: [AUSKLAPPEN]
'------------------------------ includes			----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"
Include "INCLUDES/TBombe.bmx"
Include "INCLUDES/TExplosion.bmx"
Include "INCLUDES/TItem.bmx"

Include "INCLUDES/TastaturBelegung.bmx"


Ich werde das mit den Tasten später noch versuchen anders zu regeln, vieleicht über eine ini datei oder übers Menü. Aber erstmal mach ich es so.

Nun muss ich noch die Steuerung() Methode von TSpieler anpassen:

BlitzMax: [AUSKLAPPEN]
'Statt so:
If KeyDown(KEY_UP) 'wenn Taste für OBEN gedrückt

'heisst es nun so:
If KeyDown(Taste_Hoch[SpielerNr]) 'wenn Taste für OBEN gedrückt


BlitzMax: [AUSKLAPPEN]
	Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Bewegung wieder erlaubt (Zeit)
If KeyDown(Taste_Hoch[SpielerNr]) 'wenn Taste für OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(Taste_Runter[SpielerNr]) 'wenn Taste für UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(Taste_Links[SpielerNr]) 'wenn Taste für LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(Taste_Rechts[SpielerNr]) 'wenn Taste für RECHTS gedrückt wird
Bewegung(RECHTS)
End If
End If
If KeyHit(Taste_Aktion[SpielerNr]) 'wenn Taste für Aktion gedrückt wird
BombeLegen() 'dann Methode zum Bombenlegen aufrufen
End If
End Method


und schon läuft Spieler 1 mit den Tasten W A S D und Leertaste zum Bomben legen

Jetzt kann ich Spieler 2 erstellen.
MainGame.bmx:

BlitzMax: [AUSKLAPPEN]
Spieler[1] = TSpieler.Erstellen(1,2,2,"GFX/robo1.png",20) 'Spieler 1 wird erstellt
Spieler[2] = TSpieler.Erstellen(2,14,14,"GFX/robo1.png",20) 'Spieler 1 wird erstellt


Spieler 2 erhält erstmal die gleiche Grafik wie Spieler 1.
SpielerNr ist 2 und er startet unten rechts (x=14 und y=14)

Da Spieler 2 allerdings noch nicht in der Hauptschleife integriert ist, passiert auch nix weiter. Also ab in die Hauptschleife mit ihm:

BlitzMax: [AUSKLAPPEN]
	For Local I:Int = 1 To 2
Spieler[I].Steuerung()
Spieler[I].Update()
Spieler[I].Zeichnen()
Next



und hier die ganze MainGame.bmx:

BlitzMax: [AUSKLAPPEN]
'MainGame.bmx

SuperStrict 'zwingt mich, sauber zu programmieren Wink
Framework brl.max2d
Import brl.Timer
Import brl.Random
Import brl.D3D9Max2D
Import brl.PNGLoader

AppTitle = "Bomberman Klon (Community Tutorial)"

'------------------------------ includes ----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"
Include "INCLUDES/TBombe.bmx"
Include "INCLUDES/TExplosion.bmx"
Include "INCLUDES/TItem.bmx"

Include "INCLUDES/TastaturBelegung.bmx"

'------------------------------ Grafikmodus ----------
Const BREITE :Int = 800
Const HOEHE :Int = 600 'Ich setze die Bildschirmbreite und Höhe als Konstante
Global Vollbild :Int = False 'Ob Vollbild oder nicht, soll später der User entscheiden, darum keine Konstante

SetGraphicsDriver D3D9Max2DDriver()

Graphics BREITE, HOEHE, Vollbild 'Bildschirmmodus wird gesetzt
'AutoMidHandle(1)
'------------------------------ Vorbereitungen ----------
Const TS :Int = 32 'Tilegröße (32*32)

Global Frames :TTimer = CreateTimer (120) 'damit das Spiel auf jedem Rechner gleichschnell läuft setze ich einen Timer

SeedRnd (MilliSecs()) 'Da ich den "Zufall" im Spiel benötige,füttere ich ihn schonmal mit der Zeit seit dem Systemstart
'------------------------------ Grafiken laden ----------



'------------------------------ Sounds laden ----------



'------------------------------ Type-Erstellungen ----------
Global Map:TKarte = TKarte.Erstellen(15,15,0,1,2)
Global Spieler:TSpieler [5] 'es soll maximal 4 Spieler geben, [5] weil es bei 0 anfängt

Spieler[1] = TSpieler.Erstellen(1,2,2,"GFX/robo1.png",20) 'Spieler 1 wird erstellt
Spieler[2] = TSpieler.Erstellen(2,14,14,"GFX/robo1.png",20) 'Spieler 2 wird erstellt

'------------------------------ Hauptschleife ----------
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt

For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

For Local Explosion:TExplosion = EachIn TExplosion.ExplosionsListe 'jede erstellte Explosion durchgehen
Explosion.Update()
Explosion.Zeichnen()
Next

For Local Item:TItem = EachIn TItem.ItemListe 'jedes erstellte Item durchgehen
Item.Update()
Item.Zeichnen()
Next

For Local I:Int = 1 To 2
Spieler[I].Steuerung()
Spieler[I].Update()
Spieler[I].Zeichnen()
Next

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts gedrückt wird
End


Sauber... ab jetzt ist man zu zweit unterwegs Wink

Glaub, jetzt komm ich beim nächsten mal nicht drum rum, die Spieler endlich mal zu animieren...



Vielen dank fürs Lesen!!! *winke*

Items... ich will sie endlich einsammeln

Dienstag, 7. September 2010 von KirkZero
Jetzt soll man auch endlich Items einsammeln können.
Hier schonmal die Grafik der Items (Item.png):
user posted image

wird im Ordner GFX unter dem Namen Item.png abgespeichert Wink

Ich benutze hier erstmal nur die ersten beiden Items (Bombe und Feuerkraft)


Erstmal eine neue Datei angelegt... TItem.bmx:

BlitzMax: [AUSKLAPPEN]
Const ITEM_AUFGEDECKT	:Int = -1 'Werte für die Items in Konstante setzen
Const ITEM_BOMBE :Int = 1 'erspart mir wieder den Umgang mit Zahlen
Const ITEM_FEUER :Int = 2


Type TItem

Const ITEMFRAMES :Int=3 'Anzahl der Frames der ItemGrafik
Global ItemIMG :TImage = LoadAnimImage("gfx/items.png",TS,TS,0,ITEMFRAMES)

Global ItemListe :TList = CreateList() 'eine globale Liste für alle Items anlegen

Field xFeld :Int 'Koordinaten des Items auf dem Feld
Field yFeld :Int
Field Item :Int 'Welches Item ist es? (I_BOMBE, I_FEUER, usw)
Field Sammler :Int 'wer hat das Item eingesammelt?

Method Update()

End Method

Method Zeichnen()
DrawImage (ItemIMG,xFeld*TS,yFeld*TS,Item)
End Method

Function Erstellen:TItem (xFeld:Int,yFeld:Int,Item:Int)
Local NeuesItem:TItem = New TItem

NeuesItem.xFeld = xFeld
NeuesItem.yFeld = yFeld
NeuesItem.Item = Item

ItemListe.AddLast(NeuesItem) 'neues Item der globalen ItemListe hinzufügen
End Function


End Type


Denke mal, die Kommentare sagen soweit alles und ansonsten ist das ganze ja fast genauso, wie bei den Bomben. Die Update() Methode ist noch leer, kommt aber noch. Die Konstanten ganz am Anfang erleichtern mir nur wieder den Umgang mit Zahlen und werden ausserhalb des Types angelegt, damit ich sie überall benutzen kann.

diese Datei kommt wieder in den INCLUDES Ordner.
Noch schnell in der MainGame.bmx includieren:

BlitzMax: [AUSKLAPPEN]
'------------------------------ includes			----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"
Include "INCLUDES/TBombe.bmx"
Include "INCLUDES/TExplosion.bmx"
Include "INCLUDES/TItem.bmx"



Nun muss ich natürlich auch ein paar der Items auf der Map verstecken. Das erledige ich in der Erstellen() Funktion von TKarte.bmx unter der Stelle, an der ich die SoftBlöcke auf der KArte gesetzt habe:

BlitzMax: [AUSKLAPPEN]
		'#############################################
'# MAP MIT ITEMS FÜLLEN #
'# ITEMS DÜRFEN NUR AUF FELDER MIT SOFTBLOCK #
'#############################################
Local AnzahlBomben :Int = 10 'wieviel von welchem Item sollen vorkommen?
Local AnzahlFeuer :Int = 10

Repeat 'BombenItems verstecken
For Local Y:Int=1 To MY
For Local X:Int=1 To MX
If AnzahlBomben > 0 'wenn noch Items zu verstecken sind, dann
If NeueMap.Feld[X,Y].Inhalt = SBLOCK And NeueMap.Feld[X,Y].Item = 0
'wenn auf aktuellem Feld ein Softblock ist, UND noch KEIN Item
If Rand (0,20) = 10 Then 'bei der richtigen Zufallszahl...
NeueMap.Feld[X,Y].Item = ITEM_BOMBE 'Item verstecken
AnzahlBomben:-1 'gelegtes Item von den noch zu legenden abziehen
End If
End If
End If
Next
Next
Until (AnzahlBomben=0)

Repeat 'FeuerItems verstecken
For Local Y:Int=1 To MY
For Local X:Int=1 To MX
If AnzahlFeuer > 0 'wenn noch Items zu verstecken sind, dann
If NeueMap.Feld[X,Y].Inhalt = SBLOCK And NeueMap.Feld[X,Y].Item = 0
'wenn auf aktuellem Feld ein Softblock ist, UND noch KEIN Item
If Rand (0,20) = 10 Then 'bei der richtigen Zufallszahl...
NeueMap.Feld[X,Y].Item = ITEM_FEUER 'Item verstecken
AnzahlFeuer:-1 'gelegtes Item von den noch zu legenden abziehen
End If
End If
End If
Next
Next
Until (AnzahlFeuer=0)


Vielleicht fragt sich ja der ein oder andere, warum ich Zufallszahlen von 0 bis 20 nehme und nicht wie bei den Softblöcken 0 bis 2. Ganz einfach, wenn ich einen kleinen Wertebereich habe, kommt meine gewählte Zahl häufiger, als bei einem großem Wertebereich. Die Folge wäre, das so ziemlich alle Items im Oberen linken Teil der Karte versteckt wären und nicht auf der ganzen Map.

hier noch die vollständige Erstellen() Funktion von TKarte.bmx:

BlitzMax: [AUSKLAPPEN]
	Function Erstellen:TKarte(MX:Int,MY:Int,Boden:Int,HardBl:Int,SoftBl:Int)
Local NeueMap:TKarte = New TKarte 'hier wird die neue Map erstmal Temporär gespeichert

NeueMap.Feld=New TFeld[MX+1,MY+1] 'Map Dimensionieren
NeueMap.MX=MX 'Breite der Map speichern
NeueMap.MY=MY 'Höhe der Map speichern
'#############################################
'# STANDARTMAP ERSTELLEN #
'#############################################
For Local Y:Int=1 To MY 'jedes Feld von 1 bis zur Höhe der Map
For Local X:Int=1 To MX 'jedes Feld von 1 bis zur Breite der Map
NeueMap.Feld[X,Y] = TFeld.Erstellen(Boden) 'die einzelnen Felder werden erstellt
'siehe TFeld.Erstellen Funktion
If X=1 Or Y=1 Or X=MX Or Y=MY Then
NeueMap.Feld[X,Y].BlockTile =HardBl
NeueMap.Feld[X,Y].Inhalt =HBLOCK
End If
'haben X oder Y eine Wert, welcher den Rand der Map darstellt, so wird das
'BlockTile auf einen HardBlock gesetzt. Auf diese Weise zeichne ich den Rand
'der Map

If X=3 Or X=5 Or X=7 Or X=9 Or X=11 Or X=13 Then
If Y=3 Or Y=5 Or Y=7 Or Y=9 Or Y=11 Or Y=13 Then
NeueMap.Feld[X,Y].BlockTile =HardBl
NeueMap.Feld[X,Y].Inhalt =HBLOCK
End If
End If
'haben der X und der Y Wert eine Position erreicht, die wichtig für das
'Schachbrettähnliche Muster sind, so wird ebenfalls ein HardBlock an die
'Position gesetzt
Next
Next
'#############################################
'# MAP MIT 100 SOFTBLÖCKEN FÜLLEN #
'# DABEI SPIELER-STARTECKEN FREI LASSEN #
'#############################################
Local BlockZahl:Int = 100 '100 Softblöcke sollen gesetzt werden
Repeat 'eine Schleife, die solange läuft, bis 100 SoftBlöcke gesetzt worden sind
For Local Y:Int=1 To MY 'ich gehe wieder die ganze Map durch
For Local X:Int=1 To MX
Local Check:Int=0 'setze Check auf 0
If Y=2 Or Y=3 Or Y=MY-2 Or Y=MY-1 Then Check:+1 'für die StartEcken
If X=2 Or X=3 Or X=MX-2 Or X=MX-1 Then Check:+1 'wie zuvor beschrieben
If BlockZahl=0 Or (Y=8 And X=8) Then Check=2
'habe ich schon 100 Blöcke gesetzt ODER ist das aktuelle Feld grade die Mitte
'Dann Check auf 2 setzen, damit kein Block gesetzt wird.
If Check<2 Then
If NeueMap.Feld[X,Y].BlockTile=0 And Rand(0,2)=1 Then
'wenn auf aktuellem Feld noch kein Block ist und Rand(0,2)=1 dann neuen Block setzen
NeueMap.Feld[X,Y].BlockTile =SoftBl
NeueMap.Feld[X,Y].Inhalt =SBLOCK
BlockZahl:-1 'gesetzten Block abziehen (denn ich will ja nur 100 haben)
End If
End If
Next
Next
Until (BlockZahl=0) 'wenn 100 Softblöcke gesetzt wurden Schleife verlassen und Map zurück geben.
'#############################################
'# MAP MIT ITEMS FÜLLEN #
'# ITEMS DÜRFEN NUR AUF FELDER MIT SOFTBLOCK #
'#############################################
Local AnzahlBomben :Int = 10 'wieviel von welchem Item sollen vorkommen?
Local AnzahlFeuer :Int = 10

Repeat 'BombenItems verstecken
For Local Y:Int=1 To MY
For Local X:Int=1 To MX
If AnzahlBomben > 0 'wenn noch Items zu verstecken sind, dann
If NeueMap.Feld[X,Y].Inhalt = SBLOCK And NeueMap.Feld[X,Y].Item = 0
'wenn auf aktuellem Feld ein Softblock ist, UND noch KEIN Item
If Rand (0,20) = 10 Then 'bei der richtigen Zufallszahl...
NeueMap.Feld[X,Y].Item = ITEM_BOMBE 'Item verstecken
AnzahlBomben:-1 'gelegtes Item von den noch zu legenden abziehen
End If
End If
End If
Next
Next
Until (AnzahlBomben=0)

Repeat 'FeuerItems verstecken
For Local Y:Int=1 To MY
For Local X:Int=1 To MX
If AnzahlFeuer > 0 'wenn noch Items zu verstecken sind, dann
If NeueMap.Feld[X,Y].Inhalt = SBLOCK And NeueMap.Feld[X,Y].Item = 0
'wenn auf aktuellem Feld ein Softblock ist, UND noch KEIN Item
If Rand (0,20) = 10 Then 'bei der richtigen Zufallszahl...
NeueMap.Feld[X,Y].Item = ITEM_FEUER 'Item verstecken
AnzahlFeuer:-1 'gelegtes Item von den noch zu legenden abziehen
End If
End If
End If
Next
Next
Until (AnzahlFeuer=0)

Return NeueMap 'hiermit gebe ich die temporär erstellte Map zurück
End Function


Jetzt, wo die Items auf der Map verteilt sind, muss ich sie nur noch erstellen, sobald ein Softblock, unter dem sich ein Item befindet, zerstört wird. Das wird in der Update() Methode von TFeld.bmx erledigt:

BlitzMax: [AUSKLAPPEN]
	Method Update(x:Int,y:Int)
If Inhalt = NIX And BlockTile > 0'wenn NIX im Feld aber ein BlockTile da ist
BlockTile = 0 'BlockTile löschen
End If
If Inhalt = NIX And Item > 0'wenn NIX im Feld und ein Item wurde freigelegt
TItem.Erstellen(x,y,Item) 'Item erstellen
Item = ITEM_AUFGEDECKT 'und als aufgedeckt markieren.
End If
End Method



Der Methode müssen nun Parameter übergeben werden, damit das Item auch seine x und y Koordinaten bekommt. Also muss der Aufruf der Update() Methode von TFeld ebenfalls angepasst werden.
Aufgerufen wird sie in TKarte.bmx in der Methode Zeichnen. Also passe ich den Aufruf dort an:

BlitzMax: [AUSKLAPPEN]
	Method Zeichnen()
For Local Y:Int=1 To MX
For Local X:Int=1 To MY
Feld[X,Y].Update(X,Y) 'Aktuelles Feld auf Richtigkeit prüfen
Feld[X,Y].Zeichnen(X,Y) 'Übergibt X und Y Werte an Feld.Zeichnen
Next
Next
End Method


Erstellt werden die Items nun... doch ich muss sie auch in der Hauptschleife von MainGame Zeichnen lassen:

BlitzMax: [AUSKLAPPEN]
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt

For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

For Local Explosion:TExplosion = EachIn TExplosion.ExplosionsListe 'jede erstellte Explosion durchgehen
Explosion.Update()
Explosion.Zeichnen()
Next

For Local Item:TItem = EachIn TItem.ItemListe 'jedes erstellte Item durchgehen
Item.Update()
Item.Zeichnen()
Next

Spieler[0].Zeichnen()
Spieler[0].Steuerung()

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts gedrückt wird
End


Ich rufe hier auch schonmal die Update() Methode von TItem mit auf, auch wenn diese noch "leer" ist. Da ich die Update() Methode aber auch gleich noch bearbeite, brauch ich sie dann nicht mehr in die Hauptschleife einfügen.

Ab jetzt erscheinen die Items, sobald man einen Block zerstört, unter dem eines versteckt ist Wink

Nun gehts ans einsammeln...
Dazu erhält TSpieler.bmx auch erstmal eine "leere" Update() Methode:

BlitzMax: [AUSKLAPPEN]
	Method Update()

End Method


diese binde ich auch gleich in die Hauptschleife ein:

BlitzMax: [AUSKLAPPEN]
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt

For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

For Local Explosion:TExplosion = EachIn TExplosion.ExplosionsListe 'jede erstellte Explosion durchgehen
Explosion.Update()
Explosion.Zeichnen()
Next

For Local Item:TItem = EachIn TItem.ItemListe 'jedes erstellte Item durchgehen
Item.Update()
Item.Zeichnen()
Next

Spieler[0].Steuerung()
Spieler[0].Update()
Spieler[0].Zeichnen()

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts gedrückt wird
End


nun muss ich in der Update() Methode (von TSpieler.bmx) nur noch abfragen, ob das aktuelle Feld, auf dem sich der Spieler befindet, den Flag ITEM_AUFGEDECKT hat. Ist das der Fall, so gehe ich die TList der Items durch und überpfrüfe, welches Item die selben FeldKoordinaten wie der Spieler hat und gebe an das Item weiter, wer es gesammelt hat. Ausserdem entfehrne ich den Flag ITEM_AUFGEDECKT damit kein andere Spieler das Item mehr sammeln kann:

BlitzMax: [AUSKLAPPEN]
	Method Update()
If Map.Feld[xFeld,yFeld].Item = ITEM_AUFGEDECKT 'wenn aufgedecktes Item auf SpielerFeld
For Local Item:TItem = EachIn TItem.ItemListe 'jedes erstellte Item durchgehen
If xFeld=Item.xFeld And yFeld=Item.yFeld 'wenn richtiges Item gefunden
Map.Feld[xFeld,yFeld].Item = 0 'ItemFlag auf 0
Item.Sammler = SpielerNr 'Spieler der es gesammelt hat festlegen
End If
Next
End If
End Method


Jetzt weiter zur bisher "leeren" Update() Methode von TItem.bmx. Da ich ja nun dem Item übergebe, wer es gesammelt hat, werden dort, dem Spieler, die eingesammelten Items und ihre Fähigkeiten gutgeschrieben. Dazu frage ich ab, ob das Item einen Sammler hat. Ist das der Fall, so wird jeh nach eingesammeltem Item (Select... Case...) der entsprechende Bonus freigegeben. Das Item muss dann natürlich auch gelöscht werden:

BlitzMax: [AUSKLAPPEN]
	Method Update()
If Sammler > 0 'wenn jemand das Item eingesammelt hat
Select Item
Case ITEM_BOMBE
'Spieler kann eine Bombe mehr legen
Case ITEM_FEUER
'Spieler erhält mehr FeuerKraft
End Select

ItemListe.Remove(Self) 'Item aus Liste löschen
End If
End Method


und nu hab ich richtig Mist gebaut. Spieler1 kann niemals Items einsammeln... und warum nicht? Weil Spieler1 beim Erstellen seiner Variable SpielerNr den Wert = 0 zugewiesen bekommt. Da das Item nur eingesammelt werden kann, wenn Sammler (=SpielerNr) größer ist als 0 wird das nie funktionieren.
Also stehen wieder ein paar Änderungen an der MainGame.bmx an. So wird Spieler1 zur Zeit erstellt:

BlitzMax: [AUSKLAPPEN]
'------------------------------ Type-Erstellungen	----------
Global Map:TKarte = TKarte.Erstellen(15,15,0,1,2)
Global Spieler:TSpieler [4] 'es soll maximal 4 Spieler geben
Spieler[0] = TSpieler.Erstellen(0,2,2,"GFX/robo1.png",20) 'Spieler 1 wird erstellt


Spieler[0] wird also zu Spieler[1] und die SpielerNr welche in der Erstellen() Funktion übergeben wird muss ebenfalls 1 werden:

Spieler[1] = TSpieler.Erstellen(1,2,2,"GFX/robo1.png",20) 'Spieler 1 wird erstellt

(Änderungen FETT hervorgehoben.)

Wenn es weiterhin 4 spieler geben soll, muss natürlich auch das Array Spieler[] anders dimensioniert werden. So ist es grad noch falsch:

BlitzMax: [AUSKLAPPEN]
Global Spieler:TSpieler [4] 'es soll maximal 4 Spieler geben


So ist es dann richtig:

Global Spieler:TSpieler [5] 'es soll maximal 4 Spieler geben, [5] weil es bei 0 anfängt

(Änderung wieder FETT hervorgehoben.)

und natürlich muss das ganze nun in der Hauptschleife auch nicht mehr mit Spieler[0], sondern mit Spieler[1] aufgerufen werden... so wie hier:

BlitzMax: [AUSKLAPPEN]
	Spieler[1].Steuerung()
Spieler[1].Update()
Spieler[1].Zeichnen()


damit alles komplett ist... hier nochmal die komplette MainGame.bmx wie sie sein muss:

BlitzMax: [AUSKLAPPEN]
'MainGame.bmx

SuperStrict 'zwingt mich, sauber zu programmieren Wink
Framework brl.max2d
Import brl.Timer
Import brl.Random
Import brl.D3D9Max2D
Import brl.PNGLoader

AppTitle = "Bomberman Klon (Community Tutorial)"

'------------------------------ includes ----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"
Include "INCLUDES/TBombe.bmx"
Include "INCLUDES/TExplosion.bmx"
Include "INCLUDES/TItem.bmx"

'------------------------------ Grafikmodus ----------
Const BREITE :Int = 800
Const HOEHE :Int = 600 'Ich setze die Bildschirmbreite und Höhe als Konstante
Global Vollbild :Int = False 'Ob Vollbild oder nicht, soll später der User entscheiden, darum keine Konstante

SetGraphicsDriver D3D9Max2DDriver()

Graphics BREITE, HOEHE, Vollbild 'Bildschirmmodus wird gesetzt
'AutoMidHandle(1)
'------------------------------ Vorbereitungen ----------
Const TS :Int = 32 'Tilegröße (32*32)

Global Frames :TTimer = CreateTimer (120) 'damit das Spiel auf jedem Rechner gleichschnell läuft setze ich einen Timer

SeedRnd (MilliSecs()) 'Da ich den "Zufall" im Spiel benötige,füttere ich ihn schonmal mit der Zeit seit dem Systemstart
'------------------------------ Grafiken laden ----------



'------------------------------ Sounds laden ----------



'------------------------------ Type-Erstellungen ----------
Global Map:TKarte = TKarte.Erstellen(15,15,0,1,2)
Global Spieler:TSpieler [5] 'es soll maximal 4 Spieler geben, [5] weil es bei 0 anfängt
Spieler[1] = TSpieler.Erstellen(1,2,2,"GFX/robo1.png",20) 'Spieler 1 wird erstellt

'------------------------------ Hauptschleife ----------
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt

For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

For Local Explosion:TExplosion = EachIn TExplosion.ExplosionsListe 'jede erstellte Explosion durchgehen
Explosion.Update()
Explosion.Zeichnen()
Next

For Local Item:TItem = EachIn TItem.ItemListe 'jedes erstellte Item durchgehen
Item.Update()
Item.Zeichnen()
Next

Spieler[1].Steuerung()
Spieler[1].Update()
Spieler[1].Zeichnen()

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts gedrückt wird
End


sooooo... nun funktioniert auch das Item sammeln... wenn ich nun über ein Item laufe, verschwindet es. Mehr passiert allerdings noch nicht. Damit aber etwas passiert, muss ich nun wieder die Update() Methode von TItem.bmx bearbeiten und die Select... Case... Bedingung mit Code füllen.

Sammelt der Spieler eine extra Bombe (ITEM_BOMBE) so muss ich einfach nur dem Sammelden Spieler die Variable BombenMenge um 1 erhöhen. Sammelt er Feuer (ITEM_FEUER) so erhöhe ich ihm einfach die Variable FeuerKraft um 1.

Die neue Update() Methode von TItem.bmx:

BlitzMax: [AUSKLAPPEN]
	Method Update()
If Sammler > 0 'wenn jemand das Item eingesammelt hat
Select Item
Case ITEM_BOMBE
Spieler[Sammler].BombenMenge:+1 'Spieler kann eine Bombe mehr legen
Case ITEM_FEUER
Spieler[Sammler].FeuerKraft:+1 'Spieler erhält mehr FeuerKraft
End Select

ItemListe.Remove(Self) 'Item aus Liste löschen
End If
End Method


und was soll ich sagen... ES FUNKTIONIERT!!!! *freu*

es sei noch kurz angemerkt, das egal, wieviele ITEM_BOMBE man sammelt, man kann nie mehr als 5 legen, da ich das vorher in TSpieler.bmx so festgelegt habe:

BlitzMax: [AUSKLAPPEN]
Field MaxBombenMenge	:Int = 5 'egal wieviele Items man sammelt, man kann nie mehr legen, als hier verzeichnet


wer also mehr legen will, muss hier den Wert erhöhen Idea

So... ab jetzt kann man Items sammeln. Sind zwar bisher nur 2, aber das lässt sich ja leicht ändern Wink

und nun gibts mal wieder den KOMPLETTEN Code (inklusive .exe) bis zu diesem Punkt zum Downloaden:

https://www.blitzforum.de/upload/file.php?id=9457


Ich bedanke mich mal wieder fürs Lesen... und wie immer gilt: Anregungen und Kritik (positiv, wie negativ) sowie Verbesserungsvorschläge sind sehr gerne gesehen!!!

Vorbereitungen für die Items

Sonntag, 5. September 2010 von KirkZero
Auf zu den Items:

erstmal neues Field in TFeld.bmx:

BlitzMax: [AUSKLAPPEN]
Field Item		:Int 'bekommt den Wert des Items, wenn kein Item, dann = 0


Sollte später unter dem SoftBlock/auf dem Feld ein Item sein, so entspricht der Wert des Feldes, welches Item dort liegt.

Auch TSpieler benötigt neue Fields:

BlitzMax: [AUSKLAPPEN]
Field SpielerNr		:Int 'Welcher Spieler bin ich? 1,2,3 oder 4
Field BombenMenge :Int = 1 'Anzahl an Bomben, die der Spieler gleichzeitig legen kann
Field MaxBombenMenge :Int = 5 'egal wieviele Items man sammelt, man kann nie mehr legen, als hier verzeichnet
Field GelegteBomben :Int 'Wieviel hat der Spieler bereits gelegt und sind noch nicht explodiert?


BombenMenge sollte klar sein. Bedeutet, wieviele Bomben der Spieler gleichzeitig auf der Map legen kann.
MaxBombenMenge steht dafür, wieviele Bomben man ALLGEMEIN überhaupt legen darf... unabhängig davon, wieviele Items,welche einem mehr Bomben geben, man eingesammelt hat.
In der Variable gelegte Bomben wird festgehalten, wieviele Bomben der Spieler bereits gelegt hat, welche noch nicht explodiert sind.

Jede neu erstellte Klasse TSpieler soll nun bei der Erstellung mit auf den Weg bekommen, welcher Spieler er eigentlich ist. dieser Wert wird in SpielerNr gespeichert.

Hier die neue Erstellen() Funktion von TSpieler:

BlitzMax: [AUSKLAPPEN]
	Function Erstellen:TSpieler (SpielerNr:Int,xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.SpielerNr = SpielerNr 'welcher Spieler bin ich?
NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function



Nun noch ein paar Änderungen an der MainGame.bmx. Da der Spieler nun seine SpielerNr mitbekommen soll, muss die Erstellen() Funktion anders aufgerufen werden. Ausserdem ist der Spieler nun ein Array:

BlitzMax: [AUSKLAPPEN]
'------------------------------ Type-Erstellungen	----------
Global Map:TKarte = TKarte.Erstellen(15,15,0,1,2)
Global Spieler:TSpieler [4] 'es soll maximal 4 Spieler geben
Spieler[0] = TSpieler.Erstellen(0,2,2,"GFX/robo1.png",20) 'Spieler 1 wird erstellt


Hauptschleife noch anpassen:

BlitzMax: [AUSKLAPPEN]
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt

For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

For Local Explosion:TExplosion = EachIn TExplosion.ExplosionsListe 'jede erstellte Explosion durchgehen
Explosion.Update()
Explosion.Zeichnen()
Next

Spieler[0].Zeichnen()
Spieler[0].Steuerung()

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts gedrückt wird
End


und schon läuft das ganze wieder.

Jetzt muss ich noch jeder Bombe, die ich lege ebenfalls mit auf den Weg geben, welcher Spieler sie gelegt hat. TBombe bekommt also ein neues Field und eine angepasste Erstellen() Funktion:
BlitzMax: [AUSKLAPPEN]

Field Besitzer :Int 'wer hat die Bombe gelegt ?


BlitzMax: [AUSKLAPPEN]
	Function Erstellen:TBombe(Besitzer:Int,xFeld:Int,yFeld:Int,FeuerKraft:Int) 'neue Bombe wird erstellt
Local NeueBombe:TBombe = New TBombe

NeueBombe.Besitzer = Besitzer 'Wer hat die Bombe gelegt?
NeueBombe.xFeld=xFeld 'Koordinaten der neuen Bombe übergeben
NeueBombe.yFeld=yFeld

NeueBombe.FeuerKraft=FeuerKraft

Map.Feld[xFeld,yFeld].Inhalt = BOMBE 'wichtig! Inhalt des Feldes auf Bombe setzen
BombenListe.AddLast(NeueBombe) 'neue Bombe der globalen BombenListe hinzufügen
End Function


und den Aufruf in TSpieler ebenfalls angepasst:

BlitzMax: [AUSKLAPPEN]
	Method BombeLegen()
If Map.Feld[xFeld,yFeld].Inhalt = NIX 'wenn aktuelles Feld leer ist
TBombe.Erstellen(SpielerNr,xFeld,yFeld,FeuerKraft)
End If
End Method



Wo ich schonmal bei der Methode BombeLegen bin...
Ich möchte ja, das der Spieler nur so viele Bomben legen kann, wie er Items dafür eingesammelt hat und dazu noch nicht mehr als in der Variablen MaxBombenMenge angegeben. Also muss eine If Abfrage her:

BlitzMax: [AUSKLAPPEN]
	Method BombeLegen()
If Map.Feld[xFeld,yFeld].Inhalt = NIX 'wenn aktuelles Feld leer ist
If GelegteBomben < BombenMenge And GelegteBomben < MaxBombenMenge
'wenn bisher weniger Bomben gelegt als man kann UND als man darf
TBombe.Erstellen(SpielerNr,xFeld,yFeld,FeuerKraft) 'Bombe erstellen
GelegteBomben:+1 'um 1 erhöhen, da ja nun eine bombe gelegt wurde
End If
End If
End Method


Startet man nun das Programm, so kann man nur eine Bombe legen. Wenn diese explodiert kann man allerdings keine weitere legen, da die Variable GelegteBomben nach der Explosion nicht wieder um 1 reduziert wird.
Das erledige ich in TBombe.bmx in der Update() Methode:

BlitzMax: [AUSKLAPPEN]
	Method Update()

If MilliSecs() > BombenTimer+BombenIntervall
BombenCountDown:-1 'Intervall erreicht, CountDown um 1 verringern
BombenTimer=MilliSecs()
If BombenFrame < BOMBENFRAMES-1
BombenFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
End If
End If

If Map.Feld[xFeld,yFeld].Explosion = True 'Explosion auf Feld
BombenCountDown = 0 'Bombe zur Explosion bringen
End If

If BombenCountDown <=0 'CountDown ist abgelaufen *BOOOM*
Spieler[Besitzer].GelegteBomben:-1 'damit der Spieler wieder neue Bomben legen kann
BombenListe.Remove(Self) 'Bombe aus Liste löschen
Map.Feld[xFeld,yFeld].Inhalt = NIX 'wichtig! Inhalt des Feldes auf NIX zurück setzen
TExplosion.Erstellen(xFeld,yFeld,FeuerKraft) 'Explosion wird erstellt
End If

End Method



Diese Stelle sorgt dafür, das der Spieler nach der Explosion der Bombe auch wieder weitere legen kann:

BlitzMax: [AUSKLAPPEN]
		If BombenCountDown <=0 'CountDown ist abgelaufen *BOOOM*
Spieler[Besitzer].GelegteBomben:-1 'damit der Spieler wieder neue Bomben legen kann
BombenListe.Remove(Self) 'Bombe aus Liste löschen
Map.Feld[xFeld,yFeld].Inhalt = NIX 'wichtig! Inhalt des Feldes auf NIX zurück setzen
TExplosion.Erstellen(xFeld,yFeld,FeuerKraft) 'Explosion wird erstellt
End If


Noch kurz ein Testlauf....
Funktioniert perfekt Wink

nun ist alles soweit für die ersten beiden Items vorbereitet Wink
Es handelt sich dabei um ein Item, welches die Feuerkraft erhöht und das andere die Anzahl der Bomben, die man legen kann.
Diese gibts aber erst im nächsten Eintrag

Hier nochmal alle .bmx Dateien, die ich bearbeitet habe:

BlitzMax: [AUSKLAPPEN]
Type TFeld
Const BODENFRAMES :Int=5
Const BLOCKFRAMES :Int=3
Global BodenTilesIMG :TImage = LoadAnimImage("gfx/boden.png",TS,TS,0,BODENFRAMES)
Global BlockTilesIMG :TImage = LoadAnimImage("gfx/block.png",TS,TS,0,BLOCKFRAMES)

Field BodenTile :Int 'die Bodenfliesen. Index für AnimImage welches die Bodenfliesen enthält
Field BlockTile :Int 'die Blöcke. Index für AnimImage. 0=erstes Tile im AnimImage leerer/durchsichtiger Block
Field Inhalt :Int '0=begehbar/kein Block, 1=zerstörbarer Block, 5=Bombe, 10=unzerstöbar/unpassierbar
Field Explosion :Int 'wird auf true gesetzt, solange eine Explosion auf dem Feld ist
Field Item :Int 'bekommt den Wert des Items, wenn kein Item, dann = 0

Method Update()
If Inhalt = NIX And BlockTile > 0'wenn NIX im Feld aber ein BlockTile da ist
BlockTile = 0 'BlockTile löschen
End If
End Method

Method Zeichnen(x:Int,y:Int) 'die Arraywerte für X und Y werden übergeben
DrawImage(BodenTilesIMG,x*TS,y*TS,BodenTile) 'und beim Zeichnen mit der TileSize (TS=32)
DrawImage(BlockTilesIMG,x*TS,y*TS,BlockTile) 'multipliziert, damit sie den richtigen Abstand zueinander haben

End Method

Function Erstellen:TFeld(Boden:Int) 'BodenTile wird übergeben
Local NeuesFeld:TFeld = New TFeld
NeuesFeld.BodenTile=Boden
Return NeuesFeld
End Function


End Type



BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field SpielerNr :Int 'Welcher Spieler bin ich? 1,2,3 oder 4
Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2 'man bewegt sich immer um 2 Pixel pro Bewegung

Field FeuerKraft :Int = 2 'Reichweite der Explosionen einer Bombe (2 Felder in alle Richtungen)
Field BombenMenge :Int = 1 'Anzahl an Bomben, die der Spieler gleichzeitig legen kann
Field MaxBombenMenge :Int = 5 'egal wieviele Items man sammelt, man kann nie mehr legen, als hier verzeichnet
Field GelegteBomben :Int 'Wieviel hat der Spieler bereits gelegt und sind noch nicht explodiert?

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 20

Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Bewegung wieder erlaubt (Zeit)
If KeyDown(KEY_UP) 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If
End If
If KeyHit(KEY_LCONTROL) 'wenn linke STRG Taste gedrückt wird
BombeLegen() 'dann Methode zum Bombenlegen aufrufen
End If
End Method

Method BombeLegen()
If Map.Feld[xFeld,yFeld].Inhalt = NIX 'wenn aktuelles Feld leer ist
If GelegteBomben < BombenMenge And GelegteBomben < MaxBombenMenge
'wenn bisher weniger Bomben gelegt als man kann UND als man darf
TBombe.Erstellen(SpielerNr,xFeld,yFeld,FeuerKraft) 'Bombe erstellen
GelegteBomben:+1 'um 1 erhöhen, da ja nun eine bombe gelegt wurde
End If
End If
End Method

Method Bewegung(Richtung:Int)
Local x :Int 'Temporäre Speicher
Local y :Int
Local fx :Int 'Temporäre Speicher für zu prüfende Felder
Local fy :Int 'ist fy=-1 wird oberes vom aktuellen Feld geprüft

Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

Select Richtung
Case OBEN
fy=-1 'oberes Feld prüfen
Case UNTEN
fy=+1 'unteres Feld prüfen
Case LINKS
fx=-1 'linkes Feld prüfen
Case RECHTS
fx=+1 'rechtes Feld prüfen
End Select

If Map.Feld[xFeld+fx,yFeld+fy].Inhalt = NIX 'wenn auf dem zu prüfenden Feld NIX ist

If fy <> 0 'ist das zu prüfende Feld oben oder unten, dann auf Autobewegung checken
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else
y=fy*Speed 'passt es genau, dann um Speed Pixel nach fy bewegen
End If
End If

If fx <> 0 'ist das zu prüfende Feld links oder rechts, dann auf Autobewegung checken
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=fx*Speed 'passt es genau, dann um Speed Pixel nach fx bewegen
End If
End If

Else If fy <> 0 And yMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
y=fy*Speed
Else If fx <> 0 And xMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
x=fx*Speed
End If

yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x
MapFeld() 'Aktuelles Map.Feld[,] wird berechnet
BewegungsTimer = MilliSecs()

End Method

Method MapFeld() 'berechnet aktuelles Map.Feld[,]
Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

If yMod < (TS/2) Then yFeld=(yPos/TS) 'wenn modulus < als 16
If yMod > (TS/2) Then yFeld=(yPos/TS)+1 'wenn modulus > als 16
If xMod < (TS/2) Then xFeld=(xPos/TS) 'wenn modulus < als 16
If xMod > (TS/2) Then xFeld=(xPos/TS)+1 'wenn modulus > als 16

End Method

Method Zeichnen()
'DrawRect (xFeld*TS,yFeld*TS,TS,TS) 'Erstmal auskommentiert... vorerst klappt ja alles Wink
DrawImage (SpielerIMG,xPos,yPos,0)
End Method

Function Erstellen:TSpieler (SpielerNr:Int,xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.SpielerNr = SpielerNr 'welcher Spieler bin ich?
NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type




BlitzMax: [AUSKLAPPEN]
Type TBombe
Const BOMBENFRAMES :Int=3 'Anzahl der Frames der BombenGrafik
Global BombenIMG :TImage = LoadAnimImage("gfx/bombe.png",TS,TS,0,BOMBENFRAMES)

Global BombenListe :TList = CreateList() 'eine globale Liste für alle Bomben anlegen

Field Besitzer :Int 'wer hat die Bombe gelegt ?
Field xFeld :Int 'Koordinaten der Bombe auf dem Feld
Field yFeld :Int

Field FeuerKraft :Int 'Reichweite der Explosionen einer Bombe

Field BombenFrame :Int = 0 'Aktuelles Frame der BombenGrafik (0=erstes Frame)
Field BombenCountDown :Int = 3 'jede Bombe soll nach 3 Sekunden (oder Intervallen) explodieren
Field BombenIntervall :Int = 1000 'der Intervall mit dem runtergezählt wird (1000 = 1 Sekunde)
Field BombenTimer :Int = MilliSecs() 'aktuelle Zeit setzen

Method Update()

If MilliSecs() > BombenTimer+BombenIntervall
BombenCountDown:-1 'Intervall erreicht, CountDown um 1 verringern
BombenTimer=MilliSecs()
If BombenFrame < BOMBENFRAMES-1
BombenFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
End If
End If

If Map.Feld[xFeld,yFeld].Explosion = True 'Explosion auf Feld
BombenCountDown = 0 'Bombe zur Explosion bringen
End If

If BombenCountDown <=0 'CountDown ist abgelaufen *BOOOM*
Spieler[Besitzer].GelegteBomben:-1 'damit der Spieler wieder neue Bomben legen kann
BombenListe.Remove(Self) 'Bombe aus Liste löschen
Map.Feld[xFeld,yFeld].Inhalt = NIX 'wichtig! Inhalt des Feldes auf NIX zurück setzen
TExplosion.Erstellen(xFeld,yFeld,FeuerKraft) 'Explosion wird erstellt
End If

End Method

Method Zeichnen()
DrawImage (BombenIMG,xFeld*TS,yFeld*TS,BombenFrame)
End Method

Function Erstellen:TBombe(Besitzer:Int,xFeld:Int,yFeld:Int,FeuerKraft:Int) 'neue Bombe wird erstellt
Local NeueBombe:TBombe = New TBombe

NeueBombe.Besitzer = Besitzer 'Wer hat die Bombe gelegt?
NeueBombe.xFeld=xFeld 'Koordinaten der neuen Bombe übergeben
NeueBombe.yFeld=yFeld

NeueBombe.FeuerKraft=FeuerKraft

Map.Feld[xFeld,yFeld].Inhalt = BOMBE 'wichtig! Inhalt des Feldes auf Bombe setzen
BombenListe.AddLast(NeueBombe) 'neue Bombe der globalen BombenListe hinzufügen
End Function
End Type




BlitzMax: [AUSKLAPPEN]

'MainGame.bmx

SuperStrict 'zwingt mich, sauber zu programmieren Wink
Framework brl.max2d
Import brl.Timer
Import brl.Random
Import brl.D3D9Max2D
Import brl.PNGLoader

AppTitle = "Bomberman Klon (Community Tutorial)"

'------------------------------ includes ----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"
Include "INCLUDES/TBombe.bmx"
Include "INCLUDES/TExplosion.bmx"

'------------------------------ Grafikmodus ----------
Const BREITE :Int = 800
Const HOEHE :Int = 600 'Ich setze die Bildschirmbreite und Höhe als Konstante
Global Vollbild :Int = False 'Ob Vollbild oder nicht, soll später der User entscheiden, darum keine Konstante

SetGraphicsDriver D3D9Max2DDriver()

Graphics BREITE, HOEHE, Vollbild 'Bildschirmmodus wird gesetzt
'AutoMidHandle(1)
'------------------------------ Vorbereitungen ----------
Const TS :Int = 32 'Tilegröße (32*32)

Global Frames :TTimer = CreateTimer (120) 'damit das Spiel auf jedem Rechner gleichschnell läuft setze ich einen Timer

SeedRnd (MilliSecs()) 'Da ich den "Zufall" im Spiel benötige,füttere ich ihn schonmal mit der Zeit seit dem Systemstart
'------------------------------ Grafiken laden ----------



'------------------------------ Sounds laden ----------



'------------------------------ Type-Erstellungen ----------
Global Map:TKarte = TKarte.Erstellen(15,15,0,1,2)
Global Spieler:TSpieler [4] 'es soll maximal 4 Spieler geben
Spieler[0] = TSpieler.Erstellen(0,2,2,"GFX/robo1.png",20) 'Spieler 1 wird erstellt

'------------------------------ Hauptschleife ----------
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt

For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

For Local Explosion:TExplosion = EachIn TExplosion.ExplosionsListe 'jede erstellte Explosion durchgehen
Explosion.Update()
Explosion.Zeichnen()
Next

Spieler[0].Zeichnen()
Spieler[0].Steuerung()

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts gedrückt wird
End



Danke fürs Lesen!!!

Die Bombe kann... BOOOM ;)

Donnerstag, 2. September 2010 von KirkZero
So, die Explosionen sollen nun auch etwas kaputt machen...
nämlich andere Bomben und SoftBlöcke.

Zuerst mal sollen andere Bomben zur Explosion gebracht werden.
Ich setze jedesmal, wenn ich eine Explosion erstelle (siehe vorigen WorklogEintrag) dem Field Explosion den flag True:
BlitzMax: [AUSKLAPPEN]
Map.Feld[xFeld,yFeld].Explosion = True


nun muss ich einfach in der Update() Methode von TBombe.bmx jedesmal prüfen, ob das Feld, auf dem die Bombe liegt im Field Explosion den wert True hat, ist dem so, so lasse ich die Bombe Explodieren:
BlitzMax: [AUSKLAPPEN]
	Method Update()

If MilliSecs() > BombenTimer+BombenIntervall
BombenCountDown:-1 'Intervall erreicht, CountDown um 1 verringern
BombenTimer=MilliSecs()
If BombenFrame < BOMBENFRAMES-1
BombenFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
End If
End If

If Map.Feld[xFeld,yFeld].Explosion = True 'Explosion auf Feld
BombenCountDown = 0 'Bombe zur Explosion bringen
End If

If BombenCountDown <=0 'CountDown ist abgelaufen *BOOOM*
BombenListe.Remove(Self) 'Bombe aus Liste löschen
Map.Feld[xFeld,yFeld].Inhalt = NIX 'wichtig! Inhalt des Feldes auf NIX zurück setzen
TExplosion.Erstellen(xFeld,yFeld,FeuerKraft) 'Explosion wird erstellt
End If

End Method


und schon entzünden Explosionen andere Bomben Wink
Anmerkung von mir: auf diese Weise prüfe ich später auch, ob ein Spieler in einer Explosion steht Idea

Nun will ich auch, dass eine Explosion, die auf eine Bombe trifft, sich nicht weiter fortpflanzt:
Ich weise jedesmal, wenn ich eine Bombe lege (siehe vorigen WorklogEintrag) dem Inhalt des Feldes den Wert der Konstante BOMBE zu:
BlitzMax: [AUSKLAPPEN]
Map.Feld[xFeld,yFeld].Inhalt = BOMBE


Somit muss ich also nur rausfinden, ob der Inhalt des Feldes, auf dem ich eine neue Explosion erstelle den Wert der Konstante BOMBE beträgt:
BlitzMax: [AUSKLAPPEN]
If Map.Feld[xFeld,yFeld].Inhalt = BOMBE Then


ist das der Fall, soll die FeuerKraft auf 0 gesetzt werden, damit sich die Explosion nicht weiter ausbreitet.
Das Ganze kommt nun mit in die Erstellen Funktion von TExplosion:

BlitzMax: [AUSKLAPPEN]
	Function Erstellen:TExplosion(xFeld:Int,yFeld:Int,FeuerKraft:Int,ExplosionsRichtung:Int=ALLE) 
Local NeueExplosion:TExplosion = New TExplosion

NeueExplosion.xFeld=xFeld 'Koordinaten der neuen Explosion übergeben
NeueExplosion.yFeld=yFeld

If Map.Feld[xFeld,yFeld].Inhalt = BOMBE 'wenn Bombe auf Feld
FeuerKraft=0 'auf 0 setzen, Explosion soll sich nicht weiter fortpflanzen
Else
NeueExplosion.FeuerKraft=FeuerKraft 'ansonsten Feuerkraft und
NeueExplosion.ExplosionsRichtung=ExplosionsRichtung 'Richtung in die sie sich "fortpflanzt" übergeben
End If

Map.Feld[xFeld,yFeld].Explosion = True 'wichtig! Inhalt des Feldes auf true setzen
ExplosionsListe.AddLast(NeueExplosion) 'neue Bombe der globalen BombenListe hinzufügen
End Function



Das war einfacher, als ich anfangs dachte Very Happy
Jetzt lassen sich Kettenreaktionen auslösen Wink

so, nun gehts den SoftBlöcken an den Kragen.
Da die Map z.Z. keine Softblöcke enthält (hatte den Wert auf 0 gesetzt, damit ich die Bewegung besser testen konnte), werd ich nun wieder ein paar einfügen. Dazu einfach die Erstellen Funktion von TKarte.bmx bearbeiten:

BlitzMax: [AUSKLAPPEN]
		'#############################################
'# MAP MIT 100 SOFTBLÖCKEN FÜLLEN #
'# DABEI SPIELER-STARTECKEN FREI LASSEN #
'#############################################
Local BlockZahl:Int = 50 '100 Softblöcke sollen gesetzt werden


ich lasse nun einfach mal 50 zufällige Softblöcke erstellen.

Ich muss nun einfach nur prüfen, ob der inhalt des Feldes ein softblock ist, dann die Fortpflanzung der Explosion stoppen und den Inhalt des Feldes auf NIX setzen:
BlitzMax: [AUSKLAPPEN]
		If Map.Feld[xFeld,yFeld].Inhalt = SBLOCK 'wenn SoftBlock auf Feld
FeuerKraft=0 'auf 0 setzen, Explosion soll sich nicht weiter fortpflanzen
Map.Feld[xFeld,yFeld].Inhalt = NIX
End If


Das ganze kommt in die Update() Methode der TExplosion.bmx und zwar ganz am Anfang (es soll ja die Fortpflanzung verhindert werden):

BlitzMax: [AUSKLAPPEN]
	Method Update()

If Map.Feld[xFeld,yFeld].Inhalt = SBLOCK 'wenn SoftBlock auf Feld
FeuerKraft=0 'auf 0 setzen, Explosion soll sich nicht weiter fortpflanzen
Map.Feld[xFeld,yFeld].Inhalt = NIX
End If

If MilliSecs() > ExplosionsTimer+ExplosionsIntervall
ExplosionsTimer=MilliSecs()

If Feuerkraft > 0
FeuerKraft:-1 'um 1 reduzieren, damit korrekter Wert an nächste Explosion gegeben wird

If ExplosionsRichtung = OBEN Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld,yFeld-1].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld,yFeld-1,FeuerKraft,OBEN) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = UNTEN Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld,yFeld+1].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld,yFeld+1,FeuerKraft,UNTEN) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = LINKS Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld-1,yFeld].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld-1,yFeld,FeuerKraft,LINKS) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = RECHTS Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld+1,yFeld].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld+1,yFeld,FeuerKraft,RECHTS) 'neue Explosion in Richtung
End If
End If

FeuerKraft=0 'auf 0 setzen, damit keine endlos Fortpflanzung entsteht
End If

If ExplosionsFrame < EXPLOFRAMES-1
ExplosionsFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
Else 'wenn max Explosionsframes erreicht sind, Explosion löschen
ExplosionsListe.Remove(Self) 'Explosion aus Liste löschen
Map.Feld[xFeld,yFeld].Explosion = False 'Wichtig! Feld auf false setzen
End If
End If

End Method


Jetzt wird bei einer Explosion auf einem Softblock der Inhalt auf NIX gesetzt, und man kann durchlaufen... der Block bleibt aber weiterhin sichtbar. Das liegt daran, das der Inhalt des Feldes nichts mit dessen Grafik zu tun hat. Eine Update() Methode in TFeld.bmx schafft da abhilfe:

BlitzMax: [AUSKLAPPEN]
	Method Update()
If Inhalt = NIX And BlockTile > 0'wenn NIX im Feld aber ein BlockTile da ist
BlockTile = 0 'BlockTile löschen
End If
End Method


so... diese Update() Methode rufe ich in der Zeichnen() Methode von TKarte.bmx auf:

BlitzMax: [AUSKLAPPEN]
	Method Zeichnen()
For Local Y:Int=1 To MX
For Local X:Int=1 To MY
Feld[X,Y].Update() 'Aktuelles Feld auf Richtigkeit prüfen
Feld[X,Y].Zeichnen(X,Y) 'Übergibt X und Y Werte an Feld.Zeichnen
Next
Next
End Method


und schon werden Felder, welche den Inhalt NIX haben, aber dennoch BlockTiles besitzen korrigiert. Die Update() Methode von TFeld werde ich später noch für andere Sachen benutzen Idea


Noch einen WICHTIGE Fehlerkorrektur:
Ich habe grade gemerkt, das es möglich ist, das ein Feld den Status Explosion = True zu früh verlieren kann, wenn sich Explosionen auf einem Feld überschneiden. Um das zu umgehen, setze ich den Status am Anfang der Update() Methode von TExplosion immer wieder erneut auf True!!!:

BlitzMax: [AUSKLAPPEN]
	Method Update()

Map.Feld[xFeld,yFeld].Explosion = True 'muss immer erneut gesetzt werden, da überschneidende
'Explosionen den Status zu früh aufheben können

If Map.Feld[xFeld,yFeld].Inhalt = SBLOCK 'wenn SoftBlock auf Feld
FeuerKraft=0 'auf 0 setzen, Explosion soll sich nicht weiter fortpflanzen
Map.Feld[xFeld,yFeld].Inhalt = NIX
End If

If MilliSecs() > ExplosionsTimer+ExplosionsIntervall
ExplosionsTimer=MilliSecs()

If Feuerkraft > 0
FeuerKraft:-1 'um 1 reduzieren, damit korrekter Wert an nächste Explosion gegeben wird

If ExplosionsRichtung = OBEN Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld,yFeld-1].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld,yFeld-1,FeuerKraft,OBEN) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = UNTEN Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld,yFeld+1].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld,yFeld+1,FeuerKraft,UNTEN) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = LINKS Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld-1,yFeld].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld-1,yFeld,FeuerKraft,LINKS) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = RECHTS Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld+1,yFeld].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld+1,yFeld,FeuerKraft,RECHTS) 'neue Explosion in Richtung
End If
End If

FeuerKraft=0 'auf 0 setzen, damit keine endlos Fortpflanzung entsteht
End If

If ExplosionsFrame < EXPLOFRAMES-1
ExplosionsFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
Else 'wenn max Explosionsframes erreicht sind, Explosion löschen
ExplosionsListe.Remove(Self) 'Explosion aus Liste löschen
Map.Feld[xFeld,yFeld].Explosion = False 'Wichtig! Feld auf false setzen
End If
End If

End Method


Ausserdem kann nun endlich mal das DrawRect weg, welches mir visuell anzeigt, auf welchem Feld sich der Spieler grad befindet. Dazu kommentiere ich den Befehl einfach erstmal aus (vielleicht brauche ich es ja später nocheinmal) das erledige ich dann in der Zeichnen() Methode von TSpieler:

BlitzMax: [AUSKLAPPEN]
	Method Zeichnen()
'DrawRect (xFeld*TS,yFeld*TS,TS,TS) 'Erstmal auskommentiert... vorerst klappt ja alles Wink
DrawImage (SpielerIMG,xPos,yPos,0)
End Method



Download (Code und .EXE) bis hier hin:

https://www.blitzforum.de/upload/file.php?id=9438


Nun werden Softblöcke gesprengt... andere Bomben zur Explosion gebracht und mich bringt es nun zu der Überlegung:

ITEMS
wie machen? Hardcode oder durch script?
Jetzt brauch ich ein bissel Hilfe... Hardcoded wäre das alles kein Problem... aber, da ich ja auch was lernen will, würde ich diese gerne per Script oder sonstiges (externe Datei) ins Spiel bringen.
Welche Möglichkeiten wären sinnvoll???
Gibt es ein tolles Tutorial (englisch oder deutsch ist egal) oder hat da jemand ne tolle Anregung?

Hoffe in der Richtung auf zahlreiche Kommentare und Anregungen... und natürlich dürft ihr mir wie immer auf die Finger hauen, wenn ich bisher mist gebaut habe... Wink

Ich bedanke mich schonmal und mach mir jetzt nen Bier auf *plopp*

Die Bombe tickt

Mittwoch, 1. September 2010 von KirkZero
Lasst mich endlich Bomben legen Wink

Vorerst soll dies mittels der linken steuerungstaste (STRG) geschehen. Also Steuerungsmethode erweitert und da ich dort auch die Methode BombeLegen() aufrufe... ist diese auch gleich mit dabei:
BlitzMax: [AUSKLAPPEN]
	Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Bewegung wieder erlaubt (Zeit)
If KeyDown(KEY_UP) And MilliSecs() 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) And MilliSecs() 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) And MilliSecs() 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If
End If
If KeyHit(KEY_LCONTROL) 'wenn linke STRG Taste gedrückt wird
BombeLegen() 'dann Methode zum Bombenlegen aufrufen
End If
End Method

Method BombeLegen()
If Map.Feld[xFeld,yFeld].Inhalt = NIX 'wenn aktuelles Feld leer ist

End If
End Method

Noch passiert nix, da die Methode BombeLegen nicht wirklich etwas macht... es wird lediglich abgefragt, ob das aktuelle Feld leer ist. Sollte dies der Fall sein, soll eine neue Bombe gelegt werden.

Also neue Datei angelegt: TBombe.bmx
BlitzMax: [AUSKLAPPEN]
Type TBombe

End Type

im INCLUDES Ordner speichern und in der MainGame.bmx per include einbinden:
BlitzMax: [AUSKLAPPEN]
'------------------------------ includes			----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"
Include "INCLUDES/TBombe.bmx"


nun fülle ich TBombe.bmx mit ein wenig Zeugs Wink

BlitzMax: [AUSKLAPPEN]
Type TBombe
Const BOMBENFRAMES :Int=3 'Anzahl der Frames der BombenGrafik
Global BombenIMG :TImage = LoadAnimImage("gfx/bombe.png",TS,TS,0,BOMBENFRAMES)

Global BombenListe:TList = CreateList() 'eine globale Liste für alle Bomben anlegen

Field xFeld :Int 'Koordinaten der Bombe auf dem Feld
Field yFeld :Int

Method Zeichnen()
DrawImage (BombenIMG,xFeld*TS,yFeld*TS,0)
End Method

Function Erstellen:TBombe(xFeld:Int,yFeld:Int) 'neue Bombe wird erstellt
Local NeueBombe:TBombe = New TBombe

NeueBombe.xFeld=xFeld 'Koordinaten der neuen Bombe übergeben
NeueBombe.yFeld=yFeld


Map.Feld[xFeld,yFeld].Inhalt = BOMBE 'wichtig! Inhalt des Feldes auf Bombe setzen
BombenListe.AddLast(NeueBombe) 'neue Bombe der globalen BombenListe hinzufügen
End Function
End Type


-eine Konstante für die Anzahl der Frames die die Grafik der Bombe hat
-die Grafik der Bombe wird geladen (global)
-ich erstelle eine globale TList, in der alle neu erstellten Bomben gespeichert werden
-natürlich benötigt jede Bombe ihre Koordinaten

Grafik der Bombe:
user posted image
dann noch die Zeichnen() Methode, wie bei allen anderen Klassen auch und natürlich die Erstellen() Funktion, in der ich eine neue Bombe erstelle, die Koordinaten übergebe und sie in die TList einfüge.
Dabei ist auch wichtig, Map.Feld[xFeld,yFeld].Inhalt auf eine Bombe zu setzen.

Aufgerufen wird die Erstellen() Funktion in TSpieler.bmx in der Methode BombeLegen():

BlitzMax: [AUSKLAPPEN]
	Method BombeLegen()
If Map.Feld[xFeld,yFeld].Inhalt = NIX 'wenn aktuelles Feld leer ist
TBombe.Erstellen(xFeld,yFeld)
End If
End Method


nun noch in der MainGame.bmx eine Schleife eingefügt, welche jede Bombe der TList durchgeht und diese dann zeichnet:

BlitzMax: [AUSKLAPPEN]

Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt
Spieler1.Zeichnen()
Spieler1.Steuerung()

For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Zeichnen()
Next

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts im Fenster gedrückt wird
End


und schon kann man, wenn man das Programm startet, lustig Bomben auf dem Spielfeld legen.
(Da der Inhalt der Map auf Bombe gesetzt wird, sobald man eine legt, kann man nun auch nicht mehr über solche Felder drüber laufen, sobald man sie, nach dem legen, komplett verlassen hat)
Mehr passiert aber noch nicht.

Jetzt soll die Bombe nach 3 Sekunden einfach verschwinden. Ein Paar zusätliche Fields und eine Update() Methode sollen das für mich erledigen:

BlitzMax: [AUSKLAPPEN]
Type TBombe
Const BOMBENFRAMES :Int=3 'Anzahl der Frames der BombenGrafik
Global BombenIMG :TImage = LoadAnimImage("gfx/bombe.png",TS,TS,0,BOMBENFRAMES)

Global BombenListe:TList = CreateList() 'eine globale Liste für alle Bomben anlegen

Field xFeld :Int 'Koordinaten der Bombe auf dem Feld
Field yFeld :Int

Field BombenFrame :Int = 0 'Aktuelles Frame der BombenGrafik (0=erstes Frame)
Field BombenCountDown :Int = 3 'jede Bombe soll nach 3 Sekunden (oder Intervallen) explodieren
Field BombenIntervall :Int = 1000 'der Intervall mit dem runtergezählt wird (1000 = 1 Sekunde)
Field BombenTimer :Int = MilliSecs() 'aktuelle Zeit setzen

Method Update()

If MilliSecs() > BombenTimer+BombenIntervall
BombenCountDown:-1 'Intervall erreicht, CountDown um 1 verringern
BombenTimer=MilliSecs()
If BombenFrame < BOMBENFRAMES-1
BombenFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
End If
End If
If BombenCountDown=0 'CountDown ist abgelaufen *BOOOM*
BombenListe.Remove(Self) 'Bombe aus Liste löschen
Map.Feld[xFeld,yFeld].Inhalt = NIX 'wichtig! Inhalt des Feldes auf NIX zurück setzen
End If

End Method

Method Zeichnen()
DrawImage (BombenIMG,xFeld*TS,yFeld*TS,BombenFrame)
End Method

Function Erstellen:TBombe(xFeld:Int,yFeld:Int) 'neue Bombe wird erstellt
Local NeueBombe:TBombe = New TBombe

NeueBombe.xFeld=xFeld 'Koordinaten der neuen Bombe übergeben
NeueBombe.yFeld=yFeld


Map.Feld[xFeld,yFeld].Inhalt = BOMBE 'wichtig! Inhalt des Feldes auf Bombe setzen
BombenListe.AddLast(NeueBombe) 'neue Bombe der globalen BombenListe hinzufügen
End Function
End Type

Denke mal, die Kommentare im Code sollten genügen... ansonsten fragen Wink


Update() Methode in die MainGame.bmx eingefügt:

BlitzMax: [AUSKLAPPEN]
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt
Spieler1.Zeichnen()
Spieler1.Steuerung()
For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts im Fenster gedrückt wird
End


und schon verschwinden die gelegten Bomben nach 3 Sekunden wieder.
Ist aber laaaangweilig... will Explosionen... Exclamation

Also, neue Datei erstellt. TExplosion.bmx:
BlitzMax: [AUSKLAPPEN]
Type TExplosion

End Type

im Verzeichnis INCLUDES speichern und in der MainGame.bmx per Include einbinden:
BlitzMax: [AUSKLAPPEN]
'------------------------------ includes			----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"
Include "INCLUDES/TBombe.bmx"
Include "INCLUDES/TExplosion.bmx"


Da ja jeder Spieler später Items einsammeln kann, welche die Feuerkraft erhöhen, und diese für Bomben sowie für die Explosion wichtig ist, führen wir die nun schonmal ein. Das neue Feld nenne ich passenderweise mal FeuerKraft Wink

Zuerst für den Spieler in TSpieler.bmx

BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2 'man bewegt sich immer um 2 Pixel pro Bewegung

Field FeuerKraft :Int = 2 'Reichweite der Explosionen einer Bombe (2 Felder in alle Richtungen)


Der wert soll an jede gelegte Bombe übergeben werden, somit brauchen wir das Feld auch in der TBombe.bmx:

BlitzMax: [AUSKLAPPEN]
Type TBombe
Const BOMBENFRAMES :Int=3 'Anzahl der Frames der BombenGrafik
Global BombenIMG :TImage = LoadAnimImage("gfx/bombe.png",TS,TS,0,BOMBENFRAMES)

Global BombenListe :TList = CreateList() 'eine globale Liste für alle Bomben anlegen

Field xFeld :Int 'Koordinaten der Bombe auf dem Feld
Field yFeld :Int

Field FeuerKraft :Int 'Reichweite der Explosionen einer Bombe


und nun muss ich den Wert beim legen einer Bombe natürlich auch an die Bombe übergeben. Also erstellen Funktion in TBombe.bmx angepasst:
BlitzMax: [AUSKLAPPEN]
	Function Erstellen:TBombe(xFeld:Int,yFeld:Int,FeuerKraft:Int) 'neue Bombe wird erstellt
Local NeueBombe:TBombe = New TBombe

NeueBombe.xFeld=xFeld 'Koordinaten der neuen Bombe übergeben
NeueBombe.yFeld=yFeld

NeueBombe.FeuerKraft=FeuerKraft

Map.Feld[xFeld,yFeld].Inhalt = BOMBE 'wichtig! Inhalt des Feldes auf Bombe setzen
BombenListe.AddLast(NeueBombe) 'neue Bombe der globalen BombenListe hinzufügen
End Function


und den Aufruf der Funktion in TSpieler ebenfalls anpassen:

BlitzMax: [AUSKLAPPEN]
	Method BombeLegen()
If Map.Feld[xFeld,yFeld].Inhalt = NIX 'wenn aktuelles Feld leer ist
TBombe.Erstellen(xFeld,yFeld,FeuerKraft)
End If
End Method


nun wird die FeuerKraft des Spielers an die Bomben die er legt weitergegeben *freu*

nun auf zur Explosion!!! erstmal die verwendete Grafik:

user posted image

Diese gehört natürlich in den Ordner GFX und nennt sich Explosion.png

so, jetzt ab zur TExplosion.bmx. Diese ähnelt der TBombe.bmx sehr ich denke, das ich dann dazu erstmal nichts weiter sagen muss:

BlitzMax: [AUSKLAPPEN]
Type TExplosion
Const EXPLOFRAMES :Int=49 'Anzahl der Frames der ExplosionsGrafik
Global ExplosionsIMG :TImage = LoadAnimImage("gfx/explosion.png",TS,TS,0,EXPLOFRAMES)

Global ExplosionsListe :TList = CreateList() 'eine globale Liste für alle Explosionen anlegen

Field xFeld :Int 'Koordinaten der Explosion auf dem Feld
Field yFeld :Int

Field FeuerKraft :Int 'Reichweite der Explosionen

Field ExplosionsFrame :Int = 0 'Aktuelles Frame der ExplosionsGrafik (0=erstes Frame)
Field ExplosionsIntervall:Int = 25 'der Intervall mit dem die Frames gewechselt werden
Field ExplosionsTimer :Int = MilliSecs() 'aktuelle Zeit setzen


Method Zeichnen()
DrawImage (ExplosionsIMG,xFeld*TS,yFeld*TS,ExplosionsFrame)
End Method

Function Erstellen:TExplosion(xFeld:Int,yFeld:Int,FeuerKraft:Int) 'neue Explosion wird erstellt
Local NeueExplosion:TExplosion = New TExplosion

NeueExplosion.xFeld=xFeld 'Koordinaten der neuen Explosion übergeben
NeueExplosion.yFeld=yFeld

NeueExplosion.FeuerKraft=FeuerKraft

Map.Feld[xFeld,yFeld].Explosion = True 'wichtig! Inhalt des Feldes auf true setzen
ExplosionsListe.AddLast(NeueExplosion) 'neue Bombe der globalen BombenListe hinzufügen
End Function

End Type


jetzt noch die passende Update() Methode um die Frames zu aktualisieren und die Explosion zu löschen, sobald sie zu ende ist:

BlitzMax: [AUSKLAPPEN]
	Method Update()

If MilliSecs() > ExplosionsTimer+ExplosionsIntervall
ExplosionsTimer=MilliSecs()
If ExplosionsFrame < EXPLOFRAMES-1
ExplosionsFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
Else 'wenn max Explosionsframes erreicht sind, Explosion löschen
ExplosionsListe.Remove(Self) 'Explosion aus Liste löschen
Map.Feld[xFeld,yFeld].Explosion = False 'Wichtig! Feld auf false setzen
End If
End If

End Method


WICHTIG:
Map.Feld[xFeld,yFeld].Explosion
ist ein neues Field in der TFeld.bmx, dieses hatte ich dort bisher immer auskommentiert, da ich es nun aber benutze, ändere ich das schnell noch:

BlitzMax: [AUSKLAPPEN]
Type TFeld
Const BODENFRAMES :Int=5
Const BLOCKFRAMES :Int=3
Global BodenTilesIMG :TImage = LoadAnimImage("gfx/boden.png",TS,TS,0,BODENFRAMES)
Global BlockTilesIMG :TImage = LoadAnimImage("gfx/block.png",TS,TS,0,BLOCKFRAMES)

Field BodenTile :Int 'die Bodenfliesen. Index für AnimImage welches die Bodenfliesen enthält
Field BlockTile :Int 'die Blöcke. Index für AnimImage. 0=erstes Tile im AnimImage leerer/durchsichtiger Block
Field Inhalt :Int '0=begehbar/kein Block, 1=zerstörbarer Block, 5=Bombe, 10=unzerstöbar/unpassierbar
Field Explosion :Int 'wird auf true gesetzt, solange eine Explosion auf dem Feld ist



so... nun muss nur noch eine Explosion erstellt werden, sobald die gelegte Bombe sich nach 3 Sekunden auflöst Wink
Das erledige ich in der Update() Methode von TBombe.bmx:

BlitzMax: [AUSKLAPPEN]
	Method Update()

If MilliSecs() > BombenTimer+BombenIntervall
BombenCountDown:-1 'Intervall erreicht, CountDown um 1 verringern
BombenTimer=MilliSecs()
If BombenFrame < BOMBENFRAMES-1
BombenFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
End If
End If
If BombenCountDown=0 'CountDown ist abgelaufen *BOOOM*
BombenListe.Remove(Self) 'Bombe aus Liste löschen
Map.Feld[xFeld,yFeld].Inhalt = NIX 'wichtig! Inhalt des Feldes auf NIX zurück setzen
TExplosion.Erstellen(xFeld,yFeld,FeuerKraft) 'Explosion wird erstellt
End If


So, damit man das Ganze nun auch sehen kann... müssen die Update() und Zeichnen() Methoden noch in die Hauptschleife der MainGame.bmx eingefügt werden:

BlitzMax: [AUSKLAPPEN]
Repeat
Cls 'Backbuffer löschen

Map.Zeichnen() 'die Zeichen Methode von Map wird ausgeführt
Spieler1.Zeichnen()
Spieler1.Steuerung()
For Local Bombe:TBombe = EachIn TBombe.BombenListe 'jede erstellte Bombe durchgehen
Bombe.Update()
Bombe.Zeichnen()
Next

For Local Explosion:TExplosion = EachIn TExplosion.ExplosionsListe 'jede erstellte Explosion durchgehen
Explosion.Update()
Explosion.Zeichnen()
Next

WaitTimer (Frames) 'da Frames=120 wird die Hauptschleife 60 mal pro Sekunde durchlaufen
Flip (0) 'Backbuffer wird sichtbar (0)=kein VSync

Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts im Fenster gedrückt wird
End


und schon gibts ne tolle Explosion, nachdem die Bombe verschwindet. Jedoch berücksichtige ich momentan die Feuerkraft noch nicht. Die Explosion soll sich in benachbarte Felder "fortpflanzen" das erledige ich in der Update() Methode von TExplosion, aber erstmal brauche ich ein neues Field und wieder ein paar Konstanten um mir den Umgang mit Zahlen zu ersparen (TExplosion.bmx):

BlitzMax: [AUSKLAPPEN]
	Field FeuerKraft		:Int 'Reichweite der Explosionen
Field ExplosionsRichtung :Int 'Richtung, in die Explosion sich "fortpflanzt"

Const OBEN :Int = 0 'nur wieder um nicht mit Zahlen
Const UNTEN :Int = 1 'hantieren zu müssen
Const LINKS :Int = 2
Const RECHTS :Int = 3
Const ALLE :Int = 5


FeuerKraft ist klar.... das hatte ich schon. Neu ist
Field ExplosionsRichtung :Int hier wird gespeichert, in welche Richtung sich die aktuelle Explosion "fortpflanzt" und die Konstanten geben dabei die Richtung an.
ALLE wird nur beim erstellen einer Bombe benutzt, da sich die erste Explosion in alle Richtungen "fortpflanzen muss"

Dazu passe ich kurz noch die Erstellen Funktion der TExplosion.bmx an, damit ich die ExplosionsRichtung an die NeueExplosion übergeben kann:

BlitzMax: [AUSKLAPPEN]
	Function Erstellen:TExplosion(xFeld:Int,yFeld:Int,FeuerKraft:Int,ExplosionsRichtung:Int=ALLE) 'neue Explosion wird erstellt
Local NeueExplosion:TExplosion = New TExplosion

NeueExplosion.xFeld=xFeld 'Koordinaten der neuen Explosion übergeben
NeueExplosion.yFeld=yFeld

NeueExplosion.FeuerKraft=FeuerKraft 'Feuerkraft
NeueExplosion.ExplosionsRichtung=ExplosionsRichtung 'und Richtung in die sie sich "fortpflanzt"

Map.Feld[xFeld,yFeld].Explosion = True 'wichtig! Inhalt des Feldes auf true setzen
ExplosionsListe.AddLast(NeueExplosion) 'neue Bombe der globalen BombenListe hinzufügen
End Function


Wird die Erstellen Funktion ohne den Parameter ExplosionsRichtung aufgerufen, so ist dieser automatisch auf ALLE gesetzt (ist praktisch, denn ich brauche jetzt den Aufruf der Funktion, in TBombe.bmx nicht bearbeiten)

Jetzt ist alles fürs "Fortpflanzen" soweit vorbereitet. Hier nun die Fortpflanzung in der Update() Methode der TExplosion.bmx:

BlitzMax: [AUSKLAPPEN]
	Method Update()

If MilliSecs() > ExplosionsTimer+ExplosionsIntervall
ExplosionsTimer=MilliSecs()

If Feuerkraft > 0
FeuerKraft:-1 'um 1 reduzieren, damit korrekter Wert an nächste Explosion gegeben wird

If ExplosionsRichtung = OBEN Or ExplosionsRichtung = ALLE
TExplosion.Erstellen(xFeld,yFeld-1,FeuerKraft,OBEN)
End If
If ExplosionsRichtung = UNTEN Or ExplosionsRichtung = ALLE
TExplosion.Erstellen(xFeld,yFeld+1,FeuerKraft,UNTEN)
End If
If ExplosionsRichtung = LINKS Or ExplosionsRichtung = ALLE
TExplosion.Erstellen(xFeld-1,yFeld,FeuerKraft,LINKS)
End If
If ExplosionsRichtung = RECHTS Or ExplosionsRichtung = ALLE
TExplosion.Erstellen(xFeld+1,yFeld,FeuerKraft,RECHTS)
End If

FeuerKraft=0 'auf 0 setzen, damit keine endlos Fortpflanzung entsteht
End If

If ExplosionsFrame < EXPLOFRAMES-1
ExplosionsFrame:+1 'Frame um 1 erhöhen, solange Anzahl an Frames der Grafik noch nicht erreicht
Else 'wenn max Explosionsframes erreicht sind, Explosion löschen
ExplosionsListe.Remove(Self) 'Explosion aus Liste löschen
Map.Feld[xFeld,yFeld].Explosion = False 'Wichtig! Feld auf false setzen
End If
End If

End Method


BlitzMax: [AUSKLAPPEN]
If ExplosionsRichtung = OBEN Or ExplosionsRichtung = ALLE
TExplosion.Erstellen(xFeld,yFeld-1,FeuerKraft,OBEN)
End If


Kurz erklärt:
Wenn die ExplosionsRichtung nach OBEN oder in ALLE Richtungen gehen soll, dann wird eine neue Explosion ein yFeld weiter oben, mit um 1 reduzierter FeuerKraft (da vor der IF Abfrage FeuerKraft:-1) erstellt, welche sich weiterhin nach OBEN fortpflanzen soll.

Und schon hab ich schöne Explosionen in der Größe der FeuerKraft

user posted image

Wie man sieht, gehen diese Explosionen noch durch alle Wände durch... aber das behebe ich mal kurz. Es müssen in der Update() Methode lediglich noch ein paar IF Abfragen:

BlitzMax: [AUSKLAPPEN]
				If ExplosionsRichtung = OBEN Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld,yFeld-1].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld,yFeld-1,FeuerKraft,OBEN) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = UNTEN Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld,yFeld+1].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld,yFeld+1,FeuerKraft,UNTEN) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = LINKS Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld-1,yFeld].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld-1,yFeld,FeuerKraft,LINKS) 'neue Explosion in Richtung
End If
End If
If ExplosionsRichtung = RECHTS Or ExplosionsRichtung = ALLE
If Map.Feld[xFeld+1,yFeld].Inhalt <> HBLOCK 'wenn in ExplosionsRichtung KEIN HardBlock
TExplosion.Erstellen(xFeld+1,yFeld,FeuerKraft,RECHTS) 'neue Explosion in Richtung
End If
End If


ab jetzt werden die Explosionen von HardBlöcken gestoppt

Im nächsten Eintrag sollen die Explosionen dann SoftBlöcke zerstören und andere Bomben zur Explosion bringen... und es gilt wie immer:

Ich würde mich übelst über positive wie auch negative Kritik und Verbesserungsvorschläge freuen, Danke


Download (Code und .EXE) bis hier hin:

https://www.blitzforum.de/upload/file.php?id=9431

Danke fürs lesen Wink

Kollision mit der TileMap

Sonntag, 29. August 2010 von KirkZero
Bevor es nun an die Kollision mit der TileMap geht, erstmal noch eine kleine Verbesserung. Danke für den Hinweis @n-Halbleiter
Zitat:
Deine Steuerungsmethode lässt sich schön beschleunigen (wenn auch nur minimal). Du musst das " And MilliSecs() > BewegungsTimer+BewegungsPause" bei jeder If-Abfrage entfernen und dann die gesamte If-Abfrage für die Tasten in eine Andere mit der Bedingung "MilliSecs() > BewegungsTimer+BewegungsPause" schachteln.

Die Methode würde damit wie folgt aussehen:
<code>
Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause
If KeyDown(KEY_UP) 'wenn Pfeiltaste OBEN gedrückt wird
yPos:-Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_DOWN) 'wenn Pfeiltaste UNTEN gedrückt wird
yPos:+Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_LEFT) 'wenn Pfeiltaste LINKS gedrückt wird
xPos:-Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_RIGHT) 'wenn Pfeiltaste RECHTS gedrückt wird
xPos:+Speed
BewegungsTimer = MilliSecs()
End If
End If
End Method
</code>


Somit sieht die Steuerungs Methode nun so aus:

BlitzMax: [AUSKLAPPEN]
	Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause
If KeyDown(KEY_UP) 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If
End If
End Method



Jetzt gehts an die Kollision mit der TileMap Wink

Ich beziehe mich hier immer auf die Datei TSpieler.bmx! Sollte das einmal nicht der Fall sein, so werde ich es ausdrücklich betonen Exclamation

Ich habe im letzten Worklog mit der Methode MapFeld() berechnet, auf welchem Tile der Map ich mich eigentlich grade befinde. Wenn ich mich nun nach Oben bewegen will, muss ich prüfen, ob das Feld über mir überhaupt begehbar ist:
BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int ' Temporäre Speicher
Local y :Int

Select Richtung
Case OBEN
If Map.Feld[xFeld,yFeld-1].Inhalt = NIX 'wenn oben NIX ist, dann nach oben bewegen
y=-Speed
End If

und das Gleiche natürlich für die anderen Richtungen:
BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int ' Temporäre Speicher
Local y :Int

Select Richtung
Case OBEN
If Map.Feld[xFeld,yFeld-1].Inhalt = NIX 'wenn oben NIX ist, dann nach oben bewegen
y=-Speed
End If
Case UNTEN
If Map.Feld[xFeld,yFeld+1].Inhalt = NIX 'wenn unten NIX ist, dann nach unten bewegen
y=+Speed
End If
Case LINKS
If Map.Feld[xFeld-1,yFeld].Inhalt = NIX 'wenn links NIX ist, dann nach links bewegen
x=-Speed
End If
Case RECHTS
If Map.Feld[xFeld+1,yFeld].Inhalt = NIX 'wenn rechts NIX ist, dann nach rechts bewegen
x=+Speed
End If
End Select
yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x
MapFeld() 'Aktuelles Map.Feld[,] wird berechnet
BewegungsTimer = MilliSecs()

End Method

Sobald man sich nun ein bissel hin und her bewegt, merkt man schnell... das ist wohl so noch nicht ganz richtig. Denn, sobald das Feld aktualisiert wird, wird bei einer weiteren Bewegung in der selben Richtung, gleich das nächste Feld abgefragt. Sollte dies dann jedoch nicht begehbar sein, kann man das aktuelle Feld nicht komplett betreten. (ohjeh... hab ich mal wieder blöd erklärt)
Man hängt also irgendwann fest.

Das Problem löse ich nun wieder mittels Mod:

BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int ' Temporäre Speicher
Local y :Int

Select Richtung
Case OBEN
If Map.Feld[xFeld,yFeld-1].Inhalt = NIX 'wenn oben NIX ist, dann nach oben bewegen
y=-Speed
Else If (yPos Mod TS) <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=-Speed
End If
Case UNTEN
If Map.Feld[xFeld,yFeld+1].Inhalt = NIX 'wenn unten NIX ist, dann nach unten bewegen
y=+Speed
Else If (yPos Mod TS) <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=+Speed
End If
Case LINKS
If Map.Feld[xFeld-1,yFeld].Inhalt = NIX 'wenn links NIX ist, dann nach links bewegen
x=-Speed
Else If (xPos Mod TS) <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=-Speed
End If
Case RECHTS
If Map.Feld[xFeld+1,yFeld].Inhalt = NIX 'wenn rechts NIX ist, dann nach rechts bewegen
x=+Speed
Else If (xPos Mod TS) <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=+Speed
End If
End Select
yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x
MapFeld() 'Aktuelles Map.Feld[,] wird berechnet
BewegungsTimer = MilliSecs()

End Method

Wer damit nun aber ein wenig rumspielt, wird bemerken, es sind immernoch fehlerhafte Bewegungen möglich. Bewegt man sich z.B. zu Beginn nur ein kleines Stück nach rechts und dann nach unten, so ist der Spieler ja zum Teil in einem unbegehbaren Block. Bewegt man sich dann nochmals nach rechts, kann man sogar einen unbegehbaren komplett betreten.

Ein Stück nach rechts bewegen:
user posted image

Dann nach unten
user posted image

und wenn man sich nun nach rechts bewegt, landet man komplett auf einem unbegehbarem Feld
user posted image

Bevor ich mich allerdings diesem Problem widme, erweitere ich erstmal die Methode Bewegung() um 2 Lokale Variablen. Da ich nun auch dort mit Mod arbeite.

So schauts nun aus:

BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int ' Temporäre Speicher
Local y :Int
Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

Select Richtung
Case OBEN
If Map.Feld[xFeld,yFeld-1].Inhalt = NIX 'wenn oben NIX ist, dann nach oben bewegen
y=-Speed
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=-Speed
End If
Case UNTEN
If Map.Feld[xFeld,yFeld+1].Inhalt = NIX 'wenn unten NIX ist, dann nach unten bewegen
y=+Speed
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=+Speed
End If
Case LINKS
If Map.Feld[xFeld-1,yFeld].Inhalt = NIX 'wenn links NIX ist, dann nach links bewegen
x=-Speed
Else If xMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=-Speed
End If
Case RECHTS
If Map.Feld[xFeld+1,yFeld].Inhalt = NIX 'wenn rechts NIX ist, dann nach rechts bewegen
x=+Speed
Else If xMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=+Speed
End If
End Select
yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x
MapFeld() 'Aktuelles Map.Feld[,] wird berechnet
BewegungsTimer = MilliSecs()


Und nun zu dem genannten Problem.
Sollte ich nun diese Position haben:

user posted image

und ich bewege mich nun nach unten, so möchte ich, das der Spieler sich automatisch solange nach links bewegt, bis eine Bewegung nach unten möglich ist.
(Nach links deshalb, weil mein aktuelles Feld sich links vom Sprite befindet.)

Dazu prüfe ich ersteinmal, ob meine aktuelle Feldkoordinate (mit der Tilesize multipliziert) kleiner ist, als meine aktuelle Pixelkoordinate:

BlitzMax: [AUSKLAPPEN]
			Case UNTEN
If Map.Feld[xFeld,yFeld+1].Inhalt = NIX 'wenn unten NIX ist
If xFeld*TS < xPos 'ist (Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um "Speed" Pixel nach links bewegen
Else 'passt es genau, dann um "Speed" Pixel nach unten bewegen
y=+Speed
End If
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=+Speed
End If


und nun das Ganze auch für den fall, das mein aktuelles Feld rechts von mir ist und ich mich dann automatisch nach rechts bewegen muss, bis ich genau in die lücke nach unten passe:

BlitzMax: [AUSKLAPPEN]
			Case UNTEN
If Map.Feld[xFeld,yFeld+1].Inhalt = NIX 'wenn unten NIX ist
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else 'passt es genau, dann um Speed Pixel nach unten bewegen
y=+Speed
End If
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=+Speed
End If


und nun für alle Richtungen:

BlitzMax: [AUSKLAPPEN]
		Select Richtung
Case OBEN
If Map.Feld[xFeld,yFeld-1].Inhalt = NIX 'wenn oben NIX ist, nach oben bewegen
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else 'passt es genau, dann um Speed Pixel nach oben bewegen
y=-Speed
End If
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=-Speed
End If
Case UNTEN
If Map.Feld[xFeld,yFeld+1].Inhalt = NIX 'wenn unten NIX ist
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else
y=+Speed 'passt es genau, dann um Speed Pixel nach unten bewegen
End If
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=+Speed
End If
Case LINKS
If Map.Feld[xFeld-1,yFeld].Inhalt = NIX 'wenn links NIX ist, nach links bewegen
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=-Speed 'passt es genau, dann um Speed Pixel nach links bewegen
End If
Else If xMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=-Speed
End If
Case RECHTS
If Map.Feld[xFeld+1,yFeld].Inhalt = NIX 'wenn rechts NIX ist, nach rechts bewegen
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=+Speed 'passt es genau, dann um Speed Pixel nach rechts bewegen
End If
Else If xMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=+Speed
End If
End Select

Anmerkung von mir: um die Kommentare usw richtig eingerückt zu sehen, am besten den Code Kopieren und in die IDE einfügen... dann sollte es übersichtlicher sein.

EDIT ########## Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation##########
ich habe das ganze grad nochmal schön zusammengefasst... das spart Code und gibt wieder mehr Übersicht! *freu*

BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int 'Temporäre Speicher
Local y :Int
Local fx :Int 'Temporäre Speicher für zu prüfende Felder
Local fy :Int 'ist fy=-1 wird oberes vom aktuellen Feld geprüft

Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

Select Richtung
Case OBEN
fy=-1 'oberes Feld prüfen
Case UNTEN
fy=+1 'unteres Feld prüfen
Case LINKS
fx=-1 'linkes Feld prüfen
Case RECHTS
fx=+1 'rechtes Feld prüfen
End Select

If Map.Feld[xFeld+fx,yFeld+fy].Inhalt = NIX 'wenn auf dem zu prüfenden Feld NIX ist

If fy <> 0 'ist das zu prüfende Feld oben oder unten, dan auf Autobewegung checken
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else
y=fy*Speed 'passt es genau, dann um Speed Pixel nach fy bewegen
End If
End If

If fx <> 0 'ist das zu prüfende Feld oben oder unten, dan auf Autobewegung checken
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=fx*Speed 'passt es genau, dann um Speed Pixel nach fx bewegen
End If
End If

Else If fy <> 0 And yMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
y=fy*Speed
Else If fx <> 0 And xMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
x=fx*Speed
End If

yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x
MapFeld() 'Aktuelles Map.Feld[,] wird berechnet
BewegungsTimer = MilliSecs()

End Method


END EDIT ########## Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation Exclamation##########

und hier die Komplette Spieler.bmx (ALT) ohne Zusammenfassung (siehe EDIT hier drüber):

BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 25

Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Bewegung wieder erlaubt (Zeit)
If KeyDown(KEY_UP) And MilliSecs() 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) And MilliSecs() 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) And MilliSecs() 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If
End If
End Method

Method Bewegung(Richtung:Int)
Local x :Int ' Temporäre Speicher
Local y :Int
Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

Select Richtung
Case OBEN
If Map.Feld[xFeld,yFeld-1].Inhalt = NIX 'wenn oben NIX ist, nach oben bewegen
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else 'passt es genau, dann um Speed Pixel nach oben bewegen
y=-Speed
End If
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=-Speed
End If
Case UNTEN
If Map.Feld[xFeld,yFeld+1].Inhalt = NIX 'wenn unten NIX ist
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else
y=+Speed 'passt es genau, dann um Speed Pixel nach unten bewegen
End If
Else If yMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
y=+Speed
End If
Case LINKS
If Map.Feld[xFeld-1,yFeld].Inhalt = NIX 'wenn links NIX ist, nach links bewegen
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=-Speed 'passt es genau, dann um Speed Pixel nach links bewegen
End If
Else If xMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=-Speed
End If
Case RECHTS
If Map.Feld[xFeld+1,yFeld].Inhalt = NIX 'wenn rechts NIX ist, nach rechts bewegen
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=+Speed 'passt es genau, dann um Speed Pixel nach rechts bewegen
End If
Else If xMod <> 0 'wenn nicht ganz auf dem Feld, dann trotzdem bewegen
x=+Speed
End If
End Select
yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x
MapFeld() 'Aktuelles Map.Feld[,] wird berechnet
BewegungsTimer = MilliSecs()

End Method

Method MapFeld() 'berechnet aktuelles Map.Feld[,]
Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

If yMod < (TS/2) Then yFeld=(yPos/TS) 'wenn modulus < als 16
If yMod > (TS/2) Then yFeld=(yPos/TS)+1 'wenn modulus > als 16
If xMod < (TS/2) Then xFeld=(xPos/TS) 'wenn modulus < als 16
If xMod > (TS/2) Then xFeld=(xPos/TS)+1 'wenn modulus > als 16

End Method

Method Zeichnen()
DrawRect (xFeld*TS,yFeld*TS,TS,TS)
DrawImage (SpielerIMG,xPos,yPos,0)
End Method

Function Erstellen:TSpieler (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type



und hier nochmal die komplette TSpieler.bmx mit der zusammengefassten Bewegungs Methode:

BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 20

Method Steuerung() 'Enthält die Steuerungsabfrage
If MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Bewegung wieder erlaubt (Zeit)
If KeyDown(KEY_UP) And MilliSecs() 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) And MilliSecs() 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) And MilliSecs() 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If
End If
End Method

Method Bewegung(Richtung:Int)
Local x :Int 'Temporäre Speicher
Local y :Int
Local fx :Int 'Temporäre Speicher für zu prüfende Felder
Local fy :Int 'ist fy=-1 wird oberes vom aktuellen Feld geprüft

Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

Select Richtung
Case OBEN
fy=-1 'oberes Feld prüfen
Case UNTEN
fy=+1 'unteres Feld prüfen
Case LINKS
fx=-1 'linkes Feld prüfen
Case RECHTS
fx=+1 'rechtes Feld prüfen
End Select

If Map.Feld[xFeld+fx,yFeld+fy].Inhalt = NIX 'wenn rechts NIX ist

If fy <> 0 'ist das zu prüfende Feld oben oder unten, dan auf Autobewegung checken
If xFeld*TS < xPos 'ist (X Feldkoordinate*TS) kleiner als Pixelkoordinate
x=-Speed 'dann um Speed Pixel nach links bewegen
Else If xFeld*TS > xPos 'ist (X Feldkoordinate*TS) größer als Pixelkoordinate
x=+Speed 'dann um Speed Pixel nach rechts bewegen
Else
y=fy*Speed 'passt es genau, dann um Speed Pixel nach fy bewegen
End If
End If

If fx <> 0 'ist das zu prüfende Feld oben oder unten, dan auf Autobewegung checken
If yFeld*TS < yPos 'ist (Y Feldkoordinate*TS) kleiner als Pixelkoordinate
y=-Speed 'dann um Speed Pixel nach oben bewegen
Else If yFeld*TS > yPos 'ist (Y Feldkoordinate*TS) größer als Pixelkoordinate
y=+Speed 'dann um Speed Pixel nach unten bewegen
Else
x=fx*Speed 'passt es genau, dann um Speed Pixel nach fx bewegen
End If
End If

Else If fy <> 0 And yMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
y=fy*Speed
Else If fx <> 0 And xMod <> 0 'wenn nicht begehbar aber noch nicht ganz auf dem Feld, dann trotzdem bewegen
x=fx*Speed
End If

yPos:+y 'Spieler Koordinaten aktualisieren
xPos:+x
MapFeld() 'Aktuelles Map.Feld[,] wird berechnet
BewegungsTimer = MilliSecs()

End Method

Method MapFeld() 'berechnet aktuelles Map.Feld[,]
Local xMod :Int = (xPos Mod TS) 'Temporärer Speicher für den xPos modulus von TS (32)
Local yMod :Int = (yPos Mod TS)

If yMod < (TS/2) Then yFeld=(yPos/TS) 'wenn modulus < als 16
If yMod > (TS/2) Then yFeld=(yPos/TS)+1 'wenn modulus > als 16
If xMod < (TS/2) Then xFeld=(xPos/TS) 'wenn modulus < als 16
If xMod > (TS/2) Then xFeld=(xPos/TS)+1 'wenn modulus > als 16

End Method

Method Zeichnen()
DrawRect (xFeld*TS,yFeld*TS,TS,TS)
DrawImage (SpielerIMG,xPos,yPos,0)
End Method

Function Erstellen:TSpieler (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type

Um ein wenig auf der Map rumzulaufen, ohne die störenden Softblöcke (um das ganze einfach mal zu testen), kann man diese ganz einfach in der TKarte.bmx entfernen.
Einfach in der Erstellen Funktion die Variable BlockZahl auf 0 setzen:

BlitzMax: [AUSKLAPPEN]
		'#############################################
'# MAP MIT 100 SOFTBLÖCKEN FÜLLEN #
'# DABEI SPIELER-STARTECKEN FREI LASSEN #
'#############################################
Local BlockZahl:Int = 100 '100 Softblöcke sollen gesetzt werden


dort bei

BlitzMax: [AUSKLAPPEN]
Local BlockZahl:Int = 100


einfach 0 eintragen:

BlitzMax: [AUSKLAPPEN]
Local BlockZahl:Int = 0


und die MainGame.bmx starten. Schon kann man die ganze Sache mit mehr Freiraum testen.

Diesmal gibt es wieder den kompletten Code bis hier hin (inklusive .EXE) zum Download:

https://www.blitzforum.de/upload/file.php?id=9412

Das war es dann erstmal zur Pixelweise Bewegung mit TileMap Kollision. Ich bedanke mich erneut fürs Lesen und es gilt wie immer:

Ich würde mich übelst über positive wie auch negative Kritik und Verbesserungsvorschläge freuen, Danke Exclamation

Bein nächsten mal lege ich dann ne Bombe Wink oder evtl. werd ich den Spieler ersteinmal animieren... oder beides... mal sehen *wink*

Pixelweise Bewegung und Map.Feld Berechnungen

Sonntag, 29. August 2010 von KirkZero
Nun will ich mich Pixelweise bewegen...
Dazu erhält TSpieler erstmal ein paar neue Field Einträge und ein paar Konstante:
BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 10

Neu sind nun XPos und yPos. Diese sind nun für die Position in Pixel für den Spieler zuständig. xFeld und yFeld bleiben allerdings weiterhin wichtig, da ich ja immer noch wissen will, auf welchem Feld der Map sich der Spieler befindet.
Speed habe ich auf 2 gesetzt. Speed gibt an, wieviele Pixel sich der Spieler bei jeder Bewegung fortbewegt.
die 4 Konstanten OBEN, UNTEN, LINKS und RECHTS vereinfachen mir nur wieder die Sache, dass ich nicht mit Zahlen arbeiten muss, sondern aussagekräftige Namen habe... wofür die sind, kommt später.
BewegungsPause habe ich runter auf 10 gesetzt, da wir uns ja nun gleich Pixelweise bewegen wollen und 250 wären ziemlich langsame Schritte Wink

die Erstellen Funktion passe ich ebenfalls an:
BlitzMax: [AUSKLAPPEN]
	Function Erstellen:TSpieler (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

NeuerSpieler.xPos und NeuerSpieler.yPos erhalten nun ihre PixelKoordinaten ausgehend vom StartFELD!

Hier die Zeichnen Methode:
BlitzMax: [AUSKLAPPEN]
	Method Zeichnen()
DrawRect (xFeld*TS,yFeld*TS,TS,TS) 'Zeichnet ein Rechteck auch aktuellem FELD
DrawImage (SpielerIMG,xPos,yPos,0) 'Zeichnet den Spieler an seinen PixelKoordinaten
End Method

Ich zeichne dort auch ein Rechteck. Dieses soll mir visuell veranschaulichen, auf welchem Map.Feld sich der Spieler momentan befindet (Ändert sich allerdings noch nicht, kommt später Wink )

Jetzt noch die Steuerungs Methode angepasst:
BlitzMax: [AUSKLAPPEN]

Method Steuerung() 'Enthält die Steuerungsabfrage
If KeyDown(KEY_UP) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste OBEN gedrückt wird
yPos:-Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_DOWN) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste UNTEN gedrückt wird
yPos:+Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_LEFT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste LINKS gedrückt wird
xPos:-Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste RECHTS gedrückt wird
xPos:+Speed
BewegungsTimer = MilliSecs()
End If

End Method

und schon bewege ich mich Pixelweise Wink
hier noch der Komplette Code:
BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 10

Method Steuerung() 'Enthält die Steuerungsabfrage
If KeyDown(KEY_UP) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste OBEN gedrückt wird
yPos:-Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_DOWN) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste UNTEN gedrückt wird
yPos:+Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_LEFT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste LINKS gedrückt wird
xPos:-Speed
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste RECHTS gedrückt wird
xPos:+Speed
BewegungsTimer = MilliSecs()
End If

End Method

Method Zeichnen()
DrawRect (xFeld*TS,yFeld*TS,TS,TS)
DrawImage (SpielerIMG,xPos,yPos,0)
End Method

Function Erstellen:TSpieler (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type


Jetzt zu den 4 Konstanten OBEN, UNTEN, LINKS und RECHTS.
Ich erstelle eine neue Methode namens Bewegung():
BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int 'Temporäre Speicher
Local y :Int

Select Richtung
Case OBEN
x=0
y=-Speed
Case UNTEN
x=0
y=+Speed
Case LINKS
x=-Speed
y=0
Case RECHTS
x=+Speed
y=0
End Select
yPos:+y
xPos:+x
BewegungsTimer = MilliSecs()
End Method

Statt nun immer alle koordinatenänderungen in der Methode Steuerung vorzunehmen, möchte ich das auf die Methode Bewegung abschieben.
Drücke ich nun die Rechte Pfeiltaste, Rufe ich die Methode Bewegung() so auf:
BlitzMax: [AUSKLAPPEN]
Bewegung(RECHTS)

was passiert dann? die temporäre Speichervariable x bekommt den positiven Wert von Speed zugewiesen. Also ist nun x=2
Da y=0 ist wird nun also am ende von "Select Case" die yPos Variable den wert 0 addiert und die xPos Variable den wert Speed addiert. Also bewege ich mich jetzt 2 pixel nach rechts.
Danach muss ich natürlich den BewegungsTimer wieder auf MilliSecs() setzen.

Anmerkung von mir: Man muss z.B. bei:
BlitzMax: [AUSKLAPPEN]
			Case OBEN
x=0 'braucht nicht auf 0 gesetzt zu werden (kann also weggelassen werden)
y=-Speed

x nicht den Wert 0 zuweisen, da dieser so oder so beim Aufruf der Methode erstmal 0 ist. Habe es aber erstmal der Vollständigkeit halber trotzdem gemacht. (gleiches gilt natürlich für y)


nun noch die Steuerungs Methode angepasst:
BlitzMax: [AUSKLAPPEN]
	Method Steuerung() 'Enthält die Steuerungsabfrage
If KeyDown(KEY_UP) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If

End Method

und schon klappt alles so wie vorher auch.

Kompletter Code der TSpieler.bmx:
BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Int 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Int
Field Speed :Int = 2

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 10

Method Steuerung() 'Enthält die Steuerungsabfrage
If KeyDown(KEY_UP) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If

End Method

Method Bewegung(Richtung:Int)
Local x :Int '
Local y :Int

Select Richtung
Case OBEN
x=0 'braucht nicht auf 0 gesetzt zu werden (kann also weggelassen werden)
y=-Speed
Case UNTEN
x=0 'braucht nicht auf 0 gesetzt zu werden (kann also weggelassen werden)
y=+Speed
Case LINKS
x=-Speed
y=0 'braucht nicht auf 0 gesetzt zu werden (kann also weggelassen werden)
Case RECHTS
x=+Speed
y=0 'braucht nicht auf 0 gesetzt zu werden (kann also weggelassen werden)
End Select
yPos:+y
xPos:+x
BewegungsTimer = MilliSecs()
End Method

Method Zeichnen()
DrawRect (xFeld*TS,yFeld*TS,TS,TS)
DrawImage (SpielerIMG,xPos,yPos,0)
End Method

Function Erstellen:TSpieler (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type


Jetzt will ich auch das Feld aktualisieren, wenn der Spieler ein neues Feld betritt. Das erledige ich erstmal ganz simpel, in dem ich eine neue Methode erstelle, welche ich in der Bewegungs Methode aufrufe. Diese schaut vorerst so aus:
BlitzMax: [AUSKLAPPEN]
	Method MapFeld()
yFeld=(yPos/TS)
xFeld=(xPos/TS)
End Method

rufe ich nun diese Methode am Ende der Bewegungs Methode auf, so ändern sich nun auch die Map.Feld Koordinaten.
BlitzMax: [AUSKLAPPEN]
	Method Bewegung(Richtung:Int)
Local x :Int ' Temporäre Speicher
Local y :Int

Select Richtung
Case OBEN
y=-Speed
Case UNTEN
y=+Speed
Case LINKS
x=-Speed
Case RECHTS
x=+Speed
End Select
yPos:+y
xPos:+x
MapFeld() 'Aktuelles Map.Feld wird berechnet
BewegungsTimer = MilliSecs()
End Method

Bewegt man sich nun auf ein anderes Tile der Map, so wird nun auch das Aktuelle Feld geändert. Das passiert zwar nun noch ungenau, da es sich erst ändert, wenn sich der obere linke Punkt (die BezugsKoordinate) des Spielers auf einem neuem Tile befindet, aber das werd ich gleich noch ändern.
Bin da grad am Tüfteln mittels Mod()
So... und das ist dabei rausgekommen und macht auch genau das, was ich will:
BlitzMax: [AUSKLAPPEN]
	Method MapFeld()
Local yMod :Int = (yPos Mod TS) 'Temporärer Speicher für den yPos modulus von TS (32)
Local xMod :Int = (xPos Mod TS)
If yMod < (TS/2) Then yFeld=(yPos/TS) 'wenn modulus < als 16
If yMod > (TS/2) Then yFeld=(yPos/TS)+1 'wenn modulus > als 16
If xMod < (TS/2) Then xFeld=(xPos/TS) 'wenn modulus < als 16
If xMod > (TS/2) Then xFeld=(xPos/TS)+1 'wenn modulus > als 16

End Method

Sobald man sich über die Hälfte des Feldes hinausbewegt ( (16 Mod 32)=16 also alles was über 16 ist, gilt als über die Hälfte) betritt man ein neues Tile (weiterhin in der Zeichen Methode visuell angezeigt, durch DrawRect)

und hier ein kleines CodeBeispiel, was Mod macht bzw welche Werte es ausspuckt im Wertebereich meiner TileGröße(32)
(neues Fenster in der IDE öffnen, einfügen und laufen lassen Wink )
BlitzMax: [AUSKLAPPEN]
Rem
Mod is a mathematical operator that performs the Modulo function.
End Rem


For i=0 To 32 Step 2
Print i+" Mod 32="+(i Mod 32)
Next

Mod ergibt also den nichtteilbaren Rest. 32 lässt sich durch 32 teilen, darum ergibt 32 Mod 32 = 0
34 lässt sich einmal durch 32 teilen, bleiben allerdings 2 über darum ergibt 34 Mod 32 = 2 usw...

Da nun die FeldAktualisierung korrekt funktioniert, kann ich mich im nächsten Worklog um die Kollision mit der TileMap kümmern.

Da ich ein wenig ArchivSpeicherplatz sparen will und ich lediglich die TSpieler.bmx geändert habe, gibt es diesmal nur den Code der TSpieler.bmx
welcher einfach in diesen Download (der letzte komplette Download):

https://www.blitzforum.de/upload/file.php?id=9377

eingefügt werden kann und die alte TSpieler.bmx dann ersetzt.

Der komplette Code von TSpieler.bmx:

BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende FELD
Field yFeld :Int 'auf dem sich der Spieler befindet
Field xPos :Float 'x und y Koordinaten für Pixelweise Bewegung
Field yPos :Float
Field Speed :Int = 2

Const OBEN :Int = 0
Const UNTEN :Int = 1
Const LINKS :Int = 2
Const RECHTS :Int = 3

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 10

Method Steuerung() 'Enthält die Steuerungsabfrage
If KeyDown(KEY_UP) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste OBEN gedrückt wird
Bewegung(OBEN)
ElseIf KeyDown(KEY_DOWN) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste UNTEN gedrückt wird
Bewegung(UNTEN)
ElseIf KeyDown(KEY_LEFT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste LINKS gedrückt wird
Bewegung(LINKS)
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste RECHTS gedrückt wird
Bewegung(RECHTS)
End If

End Method

Method Bewegung(Richtung:Int)
Local x :Int ' Temporäre Speicher
Local y :Int

Select Richtung
Case OBEN
y=-Speed
Case UNTEN
y=+Speed
Case LINKS
x=-Speed
Case RECHTS
x=+Speed
End Select
yPos:+y
xPos:+x
MapFeld() 'Aktuelles Map.Feld wird berechnet
BewegungsTimer = MilliSecs()
End Method

Method MapFeld()
Local yMod :Int = (yPos Mod TS) 'Temporärer Speicher für den yPos modulus von TS (32)
Local xMod :Int = (xPos Mod TS)
If yMod < (TS/2) Then yFeld=(yPos/TS) 'wenn modulus < als 16
If yMod > (TS/2) Then yFeld=(yPos/TS)+1 'wenn modulus > als 16
If xMod < (TS/2) Then xFeld=(xPos/TS) 'wenn modulus < als 16
If xMod > (TS/2) Then xFeld=(xPos/TS)+1 'wenn modulus > als 16

End Method

Method Zeichnen()
DrawRect (xFeld*TS,yFeld*TS,TS,TS)
DrawImage (SpielerIMG,xPos,yPos,0)
End Method

Function Erstellen:TSpieler (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'Start FELD Koordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.xPos = xFeld*TS 'Koordinaten für Pixelweise Bewegung füllen
NeuerSpieler.yPos = yFeld*TS
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type


Danke fürs Lesen und wie immer gilt:
Ich würde mich übelst über positive wie auch negative Kritik und Verbesserungsvorschläge freuen, Danke Exclamation

Einige Änderungen des vorigen Worklogs

Mittwoch, 25. August 2010 von KirkZero
EDIT: Dies ist nun die Editierte Version mit Kommentaren und Erklärungen. Ich werde meinen Original Text so stehen lassen, und jeweils dann da unter ein DICKES EDIT einfügen, mit den Korrekturen.


Erstmal Danke an D2006 für die Hinweise, wie ich noch ein wenig mehr Übersicht für mein Projekt bekomme und Danke auch an Lastmayday für den wirklich ausführlichen Kommentar zu meinem Code und die von ihm gegebenen Tipps!

Daraus resultierend nehme ich folgende Änderungen am Code vor:

Zuersteinmal lege ich für alle Dateien, welche ich in der MainGame.bmx durch Include einbinde, ein neues Verzeichnis namens "INCLUDES" im Hauptordner an. Dort kopiere ich alle .bmx Dateien, welche ich einbinde hinein. (das sind z.Z. TKarte.bmx, TFeld.bmx und TSpieler.bmx)
Somit ist mein Hauptordner nun viel übersichtlicher. Noch schnell die Änderung in der MainGame.bmx gemacht:
BlitzMax: [AUSKLAPPEN]
'------------------------------ includes			----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"

und schon ist das Programm wieder lauffähig.
Ich werde evtl. später, so wie D2006 es selbst auch handhabt, eine extra "includes.bmx", in der alle nötigen Dateien inkludiert werden und das Hauptprogramm inkludiert dann nur eben jene "includes.bmx" einführen. Doch vorerst lasse ich es so, da ich erstmal schauen will, wie viel ich später noch inkludieren werde. Sollten es dann doch mehr Dateien werden, werde ich es ebenso wie D2006 machen.

Nächste Änderung: einen Fenstertitel erstellen. Zur Zeit steht als Fenstertitel nur "BlitzMax Application". Mittels
BlitzMax: [AUSKLAPPEN]
AppTitle = "Bomberman Klon (Community Tutorial)

personalisiere ich das ganze ein bissel. Das ganze setze ich direkt unter SuperStrict in der MainGame.bmx (Laut Lastmayday muss der Befehl VOR Graphics stehen!!!)
BlitzMax: [AUSKLAPPEN]
SuperStrict 'zwingt mich, sauber zu programmieren Wink
AppTitle = "Bomberman Klon (Community Tutorial)"

und schon steht im Fenstertitel "Bomberman Klon (Community Tutorial)" eine kleine aber feine Sache Wink werd ich mir unbedingt angewöhnen, dies immer gleich zu beginn mit einzubauen.

EDIT: ###############
Fehlerkorrektur: (Danke @Thunder)
Zitat:
- AppTitle ist nicht ein Befehl sondern eine Variable die von der IDE farblich hervorgehoben wird.

END EDIT #############

Ebenfalls eine kleine, aber dennoch wichtige Sache, welche ich bisher auch immer vernachlässigt habe: Man kann das Programm nur mittels [ESC] beenden. Ein klick auf das Kreuz oben rechts im Fenster bewirkt z.Z. absolut garnix. Aber da es zur guten Schule gehört, dass man Fenster auch auf diese weise schliessen kann, will ich mich da mal nicht quer stellen. In der Hauptschleife (Repeat... Until) nicht nur die bdingung der Escape taste zum beenden der Schleife setzen, sondern ebenfall, wenn man auf das Kreuz klickt.
BlitzMax: [AUSKLAPPEN]
Until (KeyHit(KEY_ESCAPE) Or AppTerminate()) 'Programm wird beendet, sobald Escape oder Kreuz oben rechts im Fenster gedrückt wird


Nächster Punkt. Zitat von Lastmayday:
Zitat:
in TFeld lädst du bei jedem 'new' die Image noch einmal neu. Du kannst auch in einer Type Global benutzen. Dann wird das Image nur einmal geladen.

Ich glaube, dass wäre mir selber nie aufgefallen... junge, junge... was man nicht alles für Mist baut Embarassed aber das war ja auch einer der Gründe, warum ich das hier mache... möchte meine Fehler ausmerzen. Also änder ich das gleich mal in der TFeld.bmx
Statt:
BlitzMax: [AUSKLAPPEN]
	Field BodenTilesIMG	:TImage = LoadAnimImage("gfx/boden.png",TS,TS,0,BODENFRAMES)
Field BlockTilesIMG :TImage = LoadAnimImage("gfx/block.png",TS,TS,0,BLOCKFRAMES)

sollte es so richtig sein:
BlitzMax: [AUSKLAPPEN]
	Global 	BodenTilesIMG	:TImage = LoadAnimImage("gfx/boden.png",TS,TS,0,BODENFRAMES)
Global BlockTilesIMG :TImage = LoadAnimImage("gfx/block.png",TS,TS,0,BLOCKFRAMES)


Und weiter gehts. Wieder ein Zitat von Lastmayday:
Zitat:
Eine extra Type für jeden einzelnen Tspieler zu machen ist etwas zu viel des guten. Extends ist doch etwas für Fortgeschrittene. Wie wäre es die Tastatur Belegung in Tspieler zu speichern? KEY_LEFT ist auch nur eine variable mit einem wert.
<code>
Field upkey:int, downkey:int, rightkey:int, leftkey:int
</code>
und bei typischen 4 Spielern reicht auch eine kleine array/liste.

Wahrscheinlich hat er Recht. Ich sollte das ganze vielleicht nicht komplizierter machen, als es sein muss. Hab mir dazu noch mal nen Kopf gemacht und es lässt sich auch ohne Vererben super lösen. Mein Grund, es so zu machen, damit jeder Spieler seine eigen Steuerung bekommt, war also der falsche Ansatz.
Damit ändere ich die TSpieler.bmx wieder und es wird nix mehr vereerbt. Die neue Klasse TSpieler1 verschwindet also wieder.
Erstmal das Original, wie es jetzt noch ist:
BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende Feld
Field yFeld :Int 'auf dem sich der Spieler befindet

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 250

Method Steuerung() Abstract
'Von dieser Klasse kann nun keine Instanz mehr gebildet werden
'man muss nun ihre Eigenschaften an eine andere Klasse vererben!!!
'die Klasse, welche erbt, muss allerdings zwingend eine Methode
'namens Steuerung haben!!!!

Method Zeichnen()
DrawImage (SpielerIMG,xFeld*TS,yFeld*TS,0)
End Method

End Type


Type TSpieler1 Extends TSpieler 'Extends vererbt alle Eigenschaften von TSpieler an diese Klasse
'diese Klasse besitzt alle Felder, Funktionen und Methoden von TSpieler
'und MUSS eine Methode namens Steuerung haben

Method Steuerung() 'diese Methode MUSS sein... siehe oben Wink
If KeyDown(KEY_UP) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste OBEN gedrückt wird
yFeld:-1
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_DOWN) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste UNTEN gedrückt wird
yFeld:+1
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_LEFT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste LINKS gedrückt wird
xFeld:-1
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste RECHTS gedrückt wird
xFeld:+1
BewegungsTimer = MilliSecs()
End If

End Method

Function Erstellen:TSpieler1 (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler1 = New TSpieler1 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'StartKoordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function


End Type

Und hier die geänderte Version wieder ohne Extends:
BlitzMax: [AUSKLAPPEN]
Type TSpieler

Field xFeld :Int 'x und y Koordinaten für das entsprechende Feld
Field yFeld :Int 'auf dem sich der Spieler befindet

Field SpielerIMG :TImage 'speichert die Grafik des Spielers
Field BewegungsTimer :Int = MilliSecs()
Field BewegungsPause :Int = 250

Method Steuerung() 'Enthält die Steuerungsabfrage
If KeyDown(KEY_UP) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste OBEN gedrückt wird
yFeld:-1
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_DOWN) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste UNTEN gedrückt wird
yFeld:+1
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_LEFT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste LINKS gedrückt wird
xFeld:-1
BewegungsTimer = MilliSecs()
ElseIf KeyDown(KEY_RIGHT) And MilliSecs() > BewegungsTimer+BewegungsPause 'wenn Pfeiltaste RECHTS gedrückt wird
xFeld:+1
BewegungsTimer = MilliSecs()
End If

End Method

Method Zeichnen()
DrawImage (SpielerIMG,xFeld*TS,yFeld*TS,0)
End Method

Function Erstellen:TSpieler (xFeld:Int,yFeld:Int,ImgPfadName:String,ImgFrames:Int)
Local NeuerSpieler :TSpieler = New TSpieler 'Temporären Spieler erstellen

NeuerSpieler.xFeld = xFeld 'StartKoordinaten übergeben
NeuerSpieler.yFeld = yFeld
NeuerSpieler.SpielerIMG = LoadAnimImage (ImgPfadName,TS,TS,0,ImgFrames)
'AnimImage laden mit höhe und breite von 32 (da TS=32)

Return NeuerSpieler 'Temporär erstellten Spieler zurück geben
End Function

End Type


Jetzt kommt eine Sache, mit der ich noch absolut garnichts zu tun hatte. Zitat von Lastmayday:
Zitat:
das Thema mit Framework und Import ansprechen. Zweck ist das die exe nur mit den Modulen gebaut wird welches das Spiel benötigt:

nun gut, dann werd ich mir das jetzt mal anschauen, und das Ergebnis folgt dann nach diesem Satz Wink
Zitat:
Expecting expression but encountered Framework

*grml* ok... da muss ich wohl noch ein bissel suchen...
Ahhh... ok, das Ganze muss ganz am Anfang der MainGame.bmx stehen... ich hatte es unter SuperStrict eingefügt

EDIT: ###############
Fehlerkorrektur (Danke Thunder und Lastmayday):
Zitat:
- SuperStrict darf am Anfang stehen (ich schreibe Superstrict immer in die erste Zeile und Framework in die zweite), es darf nur keine gewöhnliche Codezeile vor Framework stehen.

Zitat:
Framework muss eben deswegen an erster(oder zweiter -> SuperStrict) stelle im Code stehen um dem Compiler zu sagen das jetzt nur die Module einbinden soll die als nächstes aufgeführt sind

Mein Fehler war, ich hatte nach SuperStrict noch AppTiltle stehen und dann erst Framework und Import
END EDIT ###############

Also, Sinn der Sache ist es, wie Lastmayday schon sagte, nur die Module in die .EXE datei mit einzubinden, welche auch wirklich gebraucht werden. Ich hätte jetzt nicht gedacht, dass das so einen Unterschied macht, aber statt 1,28 MB ist die .EXE mit Framework und Import nur noch schlanke 442 kb groß... GENIAL Wink

und hier der Code:
BlitzMax: [AUSKLAPPEN]
Framework 	brl.max2d
Import brl.Timer
Import brl.Random
Import brl.D3D7Max2D
Import brl.PNGLoader

SuperStrict 'zwingt mich, sauber zu programmieren Wink
AppTitle = "Bomberman Klon (Community Tutorial)"

'------------------------------ includes ----------
Include "INCLUDES/TKarte.bmx"
Include "INCLUDES/TFeld.bmx"
Include "INCLUDES/TSpieler.bmx"


So, hab mal nen bissel in der Hilfe geschnüffelt und mir nochmals Lastmaydays kommentare angeschaut und dabei haben sich mir folgende Erkentnisse aufgetan (Bitte korrigieren, wenn ich mit irgendetwas falsch liege)

Mein jetziges Framework basiert auf dem Modul brl.Max2D, dieses ist für die arbeit mit DirectX?
Will ich mit OpenGL arbeiten, so wäre es brl.GlMax2D.

EDIT: ###############
Fehlerkorrektur (Danke an Thunder und mpmxyz)
Zitat:
- Das Modul Max2D hat eigentlich nur wenig mit den Treibern zu tun - es ist eher ein Universalaufsatz den man bis jetzt mit 3 offiziellen Treibern verwenden kann: D3D7Max2DDriver(), D3D9Max2DDriver(), GLMax2DDriver() (die module heißen wie die Treiberfunktionen nur ohne "Driver()").

Zitat:
brl.Max2D ist ein komplett abstraktes Modul.
Das heißt, dass es weder etwas mit OpenGl noch mit DirectX zu tun hat.
Die Implementierung erledigen die brl.xyzMax2D-Module.
Wenn du einfach nur so Code wegschreibst, musst du keinen Grafiktreiber setzen, da die Module ihn beim Start des Programmes selbst festlegen.

END EDIT ###############

Nun importiere ich alle Module, die ich benötige.
brl.Timer : benötige ich, da ich CreateTimer und WaitTimer im Code benutze
brl.Random : sollte klar sein. Ich arbeite ja mit einigen Zufallsfunktionen (z.B. Rand, SeedRnd, ...)
brl.D3D7Max2D : benötige ich, um später (vor Graphics) den Grafiktreiber zu setzen
brl.PNGLoader : ist dafür da, um Pixmaps im PNG format zu laden ???

Wieder Zitat von Lastmayday:
Zitat:
dadurch müsstest du auch den Render setzen:
<code>
SetGraphicsDriver D3D7Max2DDriver()
</code>

Nun setze ich den Grafiktreiber (vor Graphics in der MainGame.bmx):
BlitzMax: [AUSKLAPPEN]
SetGraphicsDriver D3D7Max2DDriver()

Graphics BREITE , HOEHE , Vollbild 'Bildschirmmodus wird gesetzt


Anmerkung von mir: wenn ich den Grafiktreiber nicht setze, also den Befehl "SetGraphicsDriver D3D7Max2DDriver()" einfach weglasse... startet mein Programm trotzdem ohne zu meckern. Meine Frage nun an jemand, der sich auskennt: ist es unbedingt nötig, diesen Befehl zu benutzen, oder setzt BMAX den treiber automatisch, wenn der Befehl fehlt???

EDIT: ###############
Anmerkungen (Danke an Thunder, Lastmayday und BladeRunner)
Zitat:
- Das Modul D3D7Max2D führt den Befehl SetGraphicsDriver D3D7Max2DDriver() selbst aus, wenn der Treiber korrekt geladen wurde - es sollte also genügen einfach das Modul zu importieren.

Zitat:
Du kannst auch max2d, glmax2d, DX7, DX9 und opengl gesamt importieren. Was dich dann zu SetGraphicsDriver bringt.

Ein beispiel:
<code>
?Win32 'wird nur unter Windows gebaut
SetGraphicsDriver D3D9Max2DDriver()
?MacOS 'wird nur unter Mac gebaut
SetGraphicsDriver GLMax2DDriver()
?Linux 'wird nur unter Linux gebaut
SetGraphicsDriver GLMax2DDriver()
? 'wird wieder normal weiter kompiliert
</code>
so hättest du gleich für jedes OS den richtigen Render. Oder du könntest in den Optionen dem Spieler anbieten es selbst zu Bestimmen, um zum Beispiel Grafik Fehler zu beheben.

Zitat:
Ich würde empfehlen unter Windows den dx9-Treiber zu verwenden, den Thunder schon in seinem Kommentar anführte. Smile
Unter Mac/Linux bist Du zwingend auf OGL angewiesen.


Ich werde dann auf dx9 umschwenken. Einfach folgendes ändern (in der MainGame.bmx)
brl.D3D7Max2D gegen brl.D3D9Max2D austauschen und
SetGraphicsDriver D3D7Max2DDriver() gegen SetGraphicsDriver D3D9Max2DDriver() austauschen.

END EDIT ###############


So, und hier mal wieder der komplette Code bis hier hin (inklusive .EXE)
die Größe des Downloads ist durch Framework und Import geschrumpft und beträgt nun nur noch feine 207 kb statt 671 kb

https://www.blitzforum.de/upload/file.php?id=9377

Wenn das so weiter geht... reicht mein Speicherplatz im Archiv bestimmt bald nicht mehr aus Wink

Danke für die ganzen Tipps. Ich denke, ich habe heute eine Menge gelernt!!! *freu*

Gehe zu Seite 1, 2  Weiter