Skip to content

A tank is a heavily armored fighting vehicle designed for front-line combat.

License

Notifications You must be signed in to change notification settings

JoeLumbley/Tank

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tank

This exciting and interactive app brings the thrill of tank battles to your screen, combining strategic gameplay with dynamic graphics and responsive controls. This project showcases a robust architecture that handles everything from turret mechanics to projectile management and audio integration.

001

In this app, users can control a tank, rotate its turret, fire projectiles, and navigate through a beautifully rendered environment. With support for Xbox controllers, the gameplay experience is enhanced, making it accessible for both keyboard enthusiasts and gamepad users.

Whether you’re a developer looking to explore game design principles or a gamer eager for a fun challenge, this repository offers a comprehensive look into the mechanics of game development. Dive in, explore the code, and unleash your creativity as you modify and expand upon this foundational project!

Join us on this journey to create an engaging tank battle experience!


Code Walkthrough

Class Declaration

Public Class Form1
  • Public Class Form1: This line declares a new class named Form1. In Visual Basic, a class is a blueprint for creating objects. Here, Form1 will represent the main window of our application.

Member Variables

DeltaTime

Private DeltaTime As New DeltaTimeStructure(Now, Now, TimeSpan.Zero)
  • Private DeltaTime: This declares a variable named DeltaTime that will track the time between frames in the game.
  • New DeltaTimeStructure(Now, Now, TimeSpan.Zero): It creates a new instance of the DeltaTimeStructure, initializing it with the current time (Now) and a zero time span.

OffScreen Buffer

Private OffScreen As New BufferManager(Me, BackColor)
  • Private OffScreen: This variable handles off-screen rendering to reduce flickering during drawing operations.
  • New BufferManager(Me, BackColor): It creates a new BufferManager object that takes the current form (Me) and its background color (BackColor) as parameters.

Audio Player

Private Player As AudioPlayer
  • Private Player: This variable will manage audio playback in the game. It will be initialized later in the code.

Turret and Projectiles

Private Turret As Turret
  • Private Turret: This variable will hold an instance of the Turret class, which manages the tank's turret.
Private Projectiles As New ProjectileManager(Brushes.Red, New Drawing.Size(10, 10), 200, 100, 9)
  • Private Projectiles: This initializes a ProjectileManager to handle the game's projectiles.
  • Brushes.Red: Sets the projectile color to red.
  • New Drawing.Size(10, 10): Sets the size of each projectile to 10x10 pixels.
  • 200, 100, 9: These parameters define other behaviors of the projectiles, such as their speed and lifespan.

Controllers

Private Controllers As XboxControllers
  • Private Controllers: This variable will manage input from Xbox controllers.

Client Center

Private ClientCenter As Point = New Point(ClientSize.Width / 2, ClientSize.Height / 2)
  • Private ClientCenter: This calculates the center point of the client area (the drawable area) of the form, which is used for positioning objects.

Tank Body

Private Body As New Body(Brushes.Gray, New PointF(500, 500), 128, 64, 0, 0, 150, 30)
  • Private Body: This creates an instance of the Body class, representing the tank's body.
  • Brushes.Gray: Sets the tank's color to gray.
  • New PointF(500, 500): Sets the initial position of the tank to (500, 500).
  • 128, 64, 0, 0, 150, 30: These parameters define the tank's dimensions and physics properties.

Arrow Vector

Dim Arrow As New ArrowVector(New Pen(Color.Black, 10), New PointF(640, 360), 0, 60, 70, 10, 15, 0, Body.MaxVelocity, 30)
  • Dim Arrow: This defines an ArrowVector that visually represents the direction and velocity of the tank.
  • New Pen(Color.Black, 10): Initializes the arrow with a black pen of width 10.
  • New PointF(640, 360): Sets the starting point of the arrow.
  • 0, 60, 70, 10, 15, 0, Body.MaxVelocity, 30: Various parameters that define the arrow's behavior.

