Tetris Racing – The game I didn’t know how to create

Disclaimer: 

I am not a VBA expert and some portions of this code may not follow the standard coding practices. It is what it is.

Assumptions: 

I assume you have some experience with Excel VBA and its infrastructure (how to view project, general knowledge of the coding syntax etc…)

Credits: 

Ken, Developer of Excel Snake Game | Snake Game Video | Reddit Source

Other Notes:

I don’t plan to explain everything about this game (as it would just be too long). I hope to provide a sequence of events in order to help you replicate it and improve on it further for your interest and knowledge.

The entire game is free for download from this link. The gameplay can be viewed from this link

 

I would never have imagined creating this game without viewing Ken’s Excel Snake game. If you guys have not seen his game I highly recommend looking at it (it’s brilliant!).

 

I directly copied two key components from his Snake game which I modified to suit this game (oh no!). The two components are the key press actions (using the left, right, up and down arrow keys) and the timer function (controlled time loops). These two components essentially make up this game and can be used to re-create any video game which involves motion/movement. These 2 components have already been explained in this video hence I will not focus on them.

 

As per all my articles I will first breakdown this game into their individual components, and then describe each component as best as I can.

The breakdown of the game is as follows;

 

Game Setup

  1. Generating a random map & position to start from

Game Play

  1. Maneuvering the vehicle using Left & Right arrow keys
  2. Speeding Up & Slowing down using the Up & Down arrow keys
  3. Hitting Traffic & Hitting the Bonus Car
  4. Generating Traffic
  5. Generating the Bonus Yellow Car
  6. Increase speed as score increases

Game Setup

Generating a random map & position to start from

 

This is a fairly straightforward step. I have set up 4 random traffic variations as seen in the image below

 

 

When “New Game” is selected, a random number between 1 and 4 is generated in order to select which variation to use. This is simply pasted on the game play area. The position of the Player’s vehicle is made an ActiveCell and the countdown begins. This code can be viewed by selecting “Module1” and subroutine “New Game”. This code can be seen below;

 

Randomize
StartPos = Int(1 + Rnd * (4 - 1 + 1))
If StartPos = 1 Then
    Range("W4:AC37").Copy
    Current = 1
ElseIf StartPos = 2 Then
    Range("AE4:AK37").Copy
    Current = 2
ElseIf StartPos = 3 Then
    Range("AM4:AS37").Copy
    Current = 2
ElseIf StartPos = 4 Then
    Range("AU4:BA37").Copy
    Current = 1
End If

Range("D4").PasteSpecial
Application.CutCopyMode = False
Range("Q13") = 0

NextPos = Int((2 - 1 + 1) * Rnd + 1)

If StartPos = 1 Then
    Range("E34").Select
ElseIf StartPos = 2 Then
    Range("I34").Select
ElseIf StartPos = 3 Then
    Range("E34").Select
ElseIf StartPos = 4 Then
    Range("I34").Select
End If

Game Play

 

We can now move to the next subroutine, “Movement”. The following three section are grouped together in a single code below.

  1. Maneuvering the vehicle using the Left & Right arrow key
  2. Speed Up & Slowing Down
  3. Hitting Traffic & Hitting Bonus Car
 
If ActiveCell.Offset(-1, 0).Interior.Color = RGB(0, 0, 0) Then
    If Range("Q9") > Range("Q13") Then
        MsgBox ("Oh No! You Crashed!" & vbNewLine & vbNewLine & "Your final score is: " & Range("Q13")), vbInformation
    Else
        MsgBox ("Wow!, New High Score!" & vbNewLine & vbNewLine & "Your final score is: " & Range("Q13")), vbInformation
        Range("Q9") = Range("Q13")
    End If
    End
ElseIf ActiveCell.Offset(-1, 0).Interior.Color = RGB(255, 215, 100) Then
    Range("Q13") = Range("Q13") + 50
    Range(ActiveCell.Offset(-3, -1), ActiveCell.Offset(-1, 1)).Interior.Color = RGB(255, 255, 255)


