Skip to content

modi1231/OOPBasics

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OOPBasics

Adding the 2011 VB.NET code and C# translation from this tutorial.

https://www.dreamincode.net/forums/topic/245268-oop-with-video-game-basics-part-1/

================= dreamincode.net tutorial backup ahead of decommissioning

Posted 28 August 2011 - 11:05 PM

[u][b]GITHUB[/b][/u]: https://github.com/modi1231/OOPBasics

[u][b]Topics covered:[/b][/u] [list] []graphic display []video game basics []engine basics []object orientated programming. [/list]

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

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

[i]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.
[/i]

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: [attachment=25355:game interaction.jpg]

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.

[code] 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 [/code]

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.

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

[code] 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 [/code]

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!

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

[code] 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 [/code]

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!

[code] 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

[/code]

In theory this is what your screen should look like when you run the code.
[attachment=25354:example_output.jpg]

[b][u]Zip[/u][/b] [attachment=25356:VBNET_VidoGame_Basics_1.zip]

[u][b]Helpful links:[/b][/u] [url="http://www.dreamincode.net/forums/forum/125-xna/"]XNA subforum[/url] [url="http://www.dreamincode.net/forums/forum/69-game-programming/"]Game Programming[/url] [url="http://msdn.microsoft.com/en-us/library/aa289512%28v=vs.71%29.aspx"]MSDN: Object-Oriented Programming in Visual Basic .NET[/url]

[u][b]Advance topics[/b][/u] [list] []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 [/list]

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published