Relative Turret Angle

Private RelativeTurretAngle As Single
  • Private RelativeTurretAngle: This variable will store the angle difference between the turret and the body of the tank.

Input Tracking Variables

Private ADown As Boolean
Private DDown As Boolean
Private WDown As Boolean
Private SDown As Boolean
Private EDown As Boolean
Private F1Down As Boolean
Private F1DownHandled As Boolean
Private F2Down As Boolean
Private F2DownHandled As Boolean
Private XDown As Boolean
Private LeftArrowDown As Boolean
Private RightArrowDown As Boolean
Private SpaceDown As Boolean
  • Each of these variables tracks whether specific keys are currently pressed down. For example:
    • ADown: Tracks if the 'A' key is pressed.
    • WDown: Tracks if the 'W' key is pressed for moving forward.
    • SpaceDown: Tracks if the spacebar is pressed for firing.

Timing Variables

Private TimeToNextRotation As TimeSpan = TimeSpan.FromMilliseconds(1)
Private LastRotationTime As DateTime = Now
  • Private TimeToNextRotation: Sets a minimal time interval for turret rotation.
  • Private LastRotationTime: Records the last time the turret was rotated.

Font and Text Variables

Private InstructionsFont As New Font("Segoe UI", 14)
Private InstructionsLocation As New Point(0, 0)
Private F1NoticeLocation As New PointF(0, 0)
  • Private InstructionsFont: Defines the font used for displaying instructions.
  • Private InstructionsLocation: Sets where the instruction text will appear on the screen.

Hints and Instructions Text

Private HiddenHints As New String("Show / Hide Keyboard Hints > F1" & Environment.NewLine)
  • Private HiddenHints: A string containing hints for keyboard controls.
Private KeyboardHintsText As New String("Show / Hide Keyboard Hints > F1" & Environment.NewLine & "Rotate Turret CounterClockwise > Left Arrow" & Environment.NewLine & ...)
  • Private KeyboardHintsText: Provides detailed instructions for keyboard controls.

Controller Hints Text

Private ControllerHintsText As New String("Show / Hide Keyboard Hints > F1" & Environment.NewLine & "Rotate Turret CounterClockwise > Right Thumbstick Left" & Environment.NewLine & ...)
  • Private ControllerHintsText: Similar to the keyboard hints, this provides instructions for using a controller.

Hints Text

Private HintsText As String = KeyboardHintsText
  • Private HintsText: This variable holds the current hints text to be displayed, initially set to keyboard hints.

Constructor

Public Sub New()
    InitializeComponent()
    InitializeForm()
    InitializeTimer()
    Controllers.Initialize()
    InitializeSounds()
    Body.TimeToNextRotation = TimeSpan.FromMilliseconds(25)
    Turret = New Turret(New Pen(Color.Black, 10), 50, Body.Center, 75, 0, TimeSpan.FromMilliseconds(100))
    RelativeTurretAngle = Turret.AngleInDegrees - Body.AngleInDegrees
End Sub
  • Public Sub New(): This is the constructor for the Form1 class. It runs when an instance of the form is created.
  • InitializeComponent(): Sets up the form's components (like buttons, labels, etc.).
  • InitializeForm(): Custom method to set additional properties for the form.
  • InitializeTimer(): Sets up a timer for updating the game state.
  • Controllers.Initialize(): Initializes the Xbox controllers for input.
  • InitializeSounds(): Loads and prepares sound files for playback.
  • Body.TimeToNextRotation = TimeSpan.FromMilliseconds(25): Sets the rotation interval for the tank body.
  • Turret = New Turret(...): Creates a new turret instance with specified properties.
  • RelativeTurretAngle = Turret.AngleInDegrees - Body.AngleInDegrees: Calculates the initial angle difference between the turret and the tank body.