Else
    TimerMax = Timer() + TimeDelay
    Do While Timer() < TimerMax
        If GetAsyncKeyState(vbKeyLeft) = KeyPressed Then
            If ActiveCell.Column = 9 Then
            Sleep 2
                If Range("E34").Interior.Color = RGB(0, 0, 0) Or Range("E35").Interior.Color = RGB(0, 0, 0) _
                Or Range("E33").Interior.Color = RGB(0, 0, 0) Then
                    Range("H34:J37").Cut Destination:=Range("D34")
                    Range("E34").Select
                    If Range("Q9") > Range("Q13") Then
                        MsgBox ("Oh No! You Crashed!" & vbNewLine & vbNewLine & "Your final score is: " & Range("Q13")), vbInformation
                    Else
                        MsgBox ("Wow!, New High Score!" & vbNewLine & vbNewLine & "Your final score is: " & Range("Q13")), vbInformation
                        Range("Q9") = Range("Q13")
                    End If
                    End
                ElseIf Range("E34").Interior.Color = RGB(255, 215, 100) Or Range("E35").Interior.Color = RGB(255, 215, 100) _
                Or Range("E33").Interior.Color = RGB(255, 215, 100) Then
                    Range("D32:F35").Interior.Color = RGB(255, 255, 255)
                    If Range("E31").Interior.Color = RGB(255, 215, 100) Then
                        Range("D31:F31").Interior.Color = RGB(255, 255, 255)
                    End If
                    Range("H34:J37").Cut Destination:=Range("D34")
                    Range("E34").Select
                    Range("Q13") = Range("Q13") + 50
                Else
                    Range("H34:J37").Cut Destination:=Range("D34")
                    Range("E34").Select
                End If
            End If
        ElseIf GetAsyncKeyState(vbKeyRight) = KeyPressed Then
            If ActiveCell.Column = 5 Then
            Sleep 2
                If Range("I34").Interior.Color = RGB(0, 0, 0) Or Range("I35").Interior.Color = RGB(0, 0, 0) _
                Or Range("I33").Interior.Color = RGB(0, 0, 0) Then
                    Range("D34:F37").Cut Destination:=Range("H34")
                    Range("I34").Select
                    If Range("Q9") > Range("Q13") Then
                        MsgBox ("Oh No! You Crashed!" & vbNewLine & vbNewLine & "Your final score is: " & Range("Q13")), vbInformation
                    Else
                        MsgBox ("Wow!, New High Score!" & vbNewLine & vbNewLine & "Your final score is: " & Range("Q13")), vbInformation
                        Range("Q9") = Range("Q13")
                    End If
                    End
                ElseIf Range("I34").Interior.Color = RGB(255, 215, 100) Or Range("I35").Interior.Color = RGB(255, 215, 100) _
                Or Range("I33").Interior.Color = RGB(255, 215, 100) Then
                    Range("H32:J35").Interior.Color = RGB(255, 255, 255)
                    If Range("I31").Interior.Color = RGB(255, 215, 100) Then
                        Range("H31:J31").Interior.Color = RGB(255, 255, 255)
                    End If
                    Range("D34:F37").Cut Destination:=Range("H34")
                    Range("I34").Select
                    Range("Q13") = Range("Q13") + 50
                Else
                    Range("D34:F37").Cut Destination:=Range("H34")
                    Range("I34").Select
                End If
            End If
        ElseIf GetAsyncKeyState(vbKeyUp) = KeyPressed Then
            If ActiveCell.Column = 5 Or ActiveCell.Column = 9 Then
                Sleep 2
                TimeDelay = 0.005
            End If

        ElseIf GetAsyncKeyState(vbKeyDown) = KeyPressed Then
            If ActiveCell.Column = 5 Or ActiveCell.Column = 9 Then
                Sleep 2
                LastTimeDelay = (0.1 * (0.8 ^ SpeedCount))
                If LastTimeDelay < 0.005 Then
                    LastTimeDelay = 0.005
                End If
                TimeDelay = LastTimeDelay
            End If

        End If
    Loop

Maneuvering the vehicle using the Left & Right arrow keys

 

In order to maneuver the vehicle, you would first need to know its current position in order to ensure the player does not exceed the boundaries.

This is determined by checking the column position of the ActiveCell. The player’s vehicle can either be in column 5 or column 9 (as there are only 2 lanes).

 

If for example the current position of the vehicle is column 5, and the left arrow key is selected, the code will first check the current position of the vehicle. If the vehicle is in column 5, no changes would occur as the vehicle cannot move beyond this boundary. If however the current position was in column 9, the vehicle would shift to the left and the ActiveCell would change its position to accommodate this.

 

Vice versa for the right arrow key.

 

Speed up & Slowing down using the Up & Down arrow keys

 

Ken’s excel game has a variable called D1 which he adjusts in order to speed up the Snake. I have renamed this variable as TimeDelay. The TimeDelay variable simply acts as a delay between each loop allowing you to control the speed.

 

In the case of this game, the vehicle is sped up simply by reducing the TimeDelay variable. The up and down arrow keys are used to enable this. The smaller the TimeDelay, the faster vehicle moves.  

 

Hitting Traffic & Hitting the Bonus Car

 

In order to check if the vehicle is hitting a black car or a yellow car, the cells above and to the left/right side need to be checked after every loop.

 

In order to explain the code behind a rear-crash, the position of the cars during the rear-collision with traffic must be seen. I have attached an image below

You would notice that during the crash, the cell directly above the ActiveCell is black. Once this is understood, this becomes a fairly straightforward approach. If the cell above is black, the vehicle has crashed.

In the case of a side collision (i.e shifting from the right to the left lane and vice versa), it has to be looked a little differently.

 

The first thing to note is that IF the vehicle is on the left hand side, the center line of the traffic on the right side would be in column “I”.

 

IF the vehicle is on the right hand side, the center line of the traffic on the left side would be in column “E”.

A bit of trial and error was needed from my end in order determine at which point I should perform the check.

 

The idea is to ensure that IF the left/right arrow key is pressed, the 3 center cells of either column “I” or “E” are not black. If it is black, the vehicle will crash on key press.

 

The same procedure is performed in order to check if the player’s vehicle has collided with the bonus vehicle (yellow vehicle), but in this case, the Player gets an extra 50 points.

 

Generating Traffic

 

There are two events taking place in this step.

 

The first event is the movement of traffic during gameplay. The second event is adding new traffic as the game is ongoing.

 

The movement of traffic is handled by a simple process of cut and paste :P. Fairly straightforward right?

 

The cut and paste process is broken into 3 sections;

 

As seen from the above image, the first step moves the range of cell to the right one cell down. The second step shifts the top region one cell down and the final step simply changes the bottom region to white colour. The code is attached below.

 

    If ActiveCell.Column = 5 Then
        Range("G33:K39").Cut Destination:=Range("G34")
        Range("C3:K32").Cut Destination:=Range("C4")
        Range("C38:K39").Interior.Color = RGB(255, 255, 255)
    ElseIf ActiveCell.Column = 9 Then
        Range("C33:G38").Cut Destination:=Range("C34")
        Range("C3:K32").Cut Destination:=Range("C4")
        Range("C38:K39").Interior.Color = RGB(255, 255, 255)
    End If

 

We can now move onto the next event.

 

A point to remember when new traffic cars are being added during the gameplay;

  • When generating traffic on the opposite lane, there must be sufficient space between the left and right traffic vehicles to switch lanes. If the next car being generated is on the same side, the above requirement is not necessary.

In short, when a vehicle is placed, there has to be 5 cell spaces (if the next vehicle is on the same side) OR 10 cell spaces (if the next vehicle is on the opposite side) before the next vehicle is added. 

 

We first start by generating a random number between 1 and 2.

A count system is used to allow the traffic to move down before the next car is added in order to place these vehicles in their relevant positions.

 

If the first vehicle has a random number of 1 and the next random number is also 1. The count will only be 5 (i.e, the length of the vehicle plus one extra cell above). This will provide 1 space between each vehicle.

If the next random number is 2, the count would be 10. This will provide enough gap to maneuver between the 2 cars.

 

Generating a Bonus Car

 

The placement of the bonus vehicle is incorporated in the traffic code above. Instead of a black car, a yellow car is placed after a certain count has been achieved.

 

The appearance of a yellow car indicates an increase in speed (almost like a level up)

The code for the above 2 sections can be seen below;

 

    Count3 = Count3 + 1
    If Current = 1 Then
        If NextPos = 1 Then
            If Count3 > 4 Then
                If BonusCount > LastBonusCount Then
                    Range("W39:Y42").Copy
                    Range("D4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If

                    LastBonusCount = LastBonusCount + 1
                Else
                    Range("W4:Y7").Copy
                    Range("D4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If
                End If
            End If

        ElseIf NextPos = 2 Then
            If Count3 > 9 Then
                If BonusCount > LastBonusCount Then
                    Range("W39:Y42").Copy
                    Range("H4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If

                    LastBonusCount = LastBonusCount + 1
                Else
                    Range("W4:Y7").Copy
                    Range("H4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If
                End If
            End If
        End If

    ElseIf Current = 2 Then
        If NextPos = 1 Then
            If Count3 > 9 Then
                If BonusCount > LastBonusCount Then
                    Range("W39:Y42").Copy
                    Range("D4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If

                    LastBonusCount = LastBonusCount + 1
                Else
                    Range("W4:Y7").Copy
                    Range("D4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If
                End If
            End If


        ElseIf NextPos = 2 Then
            If Count3 > 4 Then
                If BonusCount > LastBonusCount Then
                    Range("W39:Y42").Copy
                    Range("H4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If

                    LastBonusCount = LastBonusCount + 1
                Else
                    Range("W4:Y7").Copy
                    Range("H4").PasteSpecial
                    Application.CutCopyMode = False
                    Current = NextPos
                    Randomize
                    NextPos = Int(1 + Rnd * (2 - 1 + 1))
                    Count3 = 0
                    If Range("E34").Interior.Color = RGB(255, 0, 0) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(170, 210, 140) Then
                        Range("E34").Select
                    ElseIf Range("E34").Interior.Color = RGB(0, 175, 240) Then
                        Range("E34").Select
                    Else
                        Range("I34").Select
                    End If
                End If
            End If
        End If

    End If

 

Increase Speed as Score Increases

The final portion of this game is increasing the difficulty of the game. This is easily achieved by adjusting the TimeDelay as you progress with your score.

 

    Count2 = Count2 + 1

    If Count2 = 7 Then
        Count2 = 0
        Range("Q13") = Range("Q13") + 1
    End If

    Count = Count + 1

    If Count = n Then
        SpeedCount = SpeedCount + 1
        BonusCount = BonusCount + 1
        TimeDelay = TimeDelay * 0.8
        Count = 0

        If TimeDelay < 0.05 Then
            n = Application.WorksheetFunction.Round((n * 1.2), -1)
        End If

        If TimeDelay < 0.005 Then
            TimeDelay = 0.005
        End If
    End If

 

New things I would consider if I were to re-build this game?

 

Why limit this game to a 2 lane traffic system? Maybe a 4 lane traffic system with multiple bonus vehicles (slow time down/speed time up) would be more interesting? 

 

I hope someone out there would use this as a means to generate something new and different. Whether it works or not is irrelevant, it’s more of the experience. These are just fun projects to kill time and help you build challenges to overcome.

Leave a comment

Your email address will not be published. Required fields are marked *