Kollision und Sprung 2D ?

Übersicht BlitzBasic Beginners-Corner

Neue Antwort erstellen

Raiden93

Betreff: Kollision und Sprung 2D ?

BeitragDi, Aug 02, 2011 15:50
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey,
ich habe mal ein Versuch gemacht und scheitere leider an der Perfekten Kollision zwischen Spieler und Boden könnte mir jemand mal nen Tipp geben wie man es richtig macht?

Code: [AUSKLAPPEN]
AppTitle "Unlimit Build"
Graphics 800,600,32,2
SetBuffer BackBuffer ()
Global FPS_Timer = CreateTimer (60)

;World Variablen
Global Allow_Gravity = True

;Spieler Variablen
Global SpielerAnimX# = 1
Global SpielerAnimY# = 1
Global Sprung# = 0
Global SprungMax# = 10


;Dim Map
Dim map(10,10)
Data 1,1,1,1,1,1,1,1,1,1,1
Data 1,0,0,0,0,0,0,0,0,0,1
Data 1,0,0,0,0,0,0,0,0,0,1
Data 1,0,0,0,0,0,0,0,0,0,1
Data 1,0,0,0,0,0,0,0,0,0,1
Data 1,0,0,0,0,0,0,0,0,0,1
Data 1,0,0,0,0,0,0,0,0,0,1
Data 1,0,0,0,0,0,0,0,0,0,1
Data 1,0,0,0,0,1,0,0,0,0,1
Data 1,0,0,0,1,1,0,0,0,0,1
Data 1,1,1,1,1,1,1,1,1,1,1

Map_Initionisieren ()
Repeat
   Cls
   Map_Zeichnen ()
   Spieler_Zeichnen ()
   Gravity ()
   Spieler_Steuerung ()
   
   Color 255,255,255
   Text 10,20,"Sprung: " +Sprung
   Text 10,0,"SprungMax: " +SprungMax
   Text 10,50,"SpielerAnimX: "+SpielerAnimX#
   Text 10,70,"SpielerAnimY: "+SpielerAnimY#
   Flip (0)
   WaitTimer (FPS_Timer)
Until KeyHit (1)


Function Map_Initionisieren ()
   For Y=0 To 10
      For X=0 To 10
         Read map(X,Y)
      Next
   Next
End Function

Function Map_Zeichnen ()
   For Y=0 To 10
      For X=0 To 10
         If map(X,Y) = 1 ;Wand
            Color 255,0,0
            Rect X*32,Y*32,32,32,1
         End If
      Next
   Next
End Function

Function Spieler_Zeichnen ()
   ;Spieler Zeichnen
   Color 0,0,255
   Rect SpielerAnimX#*32,SpielerAnimY#*32,32,32,1
End Function