Timer Tick Event

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    DeltaTime.Update()
    HandleUserInput()
    Projectiles.UpdateProjectiles(DeltaTime.ElapsedTime)
    Body.CheckWallBounce(Body.Body, ClientSize.Width, ClientSize.Height)
    Body.Update(DeltaTime.ElapsedTime)
    Turret.AngleInDegrees = (Body.AngleInDegrees + RelativeTurretAngle + 360) Mod 360
    Arrow.Center = Body.Center
    Arrow.AngleInDegrees = Body.AngleInDegrees
    Arrow.Velocity = Body.Velocity
    Arrow.Update(DeltaTime.ElapsedTime)
    Turret.Center = Body.Center
    HandleAudioPlayback()
    Invalidate()
End Sub
  • Private Sub Timer1_Tick(...): This method is called at regular intervals defined by the timer. It updates the game state.
  • DeltaTime.Update(): Updates the elapsed time since the last frame.
  • HandleUserInput(): Calls a method to process user input from the keyboard or controller.
  • Projectiles.UpdateProjectiles(DeltaTime.ElapsedTime): Updates the state of all projectiles based on the elapsed time.
  • Body.CheckWallBounce(...): Checks if the tank has hit any walls and adjusts its position accordingly.
  • Body.Update(DeltaTime.ElapsedTime): Updates the tank's position and physics based on the elapsed time.
  • Turret.AngleInDegrees = (Body.AngleInDegrees + RelativeTurretAngle + 360) Mod 360: Calculates the turret's angle based on the tank's current angle and the relative angle.
  • Arrow.Center = Body.Center: Updates the arrow's center position to match the tank's position.
  • Arrow.AngleInDegrees = Body.AngleInDegrees: Sets the arrow's angle to match the tank's angle.
  • Arrow.Velocity = Body.Velocity: Updates the arrow's velocity based on the tank's current velocity.
  • Arrow.Update(DeltaTime.ElapsedTime): Updates the arrow's position based on the elapsed time.
  • Turret.Center = Body.Center: Sets the turret's position to match the tank's position.
  • HandleAudioPlayback(): Manages the audio playback based on the tank's movement.
  • Invalidate(): Requests to repaint the form, triggering the OnPaint method.

Paint Event

Protected Overrides Sub OnPaint(e As PaintEventArgs)
    MyBase.OnPaint(e)
    DrawFrame()
    OffScreen.Buffered?.Render(e.Graphics)
    OffScreen.EraseFrame()
End Sub
  • Protected Overrides Sub OnPaint(...): This method is called when the form needs to be repainted.
  • MyBase.OnPaint(e): Calls the base class's paint method to ensure proper rendering.
  • DrawFrame(): Calls a custom method to draw the game elements.
  • OffScreen.Buffered?.Render(e.Graphics): Renders the off-screen buffer onto the form's graphics.
  • OffScreen.EraseFrame(): Clears the buffer for the next frame.

Key Down Event

Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
    MyBase.OnKeyDown(e)
    Select Case e.KeyCode
        Case Keys.A
            ADown = True
        Case Keys.D
            DDown = True
        Case Keys.W
            WDown = True
        Case Keys.S
            SDown = True
        Case Keys.E
            EDown = True
        Case Keys.F1
            F1Down = True
        Case Keys.F2
            F2Down = True
        Case Keys.X
            XDown = True
        Case Keys.Left
            LeftArrowDown = True
        Case Keys.Right
            RightArrowDown = True
        Case Keys.Space
            SpaceDown = True
    End Select
End Sub
  • Protected Overrides Sub OnKeyDown(...): This method handles key presses.
  • Select Case e.KeyCode: Checks which key was pressed and sets the corresponding boolean variable to True.

Key Up Event

