dariegel
2008-08-03, 12:56:24
Hallo zusammen,
ich programmiere derzeit einen Tetris-Klon (Bild siehe unten). Das Spiel läuft sehr gut soweit, allerdings bereitet mir die dynamische Größenänderung Kopfzerbrechen. Die Blockgröße des Spielfeldes und der Vorschau auf den nächsten Spielstein soll sich dynamisch an die Formulargröße anpassen, kurzum: mein Tetris soll komplett skalierbar sein.
Dazu fange ich per Subclassing die WM_SIZING- und die WM_GETMINMAXINFO-Nachricht ab. Erstere dient zur Festlegung des Formular-Seitverhältnisses, letztere zur ensprechenden Anpassung der Maximalgröße des Formulars. Die statische Variable dblAspectRatio ändert sich dynamisch, sie ist das Verhältnis aus Höhe und Breite von (Spielfeld + Vorschau).
Der Code funktioniert soweit auch gut, allerdings flackerte die Formulargröße zunächst bei Erreichen der Maximalgröße um +/-1. Der Grund dafür war, dass in einer früheren Entwicklungsstufe die Variable dblAspectRatio noch für beide Nachrichten separat errechnet wurde, eine Nachricht hatte also ein aktuelleres Seitenverhältnis zur Verfügung als die andere. Da beide aber fast zeitgleich ausgeführt werden und die Maximalgröße über das aktuelle Seitenverhältnis errechnet wird, musste ich sicherstellen, dass beiden Nachrichten dasselbe dblAspectRatio zur Verfügung steht. Durch Debuggen fand ich heraus, dass WM_GETMINMAXINFO vor WM_SIZING ausgelöst wird, deshalb wird dblAspectRatio in diesem Codeblock (WM_GETMINMAXINFO) aktualisiert und gespeichert, um dann auch für die WM_SIZING-Nachricht zur Verfügung zu stehen. Diesen Code seht Ihr unten.
Entschuldigt, dass ich vor meiner eigentlichen Problembeschreibung so weit aushole, ich möchte Euch nur klarmachen, warum der Code so ist, wie er ist.
Protected Overrides Sub WndProc(ByRef msg As System.Windows.Forms.Message)
Dim dblTitlebarHeight As Double = Me.Height - Me.ClientSize.Height 'Höhe der Titelleiste.
Dim dblBorderWidth As Double = Me.Width - Me.ClientSize.Width 'Rahmendicke links & rechts.
Const WM_SIZING As Long = &H214
Const WM_GETMINMAXINFO As Long = &H24
Const WMSZ_LEFT As Integer = 1
Const WMSZ_RIGHT As Integer = 2
Const WMSZ_TOP As Integer = 3
Const WMSZ_TOPLEFT As Integer = 4
Const WMSZ_TOPRIGHT As Integer = 5
Const WMSZ_BOTTOM As Integer = 6
Const WMSZ_BOTTOMLEFT As Integer = 7
Const WMSZ_BOTTOMRIGHT As Integer = 8
Static dblAspectRatio As Double
'Gilt die Nachricht diesem Formular?
If msg.HWnd.Equals(Me.Handle) Then
Select Case msg.Msg
Case WM_SIZING 'Wird nach WM_GETMINMAXINFO aufgerufen.
'lParam-Feld des Message-Objekts in Rect-Struktur umwandeln.
Dim wndRect As Rect = DirectCast(Marshal.PtrToStructure(msg.LParam, wndRect.GetType), Rect)
'Aktuelle Formularbreite und -höhe speichern.
Dim dblWid As Double = wndRect.right - wndRect.left
Dim dblHgt As Double = wndRect.bottom - wndRect.top
If msg.WParam.ToInt32 = WMSZ_LEFT Or msg.WParam.ToInt32 = WMSZ_RIGHT Then
dblHgt = dblWid * dblAspectRatio
wndRect.left = wndRect.right - CInt(dblWid)
ElseIf msg.WParam.ToInt32 = WMSZ_TOP Or msg.WParam.ToInt32 = WMSZ_BOTTOM Then
dblWid = dblHgt / dblAspectRatio
wndRect.top = wndRect.bottom - CInt(dblHgt)
ElseIf msg.WParam.ToInt32 = WMSZ_TOPLEFT Or msg.WParam.ToInt32 = WMSZ_TOPRIGHT Then
dblHgt = dblWid * dblAspectRatio
wndRect.top = wndRect.bottom - CInt(dblHgt)
ElseIf msg.WParam.ToInt32 = WMSZ_BOTTOMLEFT Or msg.WParam.ToInt32 = WMSZ_BOTTOMRIGHT Then
dblHgt = dblWid * dblAspectRatio
wndRect.bottom = wndRect.top + CInt(dblHgt)
End If
wndRect.bottom = wndRect.top + CInt(dblHgt)
wndRect.right = wndRect.left + CInt(dblWid)
'lParam-Feld des Message-Objekts aktualisieren.
Marshal.StructureToPtr(wndRect, msg.LParam, True)
'0 als Return Code an Windows zurückgeben (lt. MSDN).
msg.Result = New System.IntPtr()
Case WM_GETMINMAXINFO 'Wird vor WM_SIZING aufgerufen.
'lParam-Feld des Message-Objekts in MinMaxInfo-Struktur umwandeln.
Dim mmiStruct As MinMaxInfo = CType(Marshal.PtrToStructure(msg.LParam, mmiStruct.GetType), MinMaxInfo)
'Aktuelles Aspektverhältnis speichern.
dblAspectRatio = (2 * picGameBoard.Left + Toolbar.Height + Statusbar.Height + picGameBoard.Height + dblTitlebarHeight) / (3 * picGameBoard.Left + picGameBoard.Width + picPreview.Width + dblBorderWidth)
'Minimale Fenstergröße festlegen.
mmiStruct.ptMinTrackSize.x = 200
mmiStruct.ptMinTrackSize.y = CInt(mmiStruct.ptMinTrackSize.x * dblAspectRatio)
Debug.WriteLine("Min. Size: " & mmiStruct.ptMinTrackSize.x.ToString & "x" & mmiStruct.ptMinTrackSize.y.ToString)
'Maximale Fenstergröße festlegen.
mmiStruct.ptMaxTrackSize.y = Screen.PrimaryScreen.WorkingArea.Height
mmiStruct.ptMaxTrackSize.x = CInt(mmiStruct.ptMaxTrackSize.y / dblAspectRatio)
Debug.WriteLine("Max. Size: " & mmiStruct.ptMaxTrackSize.x.ToString & "x" & mmiStruct.ptMaxTrackSize.y.ToString)
'lParam-Feld des Message-Objekts aktualisieren.
Marshal.StructureToPtr(mmiStruct, msg.LParam, True)
'0 als Return Code an Windows zurückgeben (lt. MSDN).
msg.Result = New System.IntPtr()
End Select
End If
MyBase.WndProc(msg)
End Sub
Nun zu meinem eigentlichen Problem: starte ich die Anwendung und ziehe eine Seite (egal welche) des Fensters größer, so bleibt das Fenster kurz unterhalb der Maximalgröße stehen. Bei mir beträgt die vertikale Maximalgröße (mmiStruct.ptMaxTrackSize.y) 800 Pixel, das Fenster bleibt bei ca. 790 Pixel stecken. Setzte ich dann mit der Maus ab und ziehe das Fenster erneut, kann ich mich langsam an die 800 Pixel herantasten. Durch Debuggen fand ich heraus, dass mmiStruct.ptMaxTrackSize.x zu spät aktualisiert wird, deshalb auch das Steckenbleiben bereits unterhalb der tatsächlichen Maximalgrenze.
Nun war meine Idee, den Code aus dem Block WM_GETMINMAXINFO mit der Nachricht WM_SIZING ausführen zu lassen. Nur geht das natürlich nicht. Ich kann zwar msg.LParam nacheinander sowohl in Rect als auch in MinMaxInfo "casten", zurückschreiben (Marshal.StructureToPtr(mmiStruct, msg.LParam, True)) kann ich allerdings nur eine Struktur.
Ich würde diese beiden Nachrichtenbehandlungen gern effizienter behandeln, bzw. ihr Zusammenspiel besser steuern können. Denn der Code aus beiden ist ja durch dblAspectRatio voneinander abhängig.
Habt Ihr Vorschläge, wie ich das Problem mit der Maximalgröße lösen kann? Über Vorschläge wäre ich sehr dankbar!
Vielen Dank für's Lesen!
Gruß,
Alex
Hier schön zu sehen: das sich mit der Spielfeld-/Vorschaugröße ändernde Verhältnis.
http://home.arcor.de/alexriegel/tetris.png http://home.arcor.de/alexriegel/tetris2.png
BTW: Warum lässt eigentlich vb@rchiv nur Beiträge <= 5 KB durch? :mad:
ich programmiere derzeit einen Tetris-Klon (Bild siehe unten). Das Spiel läuft sehr gut soweit, allerdings bereitet mir die dynamische Größenänderung Kopfzerbrechen. Die Blockgröße des Spielfeldes und der Vorschau auf den nächsten Spielstein soll sich dynamisch an die Formulargröße anpassen, kurzum: mein Tetris soll komplett skalierbar sein.
Dazu fange ich per Subclassing die WM_SIZING- und die WM_GETMINMAXINFO-Nachricht ab. Erstere dient zur Festlegung des Formular-Seitverhältnisses, letztere zur ensprechenden Anpassung der Maximalgröße des Formulars. Die statische Variable dblAspectRatio ändert sich dynamisch, sie ist das Verhältnis aus Höhe und Breite von (Spielfeld + Vorschau).
Der Code funktioniert soweit auch gut, allerdings flackerte die Formulargröße zunächst bei Erreichen der Maximalgröße um +/-1. Der Grund dafür war, dass in einer früheren Entwicklungsstufe die Variable dblAspectRatio noch für beide Nachrichten separat errechnet wurde, eine Nachricht hatte also ein aktuelleres Seitenverhältnis zur Verfügung als die andere. Da beide aber fast zeitgleich ausgeführt werden und die Maximalgröße über das aktuelle Seitenverhältnis errechnet wird, musste ich sicherstellen, dass beiden Nachrichten dasselbe dblAspectRatio zur Verfügung steht. Durch Debuggen fand ich heraus, dass WM_GETMINMAXINFO vor WM_SIZING ausgelöst wird, deshalb wird dblAspectRatio in diesem Codeblock (WM_GETMINMAXINFO) aktualisiert und gespeichert, um dann auch für die WM_SIZING-Nachricht zur Verfügung zu stehen. Diesen Code seht Ihr unten.
Entschuldigt, dass ich vor meiner eigentlichen Problembeschreibung so weit aushole, ich möchte Euch nur klarmachen, warum der Code so ist, wie er ist.
Protected Overrides Sub WndProc(ByRef msg As System.Windows.Forms.Message)
Dim dblTitlebarHeight As Double = Me.Height - Me.ClientSize.Height 'Höhe der Titelleiste.
Dim dblBorderWidth As Double = Me.Width - Me.ClientSize.Width 'Rahmendicke links & rechts.
Const WM_SIZING As Long = &H214
Const WM_GETMINMAXINFO As Long = &H24
Const WMSZ_LEFT As Integer = 1
Const WMSZ_RIGHT As Integer = 2
Const WMSZ_TOP As Integer = 3
Const WMSZ_TOPLEFT As Integer = 4
Const WMSZ_TOPRIGHT As Integer = 5
Const WMSZ_BOTTOM As Integer = 6
Const WMSZ_BOTTOMLEFT As Integer = 7
Const WMSZ_BOTTOMRIGHT As Integer = 8
Static dblAspectRatio As Double
'Gilt die Nachricht diesem Formular?
If msg.HWnd.Equals(Me.Handle) Then
Select Case msg.Msg
Case WM_SIZING 'Wird nach WM_GETMINMAXINFO aufgerufen.
'lParam-Feld des Message-Objekts in Rect-Struktur umwandeln.
Dim wndRect As Rect = DirectCast(Marshal.PtrToStructure(msg.LParam, wndRect.GetType), Rect)
'Aktuelle Formularbreite und -höhe speichern.
Dim dblWid As Double = wndRect.right - wndRect.left
Dim dblHgt As Double = wndRect.bottom - wndRect.top
If msg.WParam.ToInt32 = WMSZ_LEFT Or msg.WParam.ToInt32 = WMSZ_RIGHT Then
dblHgt = dblWid * dblAspectRatio
wndRect.left = wndRect.right - CInt(dblWid)
ElseIf msg.WParam.ToInt32 = WMSZ_TOP Or msg.WParam.ToInt32 = WMSZ_BOTTOM Then
dblWid = dblHgt / dblAspectRatio
wndRect.top = wndRect.bottom - CInt(dblHgt)
ElseIf msg.WParam.ToInt32 = WMSZ_TOPLEFT Or msg.WParam.ToInt32 = WMSZ_TOPRIGHT Then
dblHgt = dblWid * dblAspectRatio
wndRect.top = wndRect.bottom - CInt(dblHgt)
ElseIf msg.WParam.ToInt32 = WMSZ_BOTTOMLEFT Or msg.WParam.ToInt32 = WMSZ_BOTTOMRIGHT Then
dblHgt = dblWid * dblAspectRatio
wndRect.bottom = wndRect.top + CInt(dblHgt)
End If
wndRect.bottom = wndRect.top + CInt(dblHgt)
wndRect.right = wndRect.left + CInt(dblWid)
'lParam-Feld des Message-Objekts aktualisieren.
Marshal.StructureToPtr(wndRect, msg.LParam, True)
'0 als Return Code an Windows zurückgeben (lt. MSDN).
msg.Result = New System.IntPtr()
Case WM_GETMINMAXINFO 'Wird vor WM_SIZING aufgerufen.
'lParam-Feld des Message-Objekts in MinMaxInfo-Struktur umwandeln.
Dim mmiStruct As MinMaxInfo = CType(Marshal.PtrToStructure(msg.LParam, mmiStruct.GetType), MinMaxInfo)
'Aktuelles Aspektverhältnis speichern.
dblAspectRatio = (2 * picGameBoard.Left + Toolbar.Height + Statusbar.Height + picGameBoard.Height + dblTitlebarHeight) / (3 * picGameBoard.Left + picGameBoard.Width + picPreview.Width + dblBorderWidth)
'Minimale Fenstergröße festlegen.
mmiStruct.ptMinTrackSize.x = 200
mmiStruct.ptMinTrackSize.y = CInt(mmiStruct.ptMinTrackSize.x * dblAspectRatio)
Debug.WriteLine("Min. Size: " & mmiStruct.ptMinTrackSize.x.ToString & "x" & mmiStruct.ptMinTrackSize.y.ToString)
'Maximale Fenstergröße festlegen.
mmiStruct.ptMaxTrackSize.y = Screen.PrimaryScreen.WorkingArea.Height
mmiStruct.ptMaxTrackSize.x = CInt(mmiStruct.ptMaxTrackSize.y / dblAspectRatio)
Debug.WriteLine("Max. Size: " & mmiStruct.ptMaxTrackSize.x.ToString & "x" & mmiStruct.ptMaxTrackSize.y.ToString)
'lParam-Feld des Message-Objekts aktualisieren.
Marshal.StructureToPtr(mmiStruct, msg.LParam, True)
'0 als Return Code an Windows zurückgeben (lt. MSDN).
msg.Result = New System.IntPtr()
End Select
End If
MyBase.WndProc(msg)
End Sub
Nun zu meinem eigentlichen Problem: starte ich die Anwendung und ziehe eine Seite (egal welche) des Fensters größer, so bleibt das Fenster kurz unterhalb der Maximalgröße stehen. Bei mir beträgt die vertikale Maximalgröße (mmiStruct.ptMaxTrackSize.y) 800 Pixel, das Fenster bleibt bei ca. 790 Pixel stecken. Setzte ich dann mit der Maus ab und ziehe das Fenster erneut, kann ich mich langsam an die 800 Pixel herantasten. Durch Debuggen fand ich heraus, dass mmiStruct.ptMaxTrackSize.x zu spät aktualisiert wird, deshalb auch das Steckenbleiben bereits unterhalb der tatsächlichen Maximalgrenze.
Nun war meine Idee, den Code aus dem Block WM_GETMINMAXINFO mit der Nachricht WM_SIZING ausführen zu lassen. Nur geht das natürlich nicht. Ich kann zwar msg.LParam nacheinander sowohl in Rect als auch in MinMaxInfo "casten", zurückschreiben (Marshal.StructureToPtr(mmiStruct, msg.LParam, True)) kann ich allerdings nur eine Struktur.
Ich würde diese beiden Nachrichtenbehandlungen gern effizienter behandeln, bzw. ihr Zusammenspiel besser steuern können. Denn der Code aus beiden ist ja durch dblAspectRatio voneinander abhängig.
Habt Ihr Vorschläge, wie ich das Problem mit der Maximalgröße lösen kann? Über Vorschläge wäre ich sehr dankbar!
Vielen Dank für's Lesen!
Gruß,
Alex
Hier schön zu sehen: das sich mit der Spielfeld-/Vorschaugröße ändernde Verhältnis.
http://home.arcor.de/alexriegel/tetris.png http://home.arcor.de/alexriegel/tetris2.png
BTW: Warum lässt eigentlich vb@rchiv nur Beiträge <= 5 KB durch? :mad: