GDI Logik-Fehler - Komme nicht weiter :(

  • VB.NET

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von Kangaroo.

    GDI Logik-Fehler - Komme nicht weiter :(

    Hallo Community!

    Ich bin im Moment dabei, einen kleinen, billigen Klon des VZ-Spiels "IcyTower" zu erstellen. Sprich: Eine Figur, die von unten nach oben hüpft, von Plattform zu Plattform, und versucht, so hoch wie möglich zu kommen, ohne abzustürzen.

    Bin mittlerweile soweit:

    - Spielfigur
    - Beweglich (links/rechts)
    - "animiert"
    - durch quadratische Funktion (Parabel) ermitteltes Sprungverhalten (realistischer Sprung durch verschiedene Pixel-Schritte)

    - Plattformen
    - als Klasse "Block" erstellt (Eigenschaften der Größe, Position und des Verhaltens einzelner Plattformen)
    - da dynamisches Erstellen der Blöcke eine List Of (Block) zum verwalten der Blöcke

    -> Dynamisches, von ein paar Variablen beeinflusstes Berechnen der Figur und der Blöcke per GDI.

    Problem:

    - Ich muss zwangsläufig eine Kollision erkennen lassen, wann meine Figur in/auf einer Plattform ist, um von dieser erneut einen Sprung nach oben auszuführen, unabhängig davon, ob sich die Figur noch im Sprung nach oben oder im Fall nach unten oder noch ganz in der Sprung-Sequenz befindet
    - Gleichzeitig muss ich die Blöcke einen Pixel weiter wandern lassen und sie ggf. löschen, wenn sie außer Sicht sind.
    - Ich habe beides im OnPaint-Event und habe nur noch ein riesiges Gemetzel an Code auf dem Bildschirm :(.
    - Ein provisorischer SB (Sicherheitsbalken) soll mit der Figur kollidieren und ihn am Fallen hindern, solange sie noch keine normale Plattform mindestens einmal berührt hat.

    Ich habe schon versucht, verschiedene Subs zu erstellen, um mir A) den Code übersichtlicher zu gestalten und B) mit Parametern leichter und schneller arbeiten zu können, aber richtig hingehauen hats net.

    Frage:

    A) Da ich die zu fallenden/springenden Pixel erst darauf prüfen muss, ob sie eine Kollision ergeben, bewegt sich meine Figur gar nicht mehr und eine For-Schleife wird nicht einmal mehr angesprochen...
    B) Meine Berechnung, um die Figur im Spielfeld zu halten scheint trotz eiskalter Logik für die Katz zu sein :(

    Quellcode:


    (komischerweise tendiert Chrome dazu, dass mein VB-Code nie richtig angezeigt wird, deswegen hab ich den Quellcode auch noch mal in dem Dateianhang)

    VB.NET-Quellcode

    1. Imports System.DrawingImports System.Drawing.Drawing2D
    2. Public Class Form1
    3. #Region "Benjii" 'Eigenschaften Benji's Dim B_X As Integer = 200 Dim B_Y As Integer = 200 Dim B_W As Integer = My.Resources.Benji_Normal.Size.Width Dim B_H As Integer = My.Resources.Benji_Normal.Size.Height 'Bewegung Benji's Public Enum BenjisDirection normal = 1 up = 2 down = 3 left = 4 right = 5 End Enum Dim BenjisFace As String = BenjisDirection.normal 'Normale Steigung Benji's Dim BenjiJump As String() = {16, 8, 11, 7, 6, 6, 4, 5, 3, 4, 3, 2, 3, 2, 2, 1, 2, 1, 1, 1, 0, 1, 0, 0, 0, 0, -1, 0, -1, -1, -1, -2, -1, -2, -2, -3, -2, -3, -4, -3, -5, -4, -6, -6, -7, -11, -8, -16} Dim BenjiJumpC As Integer = 0 Dim BenjiJumpMulti As Double = 1 'Fällt? Dim BenjiFallin As Boolean = False#End Region#Region "Blöcke" 'Block-Liste Dim BlockList As List(Of Block) = New List(Of Block) Dim BlockSpeed As Double = 1#End Region#Region "SicherheitsBalken" Dim SB_Exists As Boolean = True Dim SB_PX As Integer = 0 Dim SB_PY As Integer = 400 Dim SB_SX As Integer = Me.Width Dim SB_SY As Integer = 10
    4. #End Region
    5. Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs) Dim bg As Graphics = e.Graphics 'Berechne und verwalte Benji's Verhalten If Not BenjiFallin = True Then If Not BenjiJumpC > 46 Then BenjiJumpC += 1 Else BenjiFallin = True End If End If CalculateCollusion(BenjiJump(BenjiJumpC)) Select Case BenjisFace Case BenjisDirection.normal bg.DrawImage(My.Resources.Benji_Normal, New Point(B_X, B_Y)) Case BenjisDirection.up bg.DrawImage(My.Resources.Benji_Up, New Point(B_X, B_Y)) Case BenjisDirection.down bg.DrawImage(My.Resources.Benji_Down, New Point(B_X, B_Y)) Case BenjisDirection.right bg.DrawImage(My.Resources.Benji_Right, New Point(B_X, B_Y)) Case BenjisDirection.left bg.DrawImage(My.Resources.Benji_Left, New Point(B_X, B_Y)) End Select If SB_Exists = True Then bg.DrawRectangle(Pens.Black, New Rectangle(New Point(SB_PX, SB_PY), New Size(SB_SX, SB_SY))) 'Kontrolliere Verhalten der Blöcke If Not BlockList.Count < 1 Then For Each Block As Block In BlockList With Block If Not .PosY + .SizeX > Me.Height Then .PosY += BlockSpeed Dim BlockRect As New Rectangle(New Point(.PosX, .PosY), New Size(.SizeX, .SizeY)) If .Typ = Little_Benji.Block.BTyp.normal Then bg.DrawRectangle(Pens.Black, BlockRect) Else bg.DrawRectangle(Pens.Gray, BlockRect) End If Else BlockList.RemoveAt(BlockList.IndexOf(Block)) Exit For End If End With Next End If End Sub 'OnPaint
    6. Private Sub CalculateCollusion(ByVal Distance As Double) For i = 0 To Distance * BenjiJumpMulti If Not Distance < 0 Then B_Y += -(Distance) Else B_Y += Distance End If If SB_Exists = True Then If B_Y + B_H >= SB_PY Then BenjiJumpC = 0 BenjiFallin = False Exit Sub End If End If Label1.Text = B_X & "|" & B_Y If Not BlockList.Count < 1 Then For Each Block As Block In BlockList With Block If B_Y + B_H >= .PosY And B_Y <= .SizeY + .PosY Then If B_X + B_W >= .PosX And B_X <= .SizeX + .PosX Then BenjiJumpC = 0 BenjiFallin = False SB_Exists = False Exit Sub End If End If End With Next End If Next End Sub
    7. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Me.Invalidate() End Sub
    8. Private Sub Form1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown Select Case e.KeyCode Case Keys.Right For i = 0 To 10 If Not B_X + B_W + 1 > Me.Width Then B_X += 1 Else Exit For End If Next BenjisFace = BenjisDirection.right Case Keys.Left For i = 0 To 10 If B_X - i > 0 Then B_X -= 1 Else Exit For End If Next BenjisFace = BenjisDirection.left
    9. 'Nun kommen die Hoch- und die Runtertasten, um den Multiplikator zu testen... Case Keys.Up BenjiJumpMulti += 0.1 Case Keys.Down If Not BenjiJumpMulti - 0.1 < 1 Then BenjiJumpMulti -= 0.1 End If End Select End Sub
    10. Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick Dim R As New System.Random Dim NewBlock As New Block(50, 10, R.Next(0, 600), 0, 1) BlockList.Add(NewBlock) End Sub
    11. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click B_X = Me.Width / 2 B_Y = Me.Height / 2 End Sub
    12. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click CalculateCollusion(BenjiJump(BenjiJumpC)) End SubEnd Class



    Hoffe, dass ihr mir weiterhelfen könnt :(

    Fragen wie "Warum bist Du denn auf diesen Schwachsinn gekommen?!" kann ich getrost mit "Wahnsinn durch Schlafmangel und Verzweiflung" im Voraus beantworten :(

    MfG,
    X-Zat / Moritz
    Dateien
    • Little Benji.txt

      (6,4 kB, 135 mal heruntergeladen, zuletzt: )

    X-Zat schrieb:

    Fragen wie "Warum bist Du denn auf diesen Schwachsinn gekommen?!" kann ich getrost mit "Wahnsinn durch Schlafmangel und Verzweiflung" im Voraus beantworten
    *grins* kommt mir irgendwoher bekannt vor ;)

    Ich hab mal eben beim 1. Kaffee in Deinen Code reingeschaut: noch sehe ich keinen grossen Verhau, aber wenn Du jetzt schon nicht mehr durchsteigst, solltest Du mal dringend mal etwas Ordnung schaffen.

    Was mir so dafür einfällt ( ohne Gewähr, ich werde erst ab dem 3. Kaffee wach) :

    Du hast Deine gesamte Logik im OnPaint Event, Deine Ablaufsteuerung im Timer Tick besteht nur im schlappen Aufruf der Paint Methode per Invalidate(). Das muss da raus !! OnPaint sollte nur den augenblicklichen Status malen, sonst nix.

    Begreif den Unterschied zwischen funktionsorientierter und objektorientierter Programmierung: Deine Plattformen sind schon Klassen, die werden ja wohl selber berechnen können was ihr nächster Status ist , z.B.über eine Methode RecalcStatus. So etwas nennt man Kapselung, den Rest Deines Programmes hat es garnicht zu interessieren wie sich Deine Blöcke bewegen / verhalten.

    Das Gleiche gillt für Benji: der sollte auch per Klasse definiert werden, wobei die Berechnung des neuen Status schon etwas komplizierter ist.

    Kollisionsberechnung: brauchst Du für Benji und später wohl auch für die Plattformen, z.B. mit den Wänden. Dafür haben Objekte normalerweise ein Rectangle als Grösse, die Kollision (Überlappung) 2er Rectangles erfolgt über die Methode IntersectsWith

    Überleg Dir die Properties Deiner Plattform-Klasse ( z.B. Grösse, Position, Kraftvektor (???), Uhrzeit des letzten Status) und die Methoden ( z.B. RecalcStatus() ). Eventuell gibt es sogar eine gemeinsame Basisklasse für Benji und Plattformen, kA.

    Deine Ablaufsteuerung könnte dann im einfachsten Fall im Aufruf von RecalcStatus() aller Objekte und 1 Invalidate() zum Anzeigen bestehen.

    Mach nicht zu viel auf einmal: teste z.B. erst einmal nur Deine (beweglichen) Plattformen, wenn das alles wuppt dann nimm den lütten hüpfenden Beni hinzu.


    hmm, das war's erst einmal ...

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Kangaroo“ ()

    Vielen Dank!

    Ich werde meinen Code erstmal komplett neustricken und zwar so:

    - Benji als eigene Klasse
    - Plattform-Klasse neu strukturieren
    - Im OnPaint-Event nur Mal-Befehle
    - Zusätzliche Subs erstellen für Bewegungen Benji's und der Plattformen und zusätzlich die Kollision der Objekte

    So... Das sollte erstmal helfen - Bin jetzt nach (zum Glück nur) sechs Stunden Schule recht ausgeschlafen ;)

    MfG,
    X-Zat / Moritz

    //Edit:

    Das ist übrigens mein erstes Programm, in dem ich A) eigene Klassen (Galileo-Handbuch ist nicht ganz für die Katz), B) List(Of...) und C) GDI benutze... Habe das vorher weder gebraucht noch verstanden ;)

    X-Zat schrieb:

    Zusätzliche Subs erstellen für Bewegungen Benji's und der Plattformen und zusätzlich die Kollision der Objekte

    Nur zur Sicherheit: die Bewegungen gehören in die entsprechenden Klassen, nicht in die Form. Wenn Benji und die Plattformen etwas gemeinsam haben, kannst Du sie auch auf einer gemeinsamen Basisklasse definieren.

    X-Zat schrieb:


    Das ist übrigens mein erstes Programm, in dem ich A) eigene Klassen (Galileo-Handbuch ist nicht ganz für die Katz), B) List(Of...) und C) GDI benutze... Habe das vorher weder gebraucht noch verstanden ;)

    Wenn ich meine ersten Versuche anschaue dann war der Code noch viel umständlicher als Deiner

    Viel Glück ;)
    Habe nun fast alles fertig umgebaut, je eine Klasse für Benji, die Blöcke und den Sicherheitsbalken erstellt.
    Nach ein paar kleinen Schwierigkeiten habe ich jetzt wieder Probleme, wegen denen ich kein bisschen weiterkomme...

    Problem 1:

    Das Array an Sprungdistanzen setzt sich immer wieder auf die vorletzte Position zurück!

    Spoiler anzeigen
    Dim Benji_J_Array As String() = {16, 8, 11, 7, 6, 6, 4, 5, 3, 4, 3, 2, 3, 2, 2, 1, 2, 1, 1, 1, 0, 1, 0, 0, 0, 0, -1, 0, -1, -1, -1, -2, -1, -2, -2, -3, -2, -3, -4, -3, -5, -4, -6, -6, -7, -11, -8, -16}
    Dim Benji_J_ArrayCounter As Integer = 0 'Zählt den Index
    ...
    If Not Benji_J_Falling Then
    If Not Benji_J_ArrayCounter = Benji_J_Array.GetUpperBound(0) Then
    Benji_J_ArrayCounter += 1
    Else
    Benji_J_ArrayCounter = 38
    Benji_J_Falling = True
    End If
    End If


    Problem 2:

    Der Sicherheitsbalken wird nicht gezeichnet!

    Spoiler anzeigen

    Public SaveBlock As New SaveBlock

    ...

    'Zeichne SaveBlock, falls er existiert
    If SaveBlock.Exists Then
    Dim SaveBlockRect As New Rectangle(New Point(SaveBlock.PosX, SaveBlock.PosY), New Size(SaveBlock.SizeX, SaveBlock.SizeY))
    bg.DrawRectangle(Pens.Black, SaveBlockRect)
    End If

    '-----In der Klasse SaveBlock-----
    Dim m_Exists As Boolean = True
    ...

    Public Sub New()
    Exists = m_Exists
    End Sub

    '-----In der Klasse Benji, Benji_CalcJump()

    If Form1.SaveBlock.Exists Then
    Dim SaveBlocksColl As New Rectangle(New Point(Form1.SaveBlock.PosX, Form1.SaveBlock.PosY), New Size(Form1.SaveBlock.SizeX, Form1.SaveBlock.SizeY))
    If BenjisColl.IntersectsWith(SaveBlocksColl) Then
    Collision = True
    Form1.SaveBlock.Exists = False
    End If
    End If


    Daran hakt's im Moment...
    Kann deswegen die Bewegungen und Kollisionserkennung nicht sonderlich gut testen, da einfach kein Objekt zum Kollidieren da ist - Der SaveBlock verabschiedet sich ja leider...

    Wäre super, wenn ihr mir da helfen könntet!

    MfG,
    X-Zat / Moritz
    Aus Deinen Code-Auszügen geht nicht hervor , woher die Probleme stammen.

    Nimm Dir den Debugger und gehe entweder im Einzelschritt oder mit BreakPoints auf die Codestellen. Du kannst dabei die Variablen per ?Variable im Direktfenster anzeigen lassen.

    Was mir noch aufgefallen ist:

    Es scheinen noch einige Variablen ausserhalb der Klassen zu existieren, die dort eigentlich reingehören. In die Klasse Benji gehören z.B. isFalling, isDead, isJumping ,die SprungIndices und der actSprungindex. Genauso würde ich die Kollisionsprüfung bei der Neukalkulation der Position in die Benji Klasse verlagern.

    Auch verlagern würde ich das Zeichen: jede Klasse kann sich doch selber zeichnen wenn Du ihnen nur das Graphics-Object mitgibst.