Protected Overrides Sub OnKeyUp(e As KeyEventArgs)
    MyBase.OnKeyUp(e)
    Select Case e.KeyCode
        Case Keys.A
            ADown = False
        Case Keys.D
            DDown = False
        Case Keys.W
            WDown = False
        Case Keys.S
            SDown = False
        Case Keys.E
            EDown = False
        Case Keys.F1
            F1Down = False
            F1DownHandled = False
        Case Keys.F2
            F2Down = False
            F2DownHandled = False
        Case Keys.X
            XDown = False
        Case Keys.Left
            LeftArrowDown = False
        Case Keys.Right
            RightArrowDown = False
        Case Keys.Space
            SpaceDown = False
    End Select
End Sub
  • Protected Overrides Sub OnKeyUp(...): This method handles key releases.
  • Select Case e.KeyCode: Checks which key was released and sets the corresponding boolean variable to False.

Resize Event

Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles Me.Resize
    If Not WindowState = FormWindowState.Minimized Then
        ClientCenter = New Point(ClientSize.Width / 2, ClientSize.Height / 2)
        Body.Center = ClientCenter
        OffScreen.DisposeBuffer()
        Timer1.Enabled = True
    Else
        Timer1.Enabled = False
    End If
End Sub
  • Private Sub Form1_Resize(...): This method handles resizing the form.
  • If Not WindowState = FormWindowState.Minimized: Checks if the window is not minimized.
  • ClientCenter = New Point(ClientSize.Width / 2, ClientSize.Height / 2): Updates the center based on the new size.
  • Body.Center = ClientCenter: Centers the tank body to the new client center.
  • OffScreen.DisposeBuffer(): Disposes of the current off-screen buffer to prepare for a new one.
  • Timer1.Enabled = True: Starts the timer if the window is not minimized.
  • Else Timer1.Enabled = False: Stops the timer if the window is minimized.

Load Event

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    ClientCenter = New Point(ClientSize.Width / 2, ClientSize.Height / 2)
End Sub
  • Private Sub Form1_Load(...): This method runs when the form is loaded.
  • ClientCenter = New Point(ClientSize.Width / 2, ClientSize.Height / 2): Initializes the center point of the client area.

Draw Frame

Private Sub DrawFrame()
    OffScreen.AllocateBuffer(Me)
    OffScreen.Buffered.Graphics.DrawString(HintsText, InstructionsFont, Brushes.Black, InstructionsLocation)
    Body.Draw(OffScreen.Buffered.Graphics, ClientSize)
    Arrow.Draw(OffScreen.Buffered.Graphics)
    Projectiles.DrawProjectiles(OffScreen.Buffered.Graphics)
    Turret.Draw(OffScreen.Buffered.Graphics)
End Sub
  • Private Sub DrawFrame(): This method handles drawing the game elements.
  • OffScreen.AllocateBuffer(Me): Allocates a new buffer for off-screen drawing.
  • OffScreen.Buffered.Graphics.DrawString(...): Draws the hints text on the screen.
  • Body.Draw(...), Arrow.Draw(...), Projectiles.DrawProjectiles(...), Turret.Draw(...): Calls the draw methods for each game element to render them onto the buffer.

Handle User Input

Private Sub HandleUserInput()
    Controllers.Update()
    HandleKeyPresses()
    HandleControllerInput()
End Sub
  • Private Sub HandleUserInput(): This method processes user input.
  • Controllers.Update(): Updates the state of the controllers.
  • HandleKeyPresses(): Calls a method to handle keyboard input.
  • HandleControllerInput(): Calls a method to handle controller input.

Handle Key Presses