Function Gravity ()
   If map(SpielerAnimX#,SpielerAnimY#+0.1) = 0
      SpielerAnimY# = SpielerAnimY# +0.1
   Else
      Sprung = 0
   End If
End Function

Function Spieler_Steuerung ()
   If KeyDown (205) ;Rechts
      If map(SpielerAnimX#+0.5,SpielerAnimY#) = 0
         SpielerAnimX# = SpielerAnimX# +0.1
      End If
   End If
   
   If KeyDown (203) ;Links
      If map(SpielerAnimX#-0.5,SpielerAnimY#) = 0
         SpielerAnimX# = SpielerAnimX# -0.1
      End If
   End If
   
   If KeyDown (57) ;Springen
      If Sprung < SprungMax
         SpielerAnimY = SpielerAnimY /1.05
         Sprung = Sprung +1
      End If
   End If
End Function
 

PhillipK

BeitragDi, Aug 02, 2011 16:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Ersteinmal wäre eine Kurze beschreibung nett Very Happy
Ich persönlich habe kein blitzbasic, aber evtl kann ja einer wie ich doch helfen Wink

Der code sieht mir stark nach nem Mario-ähnlichem spiel aus (Dropdown halt Smile ) und keine Vogelperspektive.
Mit dem hintergrund nehme ich mal an, das es evtl bei der abfrage ein problem geben könnte.

(zumal du nicht genau gesagt hast, was dein problem ist^^)

Code: [AUSKLAPPEN]

Function Gravity ()
   If map(SpielerAnimX#,SpielerAnimY#+0.1) = 0
      SpielerAnimY# = SpielerAnimY# +0.1
   Else
      Sprung = 0
   End If
End Function


Mit Floats für einen Array index wär ich immer vorsichtig. Da könnte dir die rundung ein strich durch die Rechnung machen.

Kommt es denn zu einer kollision? Wird hier zb der Else-content ausgeführt (kannst du ein Print dort lesen? Am besten mit deiner momentanen Y koordinate Smile

Aber ich glaube fast nicht, dass das problem ist ^^ Najo, infos? Razz

Raiden93

BeitragDi, Aug 02, 2011 17:20
Antworten mit Zitat
Benutzer-Profile anzeigen
ups sorry mehr Infos kommen Very Happy. Ehm genau ich möchte die Engine von SuperMario nachbauen und scheitere gerade ein bisschen dran weil ich nicht genau weis wie ich eine Flüssige links und rechts bewegung machen kann ohne nicht auf Floats zu verzichten anderseits wenn ich Floats nehme sind die Kollisionen Rundlich und nicht wie bei Supermario

darth

BeitragDi, Aug 02, 2011 17:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo,

vielleicht solltest du dich erstmal ein bisschen in Tilemaps einlesen? Das wäre mein Vorschlag.

Du hast ne Map in deinem Array da (das du intonierst, oder ionisierst.. eigentlich heisst es ja initialisieren, aber das sind unwichtige Details). Beim Zeichnen der Map solltest du wissen, wie gross deine Tiles sind.

BlitzBasic: [AUSKLAPPEN]
Local TX = 32
Local TY = 32

...

For x = 0 To mapWidth
For y = 0 To mapHeight
If map(x, y) <> 0
Rect x *TX, y *TY, TX, TY ;oder ein Bild mit 32x32
EndIf
Next
Next


Wenn du jetzt wissen möchtest, in welchem Tile ein Punkt ist, dann musst du die Welt-Koordinate durch die Tilegrösse dividieren. Ein Beispiel:

Code: [AUSKLAPPEN]
Deine Map ist 32x32, der Spieler steht bei 50.
1*32 = 32 < 50
2*32 = 64 > 50
-> Der Punkt ist im zweiten Tile!


Also:

BlitzBasic: [AUSKLAPPEN]
tilePosX = playerX /TX
tilePosY = playerY /TY


Für die Kollision kannst dus beliebig kompliziert machen. Die einfachste Methode ist wohl einfach zu schauen, ob der Spieler nach der Bewegung in einem belegten Tile liegt, dann ist die Bewegung verboten. Das hält natürlich immernoch die Gefahr, dass der Spieler bei zu grossen Bewegungen durch Tiles hindurch"tunneln" kann, aber das ist meist vernachlässigbar (wirklich..).

Zur Kollisionsüberprüfung musst du wissen, welche Tiles der Spieler von seiner Position aus belegen kann. Ich mache da meist einfach einen Rahmen von 3x3 um den Spieler rum, ist wahrscheinlich etwas verschwenderisch, aber who cares, ait?

BlitzBasic: [AUSKLAPPEN]
Function checkCollision(posX, posY)
Local tileX = posX /tX
Local tileY = posY /tY

For x = tileX -1 To tileX +1
For y = tileY -1 To tileY +1
If x >= 0 And x < mWidth And y >= 0 And y < mHeight
If mapData(x, y) <> 0
If RectsOverlap(x *tX +400 -posX, y *tY +300 -posY, tX, tY, 401, 301, tX-2, tY -1)
Return True
EndIf
EndIf
EndIf
Next
Next

Return False
End Function

(Kleine Anmerkung: Das ist aus einem Projekt kopiert, die +400/+300 steht da, weil der Spieler immer in der Mitte des Bildschirmes zentriert ist. Darum wird auch -posX/-posY gerechnet, um die Spielerverschiebung einzubinden. Kleines Bisschen Vektorgeometrie, nicht allzu kompliziert. Aber selbst wenn es unklar sein sollte, müsste das Prinzip der Kollision klar werden.)

Dann musst du das mit dem Sprung und der Bewegung noch regeln und so! Dazu musst du wissen wann kollidiert wird, wann er auf dem Boden steht und so weiter. Muss man sich zwischenspeichern.

BlitzBasic: [AUSKLAPPEN]
	vY = vY +GRAVITY

;// Needs somewhat of a k (posY+k*vY until collision)
If Not checkCollision(posX, posY +vY)
posY = posY +vY

onFloor = False
Else
If vY >= 0 ;man kann auch mit dem Kopf kollidieren
onFloor = True
EndIf

vY = 0
EndIf

vX = (KeyDown(32) - KeyDown(30)) *(2 +onFloor *2) ;in der Luft ist man langsamer als am Boden, trotzdem kann man in der Luft steuern..

If Not checkCollision(posX +vX, posY)
posX = posX+vX
EndIf

If onFloor
If KeyDown(57)
vY = -15
EndIf
EndIf


Und das wärs dann eigentlich auch schon. Wie gesagt.. Lies dich etwas in Tilemaps ein und mach dann von da weiter. Wirklich kompliziert ist es eigentlich nicht.

MfG,
Darth
Diese Signatur ist leer.
 

PhillipK

BeitragDi, Aug 02, 2011 19:11
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich persönlich unterstütze die Grundidee, eine art Kollisions-array zu verwenden. Grund ist, das hier später beliebig erweitert werden kann - zb bis zu 32 Flags für verschiedene "effekte" auf den kacheln (schliddern, kollision, under_water etc).

Mit der entsprechenden brechung auf die passende koordinate sollte das kein problem sein.

Das 3x3 Raster ist eine Intressante idee. Das lässt sich als grobe vorprüfung nutzen, um zu erfahren "Was ist eigentlich um den Spieler rum los?".

Alles in allem klingt darth's lösungsansatz super, ich denke das wird dir weiterhelfen Smile

Hierzu musst du also folgendes machen:

Code: [AUSKLAPPEN]
   Rect SpielerAnimX#*32,SpielerAnimY#*32,32,32,1


Sowas rausballern.
Stattdessen deine positionsvariable direkt auf den absoluten koordinaten wert erhöhen Smile
Andersrum beim Tiles abprüfen dann halt: (ich denke, deine tiles sind 32x32 pixel groß?)

Code: [AUSKLAPPEN]
If map(Int(SpielerAnimX#/32),Int(SpielerAnimY#/32)) = 0


Abfragen.

Natürlich solltest du dann auch beim erhöhen einen höheren wert angeben, sonst hast du das gefühl, du spielst eine schildkröte.

Zum Springen habe ich noch eine anmerkung (ich weiß grade nicht, ob du soetwas schon benutzt ):

Ich nutze immer eine art Schalter-variable die meistens 'onGround' heißt.

If Player.onGround = 1 and KeyDown(KEY_SPACE) then
Player.speedY = -5.0
Endif

-> so in der art zb. nun wird pro frame Player.speedY :*0.7 oder sonstwie gerechnet, um den wert gleichmäßig runterzubrechen. Diesen speedY (und auch speedX) addiere ich anschließend, wie in jedem frame, auf die Spielerposition.
Sollte speedY einen gewissen wert überschreiten (zb > -1), kann man überlegen, wie man es weiter schaltet. Hier könntest du zb dein Gravity mit einer abfrage austatten, ob der spieler 'onGround = 0' ist, wenn ja, dann springt er grade (kein gravity). Tritt der fall einer zu niedriegen geschwindigkeit ein, sodass er wieder nach unten gezogen wird, setzt du onGround zb auf -1 (er fällt). -1 deswegen, damit man nicht im sprung einen weiteren sprung aktivieren kann.
Ist der spieler irgendwann mit dem boden kollidiert (unter ihm ist ein Tile!), wird onGround = 1 gesetzt und alles kann von vorne beginnen.

(Uff, das ist mehr geworden, wie geplant. Ich hoffe ich habe es jetzt nicht zu kompliziert erklärt, da es doch eigentlich recht simpel ist :X )

Mit freundlichem Gruß,
Phillipk Smile

Midimaster

BeitragMi, Aug 03, 2011 3:16
Antworten mit Zitat
Benutzer-Profile anzeigen
weia...das sind ja mal wirklich komplizierte antworten! und sie gehen auch noch so schön am thema vorbei...

also raiden93, dein Code sieht alles gar nicht so schlecht aus. In den Beiträgen der Anderen steckt schon ein bißchen Wahrheit....aber der Grund, warum dein Spieler-Rechteck "durchsackt" liegt nur daran, das du außer acht läßt, das ja das Spieler-Rechteck eine Breite und Tiefe besitzt und du aber nur seine linke obere Ecke auf Kollision checkst.

hier siehst du, wie das Rechteck ja noch 32 pixel tiefer hinuntergeht als der punkt SpielerAnimY#*32

BlitzBasic: [AUSKLAPPEN]
Function Spieler_Zeichnen ()
....
Rect SpielerAnimX#*32,SpielerAnimY#*32,32,32,1
^^


die untere kante deines spieler-rechtecks liegt doch eigentlich schon bei SpielerAnimY#*32+32, oder?

In einer genaueren Kollisionsabfrage wirst du deshalb alle 4 Ecken des Spielers auf Kollision checken müssen.

das sind die 4 Ecken:

1. links/oben: Map ( SpielerAnimX# , SpielerAnimY# )
2. rechts/oben: Map ( SpielerAnimX#+1 , SpielerAnimY# )
3. links/unten: Map ( SpielerAnimX# , SpielerAnimY#+1 )
4. rechts/unten: Map ( SpielerAnimX#+1 , SpielerAnimY#+1 )

für deinen senkrechten Fall wäre es jetzt ausreichend die 3. oder 4. Ecke zu checken.

BlitzBasic: [AUSKLAPPEN]
Function Gravity ()
If map(SpielerAnimX#,SpielerAnimY#+0.1+1) = 0
^^



Da es aber wegen der Verwendung der FLOAT-Variablen zu dem Rundungsfehler kommt, wird das Rechteck nun 16 Pixel zu früh anhalten. Dafür gibt es zwei Lösungen:

einfach mit 0.5 als Additionswert arbeiten:
BlitzBasic: [AUSKLAPPEN]
Function Gravity ()
If map(SpielerAnimX#,SpielerAnimY#+0.1+0.5) = 0
^^



oder das Spieler-Rechteck wirklich um den Spielerpunkt herumzeichen:
BlitzBasic: [AUSKLAPPEN]
Function Spieler_Zeichnen ()
....
Rect SpielerAnimX#*32-16,SpielerAnimY#*32-16,32,32,1
^^^ ^^^

So malst du das Spieler-Rechteck immer 16 pixel entfernt vom Spieler-Mittelpunkt

Code: [AUSKLAPPEN]
***************
*             *
*             *
*      *      *
*             *
*             *
***************

ZEVS

BeitragMi, Aug 03, 2011 11:30
Antworten mit Zitat
Benutzer-Profile anzeigen
Für ein bisschen realistischere Physik bräuchtest du außerdem noch eine Variable, die die Geschwindigkeit auf der Y-Achse angibt. Dadurch wird verhindert, dass dein Spieler nach dem Sprung ganz plötzlich schnell nach unten fällt (und das auch noch gleichmäßig). Diese Variable wird einfach immer gesenkt, solange der Spieler sich in der Luft befindet und einmalig auf einen hohen Wert gestellt, wenn er abspringt. Die Berechnung
Zitat:
SpielerAnimY = SpielerAnimY /1.05
ist für das Abspringen nämlich völlig unrealistisch: je niedriger der Spieler ist, desto höher springt er. Da fühlt sich die Physik aber schwer gekränkt. Solche Werte wie Allow_Gravity solltest du mit Const, statt mit Global deklarieren - das verkleinert und beschleunigt die executable.
Wieso du die Variablen SpielerAnimX/Y nennst, ist mir schleierhaft, da wird schließlich nichts animiert.

ZEVS

Raiden93

BeitragMi, Aug 03, 2011 16:43
Antworten mit Zitat
Benutzer-Profile anzeigen
ja das lag daran das ich SpielerX hatte und eine SpielerAnimX und die anim war vorher eine Animation vom Spieler die ich aber raus gebaut habe^^. Ich werde mich mal weiter dran versuchen.

Neue Antwort erstellen


Übersicht BlitzBasic Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group