Page 1 of 1

OOP with Video Game Basics Part 1 Rate Topic: -----

#1 modi123_1  Icon User is offline

  • Suitor #2
  • member icon



Reputation: 9204
  • View blog
  • Posts: 34,582
  • Joined: 12-June 08

Posted 28 August 2011 - 09:05 PM

Topics covered:
  • graphic display
  • video game basics
  • engine basics
  • object orientated programming.


Compiled in VS 2010, 3.5. Recommended Visual Studios 2005 or greater and framework 3.5 or greater.

Preface 1: I am going to assume you know how to create a Windows Form project and add class files to it.

Preface 2: This will be a quick walk through morphing the a VB.NET form into a game engine, show casing interesting bits of form styles to make it work, and a good example of object orientated programming fundamentals. It's not a serious engine or game programming how to, but a fun exercise.


Video games - gotta love them, but sometimes you just need to prototype something out fast. It turns out with some forward thinking you can make a pretty quick proof of concept game in a .NET windows form!

It's at this point I would highly advocate hitting up the XNA subforum and the XNA tutorials for games to be done in a proper setting through .NET. The flexibility and reliability is greatly held there so when you are done playing with this example upgrade and move over to the game programming tutorials!

By and by, to get started let's break down what is a video game. You have a user input, a game engine to process the current state in a consistently periodic time slice, and a video display. With in a windows form this means your form's keydown or mouse movement is the user input, a simple timer control will provide the periodic time, and the form's paint even for the display!

Honestly, it's that simple.

When the timer ticks (aka engine) any of our objects must process it's state and then draw itself. The state is any processing for that object. The user class processes where to move. The 'enemy' processes where on the path it needs to move by itself. The 'user' class could be adapted for attacking, using items, or feeling the effects of gravity in a jump!

Conceptually this is how the classes interact:
Attached Image

The trick is to compartmentalize your code in to object for reuse. In this case I have an abstract class that holds the basic information I think future classes will need to function. The bare necessities. An image to draw, a location, a required paint method, a required state to process, and a few other things.

Public MustInherit Class Entity

    Public Enum MovementDirection
        NONE = 0
        UP = 1
        RIGHT = 2
        DOWN = 3
        LEFT = 4
    End Enum

    Protected _bitmapImage As Bitmap = Nothing '-- the image to be drawn.
    Protected _pointLocation As Point = Nothing '-- where on the map is the user.
    Protected _sID As String = String.Empty '-- ids are usually important
    Protected _lMovementRate As Int32 = 0 '-- how fast the location changes when a move command is issued.
    Protected _lScreenWidth As Int32 = 0 '-- used to determine the boundaries so the location is still visible.
    Property _lScreenHeight As Int32 = 0 '-- used to determine the boundaries so the location is still visible.

    Public WriteOnly Property ScreenHeight As Int32
        Set(ByVal value As Int32)
            _lScreenHeight = value
        End Set
    End Property

    Public WriteOnly Property ScreenWidth As Int32
        Set(ByVal value As Int32)
            _lScreenWidth = value
        End Set
    End Property

    ''' <summary>
    ''' Print pertinent information
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Print() As String
        Return String.Format("{0}: x={1}, y={2}", _sID, _pointLocation.X, _pointLocation.Y)
    End Function

    '-- Everything must be drawn!
    Public MustOverride Sub Paint(ByVal e As Graphics)

    '-- Everything must be provided a consistant entry point to process that state for that given time slice.
    Public MustOverride Function ProcessState() As Boolean
End Class



I then have a 'user' and 'enemy' class that inherit from the 'entity' abstract class. This means user and enemy have all the functionality of 'entity' and can add to it! Future classes like items could also extend 'entity' and be mostly complete!

My 'user' class processes what action is next when the engine ticks. This means if a user pushed left then it moves left a set number of pixels. It's bound by the dimensions of the form so hopefully the location will not wander off the grid.

Note - you will need to define the path to the blue image here. If you don't it won't show up.