Private Sub HandleKeyPresses()

    If ADown Then
        Body.RotateCounterClockwise()
    End If

    If DDown Then
        Body.RotateClockwise()
    End If

    If WDown Then
        If Body.Velocity < Body.MaxVelocity Then
            Body.Velocity += 1
        Else
            Body.Velocity = Body.MaxVelocity
        End If
    ElseIf SDown Then
        If Body.Velocity > -Body.MaxVelocity Then
            Body.Velocity += -1
        Else
            Body.Velocity = -Body.MaxVelocity
        End If
    Else
        Body.Decelerate(DeltaTime.ElapsedTime)
    End If

    If EDown Then
        Body.EmergencyStop(DeltaTime.ElapsedTime)
        If Body.Velocity <> 0 Then
            If Not Player.IsPlaying("emergencystop") Then
                Player.PlaySound("emergencystop")
            End If
        Else
            If Player.IsPlaying("emergencystop") Then
                Player.PauseSound("emergencystop")
            End If
        End If
    ElseIf Not Controllers.B(0) Then
        If Player.IsPlaying("emergencystop") Then
            Player.PauseSound("emergencystop")
        End If
    End If

    If F1Down Then
        If Body.ShowControllerHints Then Body.ShowControllerHints = False
        If Body.ShowKeyboardHints Then
            If Not F1DownHandled Then
                Body.ShowKeyboardHints = False
                HintsText = HiddenHints
                F1DownHandled = True
            End If
        Else
            If Not F1DownHandled Then
                Body.ShowKeyboardHints = True
                HintsText = KeyboardHintsText
                F1DownHandled = True
            End If
        End If
    End If

    If F2Down Then
        If Body.ShowKeyboardHints Then Body.ShowKeyboardHints = False
        If Body.ShowControllerHints Then
            If Not F2DownHandled Then
                Body.ShowControllerHints = False
                HintsText = HiddenHints
                F2DownHandled = True
            End If
        Else
            If Not F2DownHandled Then
                Body.ShowControllerHints = True
                HintsText = ControllerHintsText
                F2DownHandled = True
            End If
        End If
    End If

    If XDown OrElse SpaceDown Then
        FireProjectile()
    End If

    If LeftArrowDown Then
        RotateTurretCounterClockwise()
    End If

    If RightArrowDown Then
        RotateTurretClockwise()
    End If

End Sub
  • Private Sub HandleKeyPresses(): This method processes the state of the tank based on key presses.
  • If ADown Then Body.RotateCounterClockwise(): If the 'A' key is pressed, the tank rotates counterclockwise.
  • If DDown Then Body.RotateClockwise(): If the 'D' key is pressed, the tank rotates clockwise.
  • If WDown Then: If the 'W' key is pressed, the tank accelerates.
    • If Body.Velocity < Body.MaxVelocity Then: Checks if the tank's velocity is less than its maximum speed.
    • Body.Velocity += 1: Increases the tank's velocity by 1.
    • Else Body.Velocity = Body.MaxVelocity: Sets the velocity to the maximum if it exceeds it.
  • ElseIf SDown Then: If the 'S' key is pressed, the tank reverses.
    • Similar logic checks if the velocity can be decreased or sets it to the maximum reverse speed.
  • Else Body.Decelerate(DeltaTime.ElapsedTime): If neither 'W' nor 'S' is pressed, the tank decelerates based on the elapsed time.
  • If EDown Then: If the 'E' key is pressed, it triggers an emergency stop.
    • Body.EmergencyStop(DeltaTime.ElapsedTime): Stops the tank immediately.
    • Checks if the emergency stop sound is playing and plays or pauses it accordingly.
  • If F1Down Then: If the 'F1' key is pressed, it toggles the visibility of keyboard hints.
  • If F2Down Then: If the 'F2' key is pressed, it toggles the visibility of controller hints.
  • If XDown OrElse SpaceDown Then FireProjectile(): If either the 'X' key or spacebar is pressed, it triggers the firing of a projectile.
  • If LeftArrowDown Then RotateTurretCounterClockwise(): If the left arrow key is pressed, it rotates the turret counterclockwise.
  • If RightArrowDown Then RotateTurretClockwise(): If the right arrow key is pressed, it rotates the turret clockwise.

Rotating the Turret Clockwise

