DX: Full Screen GUI Development 1

    技术2022-05-11  91

    Full Screen GUI Development

    Pt 1 ?The Basics

    By Jim (Machaira) Perry

     

     

    In this article we抣l look at developing a GUI for full-screen DirectX games. The code accompanying this article may be used as a base for developing your own GUI class. It will necessarily be simplistic, but may give you an idea of how to go about writing your own GUI code.

     

    This article assumes a working knowledge of DirectX 7 and VB classes.

     

    Download this tutorial (in Word format) and the sample projects in a zip file (336 KB)

     

    When you think of a GUI you think in terms of windows, so the base class will handle most normal window properties and draw the bitmap used to represent the window. A bitmap for a simple window could look like the following.

     

     

    This window has no Control Box or minimize, maximize, and close buttons. It抯 about as simple as you can get. See the window.bmp file.

     

    Several example of more complex GUI抯 are below:

    Figure 1 - Quake III Arena GUI

    Figure 2 - Unreal Tournament GUI

     

    Our GUI class won抰 quite be up to the task of handling something like this, but it抣l eventually be pretty useful.

     

    Below is a first try at a base window class (see the WindowSample1 project):

     

    ClsWindow Class

     

    Option Explicit

     

    Private objBitmap As DirectDrawSurface7

     

    Private iX As Integer

    Private iY As Integer

    Private iWidth As Integer

    Private iHeight As Integer

     

    'Center the window against the parent window

    'For a base window the parent is the screen

    Private bCenterX As Boolean

    Private bCenterY As Boolean

     

    'Used for centering purposes

    Private iParentWidth As Integer

    Private iParentHeight As Integer

    Private iParentX As Integer

    Private iParentY As Integer

     

    Public Property Let X(ByVal iData As Integer)

        iX = iData

    End Property

     

    Public Property Get X() As Integer

        X = iX

    End Property

     

    Public Property Let Y(ByVal iData As Integer)

        iY = iData

    End Property

     

    Public Property Get Y() As Integer

        Y = iY

    End Property

     

    'Only Property Get for Width and Height properties

    'since they are set by the dimensions of the bitmap

    Public Property Get Width() As Integer

        Width = iWidth

    End Property

     

    Public Property Get Height() As Integer

        Height = iHeight

    End Property

     

    Public Property Let CenterX(ByVal bData As Boolean)

        bCenterX = bData

        iX = ((iParentWidth / 2) + iParentX) - (iWidth / 2)

    End Property

     

    Public Property Get CenterX() As Boolean

        CenterX = bCenterX

    End Property

     

    Public Property Let CenterY(ByVal bData As Boolean)

        bCenterY = bData

        iY = ((iParentHeight / 2) + iParentY) - (iHeight / 2)

    End Property

     

    Public Property Get CenterY() As Boolean

        CenterY = bCenterY

    End Property

     

    Public Property Let ParentWidth(ByVal iData As Integer)

        iParentWidth = iData

    End Property

     

    Public Property Get ParentWidth() As Integer

        ParentWidth = iParentWidth

    End Property

     

    Public Property Let ParentHeight(ByVal iData As Integer)

        iParentHeight = iData

    End Property

     

    Public Property Get ParentHeight() As Integer

        ParentHeight = iParentHeight

    End Property

     

    Public Property Let ParentX(ByVal iData As Integer)

        iParentX = iData

    End Property

     

    Public Property Get ParentX() As Integer

        ParentX = iParentX

    End Property

     

    Public Property Let ParentY(ByVal iData As Integer)

        iParentY = iData

    End Property

     

    Public Property Get ParentY() As Integer

        ParentY = iParentY

    End Property

     

    Public Property Let ObjectSurface(ByVal objSurface As DirectDrawSurface7)

       

        Dim ddsd As DDSURFACEDESC2

       

        Set objBitmap = objSurface

       

        objBitmap.GetSurfaceDesc ddsd

       

        iHeight = ddsd.lHeight

        iWidth = ddsd.lWidth

       

    End Property

     

    Public Function DrawObject(objSurface As DirectDrawSurface7)

       

        Dim rectObject As RECT

        Dim rectBitmap As RECT

       

        On Error GoTo DrawObjectErr

       

        rectBitmap.Left = iX

        rectBitmap.Right = iX + iWidth

        rectBitmap.Top = iY

        rectBitmap.Bottom = iY + iHeight

                

        objSurface.Blt rectBitmap, objBitmap, rectObject, DDBLT_WAIT

     

        Exit Function

       

    DrawObjectErr:

        Exit Function

    End Function

     

    Take a look at the InitDD function in the modDirectDraw module. The following code creates the window object:

     

        Window.ObjectSurface = objDD.CreateSurfaceFromFile(App.Path & "/window.bmp", ddsdSurf2)

        Window.ParentX = 0

        Window.ParentY = 0

        Window.ParentHeight = 600

        Window.ParentWidth = 800

        Window.CenterX = True

        Window.CenterY = True

     

    This will simply draw a bitmap of a window centered on the screen. It doesn抰 handle input and contains no controls. Although this is a good start it doesn抰 really offer us much. Some other things are necessary. The ability to put controls on the window is one of them. Since controls are only windows themselves with some additional properties, we can use the clsWindow class to represent them with some modifications.

     

    We want the base window to know about the controls it contains. A collection is a simple way to allow this. Add the following to the Declarations section of the clsWindow class (see the WindowSample2 project):

     

    Private colChildren As New Collection

     

    This collection will hold references to the controls that the window that contains them. We need a function to allow us to add controls to the class. The following function will handle that:

     

    Public Sub AddChild(clsChild As clsWindow)

        colChildren.Add clsChild

    End Sub

     

    Since there are many types of controls we抣l need a way to tell what kind of control we抮e placing on the window. Add the following to the Declarations section as well:

     

    Private iObjectType As eObjectType

    Private iObjectState As eObjectState

     

    and add the following to the modMain module:

     

    Public Enum eObjectState

        iEnabled

        iDisabled

        iPressed

        iChecked

        iUnchecked

        iChecked_iDisabled

    End Enum

     

    Public Enum eObjectType

        Btn

        ChkBox

    End Enum

     

    These enums will represent the type of control and the state of that control. These enums will grow as new control types and states are added to them. For now a button and a checkbox are enough to demonstrate their usage.

     

    Now we need objects of the new window types. Add the following to the modDirectDraw module:

     

    Public OKButton As New clsWindow

    Public Check As New clsWindow

     

    These will represent the new controls. This will require us to update the InitDD function as well. Add the following to the function:

     

        Dim ddsdSurf3 As DDSURFACEDESC2

        Dim ddsdSurf4 As DDSURFACEDESC2

     

        OKButton.ObjectSurface = objDD.CreateSurfaceFromFile(App.Path & "/ok.bmp", ddsdSurf3)

        OKButton.ObjectType = Btn

        OKButton.ObjectState = iEnabled

        OKButton.ParentHeight = Window.Height

        OKButton.ParentWidth = Window.Width

        OKButton.ParentX = Window.X

        OKButton.ParentY = Window.Y

        OKButton.CenterX = True

        OKButton.CenterY = True

       

        Window.AddChild OKButton

           

        Check.ObjectSurface = objDD.CreateSurfaceFromFile(App.Path & "/check.bmp", ddsdSurf4)

        Check.ObjectType = ChkBox

        Check.ObjectState = iUnchecked

        Check.ParentHeight = Window.Height

        Check.ParentWidth = Window.Width

        Check.ParentX = Window.X

        Check.ParentY = Window.Y

        Check.X = 200

        Check.Y = 200

       

        Window.AddChild Check

    Take a look at the ok.bmp and check.bmp files. These are the bitmaps that are used for the controls. Notice that there are several versions of the control in the file. They represent the various states that the control can have. For the button bitmap they are iEnabled, iPressed, and iDisabled. The checkbox has iUnchecked, iChecked, iDisabled, and iChecked_iDisabled. The iEnabled enum could have been used instead of the iUnchecked, but I felt it wouldn抰 have described the state of the control as well. Notice also that we抮e centering the button inside the window, but setting the X and Y coordinates of the checkbox. We抳e also added the text 揙K?to the bitmap for the button, but this could have been left out. This will be done later when we add text support to the class. This will also allow us to add text to the checkbox and the window.

     

    Change the Property Let ObjectSurface to the following:

     

        Dim ddsd As DDSURFACEDESC2

       

        Set objBitmap = objSurface

       

        objBitmap.GetSurfaceDesc ddsd

       

        iWidth = ddsd.lWidth

        Select Case iObjectType

            Case Btn

                iHeight = ddsd.lHeight / 3

            Case ChkBox

                iHeight = ddsd.lHeight / 4

            Case BaseWindow

                iHeight = ddsd.lHeight

        End Select

     

    This will handle the new types of controls. This could also have been done by adding Property Let statements to the class and setting the Width and Height properties in the InitDD function.

     

    Now we need to handle clicking on the controls. Add the following to the clsWindow class:

     

    Public Sub MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)

       

        Dim iLp As Integer

       

        If X >= iX And X <= iX + iWidth And Y >= iY And Y <= iY + iHeight Then

           

            For iLp = 1 To colChildren.Count

                colChildren(iLp).MouseDown Button, Shift, X, Y

            Next iLp

     

            If Not (iObjectState = iDisabled) Then

                Select Case iObjectType

                    Case ChkBox

                        If iObjectState = iChecked Then

                            iObjectState = iUnchecked

                        Else

                            iObjectState = iChecked

                        End If

                    Case Btn

                        iObjectState = iPressed

                End Select

            End If

        End If

       

    End Sub

     

    Public Sub MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)

       

        Dim iLp As Integer

       

        If Not (iObjectState = iDisabled) Then

            If iObjectState = iPressed And iObjectType = Btn Then iObjectState = iEnabled

        End If

       

        For iLp = 1 To colChildren.Count

            colChildren(iLp).MouseUp Button, Shift, X, Y

        Next iLp

       

    End Sub

     

    and add the following to the frmMain form抯 code:

     

    Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

        Window.MouseDown Button, Shift, X, Y

    End Sub

     

    Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

        Window.MouseUp Button, Shift, X, Y

    End Sub

     

    Notice that we only have to call the mouse events for the base window. The class handles calling the events for the controls it contains.

     

    So what are we missing? Oh yes, we need to know how to draw the controls. Replace the DrawObject function with the following:

     

    Public Function DrawObject(objSurface As DirectDrawSurface7)

       

        Dim clsWindow As clsWindow

        Dim iLp As Integer

        Dim ddsd As DDSURFACEDESC2

        Dim rectBitmap As RECT

        Dim rectObject As RECT

       

        On Error GoTo DrawObjectErr

       

        rectBitmap.Left = iX

        rectBitmap.Right = iX + iWidth

        rectBitmap.Top = iY

        rectBitmap.Bottom = iY + iHeight

       

        Select Case iObjectType

            Case Btn

               

                rectObject.Left = 0

                rectObject.Right = iWidth

                rectObject.Top = 0

                rectObject.Bottom = iHeight

                

                Select Case iObjectState

                    Case iDisabled

                        rectObject.Top = iHeight * 2

                        rectObject.Bottom = iHeight * 3

                    Case iPressed

                        rectObject.Top = iHeight

                        rectObject.Bottom = iHeight * 2

                End Select

               

            Case ChkBox

               

                rectObject.Left = 0

                rectObject.Right = iWidth

                rectObject.Top = 0

                rectObject.Bottom = iHeight

               

                Select Case iObjectState

                    Case iDisabled

                        rectObject.Top = iHeight * 2

                        rectObject.Bottom = iHeight * 3

                    Case iChecked

                        rectObject.Top = iHeight

                        rectObject.Bottom = iHeight * 2

                    Case iChecked_iDisabled

                        rectObject.Top = iHeight * 3

                        rectObject.Bottom = iHeight * 4

                End Select

           

            Case BaseWindow

                'Nothing needed here since we use the base rectangle

        End Select

       

        objSurface.Blt rectBitmap, objBitmap, rectObject, DDBLT_WAIT

       

        For iLp = 1 To colChildren.Count

            colChildren(iLp).DrawObject objSurface

        Next iLp

       

        Exit Function

       

    DrawObjectErr:

        Exit Function

       

    End Function

     

    What抯 Next?

     

    The first part of this series is an intro into creating a relatively full-fledged GUI for your game. The next article will deal with some more advanced pieces ?more controls and adding additional properties for the controls, including text properties. If there is anything specific you would like to see, please e-mail me or post on the board for the site where you found this article (if applicable).

     

    Feel free to modify this code as you see fit. Hopefully you抣l find a use for it, even though it抯 still in it抯 infant form.

     

    See you next time!


    最新回复(0)