Public Class User
    Inherits Entity

    Private _enumNextAction As MovementDirection = MovementDirection.NONE
    Private _lHealth As Int32 = 1

    Public Property NextAction As MovementDirection
        Get
            Return _enumNextAction
        End Get
        Set(ByVal value As MovementDirection)
            _enumNextAction = value
        End Set
    End Property

    Public Sub New()
        _pointLocation = New Point(50, 50)
        _lMovementRate = 10
        _sID = "User"

        Try
            _bitmapImage = New Bitmap("your file path\blue.jpg")
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.OkOnly, String.Format("Class: {0}", Me.GetType().Name))
            _bitmapImage = New Bitmap(10, 10)
        End Try
    End Sub

    ''' <summary>
    ''' A constructor that takes in all the important bits of information to show the image on the screen.
    ''' </summary>
    ''' <param name="startingLocation"></param>
    ''' <param name="movementRate"></param>
    ''' <param name="imageLocation"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal startingLocation As Point, ByVal movementRate As Int32, ByVal imageLocation As String)
        _pointLocation = startingLocation
        _lMovementRate = movementRate
        _bitmapImage = New Bitmap(imageLocation)
        _sID = "User"
    End Sub

    '-- Sets up drawing the image.  
    Public Overrides Sub Paint(ByVal e As System.Drawing.Graphics)
        e.DrawImage(_bitmapImage, _pointLocation)
    End Sub

    Public Overrides Function ProcessState() As Boolean
        '-- The only state the user needs to worry about is which direction the user wants to move.
        Return DoMovement(_enumNextAction)
    End Function

    ''' <summary>
    ''' Takes the information from the user's key press and update the location.
    ''' </summary>
    ''' <param name="direction"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function DoMovement(ByVal direction As MovementDirection) As Boolean
        Dim bMoveOccured As Boolean = False

        If _enumNextAction <> MovementDirection.NONE Then
            Select Case direction
                Case MovementDirection.UP
                    If _pointLocation.Y - _lMovementRate >= 0 Then '-- make sure user is not moving off the screen 
                        _pointLocation.Y -= _lMovementRate
                        bMoveOccured = True
                    Else
                        _enumNextAction = MovementDirection.NONE
                    End If
                Case MovementDirection.DOWN
                    If _pointLocation.Y + _bitmapImage.Height + _lMovementRate < _lScreenHeight Then '-- make sure user is not moving off the screen 
                        _pointLocation.Y += _lMovementRate
                        bMoveOccured = True
                    Else
                        _enumNextAction = MovementDirection.NONE
                    End If
                Case MovementDirection.RIGHT
                    If _pointLocation.X + _bitmapImage.Width + _lMovementRate < _lScreenWidth Then '-- make sure user is not moving off the screen 
                        _pointLocation.X += _lMovementRate
                        bMoveOccured = True
                    Else
                        _enumNextAction = MovementDirection.NONE
                    End If
                Case MovementDirection.LEFT
                    If _pointLocation.X - _lMovementRate >= 0 Then '-- make sure user is not moving off the screen 
                        _pointLocation.X -= _lMovementRate
                        bMoveOccured = True
                    Else
                        _enumNextAction = MovementDirection.NONE
                    End If
                Case Else
                    bMoveOccured = False
            End Select

            If bMoveOccured Then _enumNextAction = MovementDirection.NONE

        End If
        Return bMoveOccured
    End Function

    ''' <summary>
    ''' This shows we can tack more information as we add variables to the print method already in the abstract class.
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overloads Function Print() As String
        Return MyBase.Print + String.Format(" Health: {0}", _lHealth)
    End Function
End Class



The 'enemy' class displays a different graphic and just walks a two hundred pixel path. It's all hard coded and you can alter the class later on to make the path be different. Notice how this is very similar in setup to the 'user' class, but different in the state processing and a few other areas. Oh the joys of object orientated programming!

Note - you will need to define the path to the red triangle image here. If you don't it won't show up.

Public Class Enemy
    Inherits Entity

    Private _bDirection As Boolean = True
    Private _pointStart As Point = Nothing
    Private _pointEnd As Point = Nothing

    Public Sub New()
        _pointLocation = New Point(400, 50)
        _lMovementRate = 10
        _sID = "Enemy1"

        SetupPath()

        Try
            _bitmapImage = New Bitmap("your path to\red_triangle.jpg")
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.OkOnly, String.Format("Class: {0}", Me.GetType().Name))
            _bitmapImage = New Bitmap(10, 10)
        End Try


    End Sub

    '-- determine the start and stop points for the path to follow.
    '-- This is hard coded to be just two hundred pixels below the current starting position.  
    Private Sub SetupPath()
        _pointStart = _pointLocation

        _pointEnd = New Point(_pointStart.X, _pointStart.Y + 200)
    End Sub

    Public Overrides Sub Paint(ByVal e As System.Drawing.Graphics)
        '-- Paints the image at the current location.
        e.DrawImage(_bitmapImage, _pointLocation)
    End Sub

    Public Overrides Function ProcessState() As Boolean
        '-- The only state the enemy is required to care about is if it is following the path hard coded.
        WalkPath()
        Return True
    End Function

    ''' <summary>
    ''' A simple back and forth path.  The location moves in one direction until it hits that direction, flips a boolean, and goes the opposite.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub WalkPath()

        If _bDirection Then
            If _pointLocation.Y + _lMovementRate + _bitmapImage.Height <= _pointEnd.Y Then
                _pointLocation.Y += _lMovementRate
            Else
                _bDirection = False
            End If
        Else
            If _pointLocation.Y - _lMovementRate >= _pointStart.Y Then
                _pointLocation.Y -= _lMovementRate
            Else
                _bDirection = True
            End If
        End If
    End Sub