Private Sub RotateTurretClockwise()
    Dim TimeSinceLastRotation As TimeSpan = Now - LastRotationTime
    If TimeSinceLastRotation > TimeToNextRotation Then
        Dim AngleInDegrees = (Turret.AngleInDegrees + 1) Mod 360
        RelativeTurretAngle = (AngleInDegrees - Body.AngleInDegrees + 360) Mod 360
        LastRotationTime = Now
    End If
End Sub
  • Private Sub RotateTurretClockwise(): This method handles the clockwise rotation of the turret.
  • Dim TimeSinceLastRotation As TimeSpan = Now - LastRotationTime: Calculates the time since the last rotation.
  • If TimeSinceLastRotation > TimeToNextRotation Then: Checks if enough time has passed to allow another rotation.
  • Dim AngleInDegrees = (Turret.AngleInDegrees + 1) Mod 360: Increments the turret's angle by 1 degree, ensuring it stays within a 0-359 degree range using modulo 360.
  • RelativeTurretAngle = (AngleInDegrees - Body.AngleInDegrees + 360) Mod 360: Updates the relative angle of the turret compared to the body.
  • LastRotationTime = Now: Updates the last rotation time to the current time.

Rotating the Turret Counterclockwise

Private Sub RotateTurretCounterClockwise()
    Dim TimeSinceLastRotation As TimeSpan = Now - LastRotationTime
    If TimeSinceLastRotation > TimeToNextRotation Then
        Dim AngleInDegrees = (Turret.AngleInDegrees - 1 + 360) Mod 360
        RelativeTurretAngle = (AngleInDegrees - Body.AngleInDegrees + 360) Mod 360
        LastRotationTime = Now
    End If
End Sub
  • Private Sub RotateTurretCounterClockwise(): This method handles the counterclockwise rotation of the turret.
  • The logic is similar to the clockwise rotation method, but it decrements the angle instead.

Firing Projectiles

Private Sub FireProjectile()
    If Now - Turret.LastFireTime > Turret.TimeToNextFire Then
        Player.PlayOverlapping("gunshot")
        Projectiles.FireProjectile(Turret.Center, Turret.AngleInDegrees)
        Turret.LastFireTime = Now
    End If
End Sub
  • Private Sub FireProjectile(): This method handles firing a projectile from the turret.
  • If Now - Turret.LastFireTime > Turret.TimeToNextFire Then: Checks if enough time has passed since the last shot.
  • Player.PlayOverlapping("gunshot"): Plays the gunshot sound effect.
  • Projectiles.FireProjectile(Turret.Center, Turret.AngleInDegrees): Fires a projectile from the turret's center at its current angle.
  • Turret.LastFireTime = Now: Updates the last fire time to the current time.

Handle Controller Input

Private Sub HandleControllerInput()
    If Controllers.LeftThumbstickLeft(0) Then
        Body.RotateCounterClockwise()
    End If
    If Controllers.LeftThumbstickRight(0) Then
        Body.RotateClockwise()
    End If
    If Controllers.A(0) OrElse Controllers.LeftStick(0) Then
        If Body.Velocity < Body.MaxVelocity Then
            Body.Velocity += 1
        Else
            Body.Velocity = Body.MaxVelocity
        End If
    ElseIf Controllers.Y(0) Then
        If Body.Velocity > -Body.MaxVelocity Then
            Body.Velocity += -1
        Else
            Body.Velocity = -Body.MaxVelocity
        End If
    Else
        Body.Decelerate(DeltaTime.ElapsedTime)
    End If
    If Controllers.B(0) Then
        Body.EmergencyStop(DeltaTime.ElapsedTime)
        If Body.Velocity <> 0 Then
            If Not Player.IsPlaying("emergencystop") Then
                Player.PlaySound("emergencystop")
            End If
            Controllers.TimeToVibe = 50
            Controllers.VibrateRight(0, 32000)
        Else
            If Player.IsPlaying("emergencystop") Then
                Player.PauseSound("emergencystop")
            End If
        End If
    ElseIf Not EDown Then
        If Player.IsPlaying("emergencystop") Then
            Player.PauseSound("emergencystop")
        End If
    End If
    If Controllers.X(0) OrElse Controllers.RightTrigger(0) Then
        FireProjectile()
    End If
    If Controllers.RightThumbstickLeft(0) Then
        RotateTurretCounterClockwise()
    End If
    If Controllers.RightThumbstickRight(0) Then
        RotateTurretClockwise()
    End If
End Sub
  • Private Sub HandleControllerInput(): This method processes input from the Xbox controller.
  • If Controllers.LeftThumbstickLeft(0): If the left thumbstick is moved left, the tank rotates counterclockwise.
  • If Controllers.LeftThumbstickRight(0): If the left thumbstick is moved right, the tank rotates clockwise.
  • If Controllers.A(0) OrElse Controllers.LeftStick(0): If button A is pressed or the left stick is pushed forward, it accelerates the tank.
  • If Controllers.Y(0): If button Y is pressed, it reverses the tank.
  • Else Body.Decelerate(DeltaTime.ElapsedTime): If neither button is pressed, the tank decelerates.
  • If Controllers.B(0): If button B is pressed, the tank performs an emergency stop.
  • If Body.Velocity <> 0: If the tank is moving, it plays the emergency stop sound and vibrates the controller.
  • If Controllers.X(0) OrElse Controllers.RightTrigger(0): If button X or the right trigger is pressed, it fires a projectile.
  • If Controllers.RightThumbstickLeft(0): If the right thumbstick is moved left, it rotates the turret counterclockwise.
  • If Controllers.RightThumbstickRight(0): If the right thumbstick is moved right, it rotates the turret clockwise.

Handle Audio Playback

Private Sub HandleAudioPlayback()
    If Body.Velocity <> 0 Then
        If Not Player.IsPlaying("running") Then
            Player.LoopSound("running")
        End If
        If Player.IsPlaying("idle") Then
            Player.PauseSound("idle")
        End If
    Else
        If Not Player.IsPlaying("idle") Then
            Player.LoopSound("idle")
        End If
        If Player.IsPlaying("running") Then
            Player.PauseSound("running")
        End If
    End If
End Sub
  • Private Sub HandleAudioPlayback(): This method manages the background sounds based on the tank's movement state.
  • If Body.Velocity <> 0: If the tank is moving, it plays the running sound.
  • If Not Player.IsPlaying("running"): Ensures the running sound is not already playing before looping it.
  • If Player.IsPlaying("idle"): If the idle sound is playing, it pauses it.
  • Else: If the tank is not moving, it plays the idle sound and pauses the running sound if it’s playing.

Initialize Form

Private Sub InitializeForm()
    CenterToScreen()
    SetStyle(ControlStyles.UserPaint, True)
    SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint, True)
    Text = "Tank - Code with Joe"
    WindowState = FormWindowState.Maximized
End Sub
  • Private Sub InitializeForm(): This method sets up the form's properties.
  • CenterToScreen(): Centers the form on the screen.
  • SetStyle(ControlStyles.UserPaint, True): Allows custom painting on the form.
  • SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint, True): Enables double buffering to reduce flickering during rendering.
  • Text = "Tank - Code with Joe": Sets the window title.
  • WindowState = FormWindowState.Maximized: Starts the form in a maximized state.

Initialize Timer

Private Sub InitializeTimer()
    Timer1.Interval = 15
    Timer1.Start()
End Sub
  • Private Sub InitializeTimer(): This method sets up the game timer.
  • Timer1.Interval = 15: Sets the timer to tick every 15 milliseconds.
  • Timer1.Start(): Starts the timer.

Initialize Sounds