End Class



My form creates the user and enemy objects, reacts to keyboard input by setting the user's next action, takes care of the periodic engine processing for the user and enemy,and draws them both.

There are some critical styles set in the form's new that keep the movement smooth and not flickering. Also notice how when the key down is pushed and held only one 'next action' is set - this prevents a backup of key presses where the 'user' object tries to catch up and process a very long series of events.

Also notice the complexity of processing the states or graphics are all pushed into the objects. This makes the window form very simple to read and understand!

Public Class Form1

    Private _IsDebug As Boolean = False '-- turn on if you want to see information in the debug window about the object's state.

    Private WithEvents _timer As Timer = Nothing '-- the 'game engine'.
    Private _oUser As User '-- our user class
    Private _oEnemy As Enemy = Nothing '-- An enemy class that follows a way point that is hard coded.
    Public Sub New()
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.

        '-- Setup the display to not flicker and procss the graphics smoothly.
        Me.SetStyle(ControlStyles.UserPaint, True)
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)

        Me.Height = 500
        Me.Width = 500

        '-- create our user to be drawn.
        _oUser = New User
        _oUser.ScreenHeight = Me.Height '-- to prevent moving off the screen!
        _oUser.ScreenWidth = Me.Width '-- to prevent moving off the screen!
        If _IsDebug Then Debug.WriteLine(_oUser.Print)

        _oEnemy = New Enemy
        _oEnemy.ScreenHeight = Me.Height '-- to prevent moving off the screen!
        _oEnemy.ScreenWidth = Me.Width '-- to prevent moving off the screen!
        If _IsDebug Then Debug.WriteLine(_oEnemy.Print)

        '-- setup our timer as the engine to process.
        _timer = New Timer
        _timer.Interval = 100
        _timer.Start()
    End Sub

    '-- the "game engine".
    Private Sub _timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _timer.Tick
        '-- When the engine ticks proces the input into something our user class can do.
        _oUser.ProcessState()
        If _IsDebug Then Debug.WriteLine(_oUser.Print)

        _oEnemy.ProcessState()
        If _IsDebug Then Debug.WriteLine(_oEnemy.Print)

        '-- Redraw the scene
        Me.Refresh()
    End Sub

    '-- The take the form's input and set that action to our user object's next action on deck.
    Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        '-- To avoid the issue of having a stack of key presses if the user holds the key down only accept one 
        '-- key stroke until it is processed and the graphics updated.
        If _oUser.NextAction = Entity.MovementDirection.NONE Then
            Select Case e.KeyCode
                Case Keys.Up, Keys.W
                    _oUser.NextAction = Entity.MovementDirection.UP
                Case Keys.Down, Keys.S
                    _oUser.NextAction = Entity.MovementDirection.DOWN
                Case Keys.Right, Keys.D
                    _oUser.NextAction = Entity.MovementDirection.RIGHT
                Case Keys.Left, Keys.A
                    _oUser.NextAction = Entity.MovementDirection.LEFT
            End Select
        End If
    End Sub

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        _oEnemy.Paint(e.Graphics)

        '-- Pain the changes to the screen.
        _oUser.Paint(e.Graphics)
    End Sub

End Class




In theory this is what your screen should look like when you run the code.
Attached Image

Zip
Attached File  VBNET_VidoGame_Basics_1.zip (43.74K)
Number of downloads: 671

Helpful links:
XNA subforum
Game Programming
MSDN: Object-Oriented Programming in Visual Basic .NET

Advance topics
  • change the images to something you created.
  • animation in a windows form
  • collisions between objects
  • inventory: tracking, collecting, and using.
  • interaction between objects (attacking and damage!)
  • menus - drawing them
  • more complex way-points for the enemy to follow.
  • flesh out the enemy's constructor


Is This A Good Question/Topic? 3
  • +

Replies To: OOP with Video Game Basics Part 1

#2 modi123_1  Icon User is offline

  • Suitor #2
  • member icon



Reputation: 9204
  • View blog
  • Posts: 34,582
  • Joined: 12-June 08

Posted 03 September 2011 - 06:14 PM

*reserved*
Was This Post Helpful? 0
  • +
  • -

#3 ???  Icon User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 48
  • Joined: 25-November 10

Posted 16 September 2011 - 02:11 PM

Nice job!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1