Private Sub InitializeSounds()
    CreateSoundFiles()
    Dim FilePath As String = Path.Combine(Application.StartupPath, "idle.mp3")
    Player.AddSound("idle", FilePath)
    Player.SetVolume("idle", 300)
    Player.LoopSound("idle")
    
    FilePath = Path.Combine(Application.StartupPath, "running.mp3")
    Player.AddSound("running", FilePath)
    Player.SetVolume("running", 400)
    
    FilePath = Path.Combine(Application.StartupPath, "emergencystop.mp3")
    Player.AddSound("emergencystop", FilePath)
    Player.SetVolume("emergencystop", 1000)
    
    FilePath = Path.Combine(Application.StartupPath, "gunshot.mp3")
    Player.AddOverlapping("gunshot", FilePath)
    Player.SetVolume("gunshot", 1000)
End Sub
  • Private Sub InitializeSounds(): This method initializes all sound effects used in the game.
  • CreateSoundFiles(): Calls a method to create sound files from resources.
  • Dim FilePath As String = Path.Combine(Application.StartupPath, "idle.mp3"): Defines the path to the sound files.
  • Player.AddSound("idle", FilePath): Loads the idle sound into the audio player.
  • Player.SetVolume(...): Sets the volume for each sound effect.
  • Player.LoopSound("idle"): Loops the idle sound when the game starts.

Create Sound Files

Private Sub CreateSoundFiles()
    Dim FilePath As String = Path.Combine(Application.StartupPath, "idle.mp3")
    CreateFileFromResource(FilePath, My.Resources.Resource1.idle)
    
    FilePath = Path.Combine(Application.StartupPath, "running.mp3")
    CreateFileFromResource(FilePath, My.Resources.Resource1.running)
    
    FilePath = Path.Combine(Application.StartupPath, "emergencystop.mp3")
    CreateFileFromResource(FilePath, My.Resources.Resource1.emergencystop)
    
    FilePath = Path.Combine(Application.StartupPath, "gunshot.mp3")
    CreateFileFromResource(FilePath, My.Resources.Resource1.gunshot003)
End Sub
  • Private Sub CreateSoundFiles(): This method creates sound files from embedded resources.
  • CreateFileFromResource(...): Calls a helper method to create the file if it doesn't exist.

Creating a File from Resource

Private Sub CreateFileFromResource(filepath As String, resource As Byte())
    Try
        If Not IO.File.Exists(filepath) Then
            IO.File.WriteAllBytes(filepath, resource)
        End If
    Catch ex As Exception
        Debug.Print($"Error creating file: {ex.Message}")
    End Try
End Sub
  • Private Sub CreateFileFromResource(...): This method creates a file from a byte array resource.
  • If Not IO.File.Exists(filepath): Checks if the file already exists.
  • IO.File.WriteAllBytes(filepath, resource): Writes the resource to a file.
  • Catch ex As Exception: Catches any exceptions that occur during file creation and prints an error message to the debug output.

Key Takeaways

  • Understanding how to manage game state using timers and events.
  • Learning how to handle user input from both keyboard and controllers.
  • Implementing basic game mechanics like movement, rotation, and firing.
  • Managing audio playback for a more immersive experience.

Feel free to explore the code further and modify it to enhance your understanding of game development principles. Happy coding!



Steps to Clone a Repository in Visual Studio

  1. Open Visual Studio:

    • Launch Visual Studio on your computer.
  2. Go to the Start Window:

    • If you're on the start window, look for the "Clone a repository" option. If you have a project open, you can access this by going to File > Open > Clone....
  3. Enter Repository URL:

    • In the "Clone a repository" dialog, enter the URL of the repository you want to clone in the "Repository location" field.
  4. Select Local Path:

    • Choose the local path where you want to clone the repository by clicking the "Browse" button.
  5. Click Clone:

    • After entering the URL and selecting the local path, click the "Clone" button.
  6. Open the Cloned Repository:

    • Once cloning is complete, Visual Studio will open the cloned repository, and you can start working on it.

Watch Cloning a repo video.

https://youtube.com/shorts/n8bCEIdI44U?si=fGO3YiglkCNPcVmj