|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThe beloved game of falling blocks was once a very popular game, but now it has been overshadowed by a never-ending line of games that just keep coming. I first started this program last month when my Advanced Visual Basic programming course decided to re-create this classic. After spending quite a few class hours looking for code samples and brainstorming aspects of the program, the task was deemed too difficult for the class as a whole. There were many examples of the game, but none were open source or explained how the program could be built. However, I could not just let this great program go without a fight. The result is what you see before you: a basic two-player falling blocks game that encompasses all the basic functionality of the classic. This tutorial will guide you through the creation of this program from start to finish, explaining every detail possible. The ConceptYou may believe that the concept to falling blocks is very simple. However, there are many aspects that need to be covered. Some of the concepts that need to be thought of are:
This is just a sampling of all the questions that must be asked before you can start the falling blocks making process. A few of the questions can be answered with simple Google searches. For example, the official dimension of a falling blocks board is 10x20. For storing pieces, I can only think of one way to store everything... arrays... or for a more maneuverable approach, a collection. For this program, I use the The last question that can really be answered right now is the inter-mingling of the logical and graphical aspects of the program. When my programming class initially chose falling blocks, one of the goals was to move the program to XNA so the game could be transferred to the XBOX 360. Of course, this will not happen as the class has moved to a different program, but to help facilitate this I wrote this program with the logical and graphical aspects separate. This way, only the graphical layer had to be changed when the program was transferred to XNA. Logical LayerTo begin the logical layer, we must now answer the question, "How do you store pieces on the board?" As a class, we started by adding one point at a time to the board and then manipulating the four points for the movement of the pieces. This almost immediately led to cumbersome code that would inevitably lead to long and confusing code statements. To correct this issue, we decided to create a class called
Movement of the Shape ObjectAs explained in the outline above, the Dim rval As New Collection(Of Point)
Select Case direction
Case NewDirection.current
Return Me.Points
Case NewDirection.down
For Each pnt As Point In Me.Points
rval.Add(New Point(pnt.X, pnt.Y - 1))
Next
Case NewDirection.left
For Each pnt As Point In Me.Points
rval.Add(New Point(pnt.X - 1, pnt.Y))
Next
Case NewDirection.right
For Each pnt As Point In Me.Points
rval.Add(New Point(pnt.X + 1, pnt.Y))
Next
...
As you can tell, the code is fairly straightforward. If the shape moves left, you subtract 1 from the x-value of every point. If the shape moves right, you add 1 to the x-value of every point. Lastly, if the shape moves down, you subtract 1 from the y-value of every point. Keep in mind that this code is still in the logical layer where x is any value between 0 and 10 and the y value is any value between 0 and 20. The code gets a bit more difficult with the Select Case direction
...
Case NewDirection.toggleOrientation
Select Case shapeType
...
Case falling blocks.ShapeType.lshapeLeft
'toggle point is 3rd cube
Dim x, y As Integer
x = Points(2).X
y = Points(2).Y
'find the farrest point from center
If (Points(0).X = Points(3).X - 2) Then 'pointing left
'switch to up
rval.Add(New Point(x, y + 2))
rval.Add(New Point(x, y + 1))
rval.Add(New Point(x, y))
rval.Add(New Point(x - 1, y))
ElseIf (Points(0).Y = Points(3).Y + 2) Then 'pointing up
'switch to right
rval.Add(New Point(x + 2, y))
rval.Add(New Point(x + 1, y))
rval.Add(New Point(x, y))
rval.Add(New Point(x, y + 1))
ElseIf (Points(0).X = Points(3).X + 2) Then 'pointing
'right switch to down
rval.Add(New Point(x, y - 2))
rval.Add(New Point(x, y - 1))
rval.Add(New Point(x, y))
rval.Add(New Point(x + 1, y))
ElseIf (Points(0).Y = Points(3).Y - 2) Then 'pointing down
'switch to left
rval.Add(New Point(x - 2, y))
rval.Add(New Point(x - 1, y))
rval.Add(New Point(x, y))
rval.Add(New Point(x, y - 1))
End If
...
Case falling blocks.ShapeType.pyramid
'toggle point is 3rd cube
Dim x, y As Integer
x = Points(2).X
y = Points(2).Y
If (Points(0).X = x - 1) Then 'pointing left switch to up
rval.Add(New Point(x, y + 1))
rval.Add(New Point(x - 1, y))
rval.Add(New Point(x, y))
rval.Add(New Point(x + 1, y))
ElseIf (Points(0).Y = y + 1) Then 'pointing up switch to right
rval.Add(New Point(x + 1, y))
rval.Add(New Point(x, y + 1))
rval.Add(New Point(x, y))
rval.Add(New Point(x, y - 1))
ElseIf (Points(0).X = x + 1) Then 'pointing right switch to down
rval.Add(New Point(x, y - 1))
rval.Add(New Point(x - 1, y))
rval.Add(New Point(x, y))
rval.Add(New Point(x + 1, y))
ElseIf (Points(0).Y = y - 1) Then 'pointing down switch to left
rval.Add(New Point(x - 1, y))
rval.Add(New Point(x, y - 1))
rval.Add(New Point(x, y))
rval.Add(New Point(x, y + 1))
End If
...
As you can tell by this code statement, it does get a bit more complicated. I chose two code excerpts for toggling the Collision DetectionThe collision system for the shape class only handles collisions between two shape controls at one time. This is accomplished through the overloaded Public Function WillCollide(ByVal shape As Shape, ByVal direction As NewDirection)_
As Boolean
Dim pntsToCheck As Collection(Of Point) = GetNewPoints(direction)
For Each pnt As Point In shape.Points
For Each pnts As Point In pntsToCheck
If (pnt.Equals(pnts)) Then
Return True
End If
Next
Next
End Function
Public Function WillCollide(ByVal direction As NewDirection) As Boolean
Dim pntsToCheck As Collection(Of Point) = GetNewPoints(direction)
For Each pnt As Point In pntsToCheck
If (pnt.X < 0 Or pnt.X > 9 Or pnt.Y > 19 Or pnt.Y < 0) Then
Return True
End If
Next
End Function
The Game BoardAs of right now, we can answer 3 of the 7 questions stated at the beginning of the article. Through the Board user control, we can now answer the last four: "How do you store pieces on the board?", "How do you draw the different pieces on to the board?", "How do you animate the pieces?" and "How do you manage completed lines?" First off, "How do you store pieces on the board?" This is a very similar question to how to store the logical points of the shape class. The main thinking should be arrays, but the Collection(Of type) adds better functionality with regards to adding/removing/manipulating of the objects stored in it. For the The second question ,"How do you draw the different pieces on the board?" is basically the entire graphical layer. For this Board object, the paint event has been handled with the following code: Private Sub Board_Paint(ByVal sender As Object,_
ByVal e As System.Windows.Forms.PaintEventArgs)_
Handles Me.Paint
For x As Integer = 0 To 9
For y As Integer = 0 To 19
e.Graphics.DrawRectangle(Pens.Black, x * 20, y * 20, 20, 20)
Next
Next
For Each shp As Shape In boardPieces
For Each pnt As Point In shp.Points
e.Graphics.FillRectangle(New LinearGradientBrush(_
New Rectangle(0, 0, 19, 19),_
shp.Color,_ControlPaint.Light(shp.Color), _
LinearGradientMode.BackwardDiagonal), _
(9 - (9 - pnt.X)) * 20 + 1, (19 - pnt.Y) * 20 + 1, 19, 19)
Next
Next
End Sub
This code very simply loops through each point of each shape and draws a rectangle based on the logical grid location of the shapes' points onto the Animation of the Game Board PiecesThe second to the last question is a very simple question, "How do you animate the pieces?" At the moment, this program does not use the Windows Presentation Foundation of the .NET 3.0 framework, so one of the few ways to accomplish such animation is through the use of timers. For this game board, two timers have been added: Private Sub tmrMove_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)_
Handles tmrMove.Tick
If (paused) Then
Return
End If
If (currentPieceIndex = -1) Then
'initially create a piece
Dim randVal As Integer = randPieceGen.Next(0, 7)
Dim shp As Shape
Select Case randVal
Case 0
shp = New Shape(ShapeType.line)
Case ...
End Select
'set the nextPiece property
randVal = randPieceGen.Next(0, 7)
Select Case randVal
Case 0
_nextPiece = ShapeType.line
Case ...
End Select
boardPieces.Add(shp)
currentPieceIndex = boardPieces.IndexOf(shp)
ElseIf (boardPieces(currentPieceIndex).IsFrozen) Then
'if frozen then create a new piece
Dim randVal As Integer = randPieceGen.Next(0, 7)
Dim shp As Shape = New Shape(_nextPiece)
randVal = randPieceGen.Next(0, 7)
Select Case randVal
Case 0
_nextPiece = ShapeType.line
Case ...
End Select
For Each shap As Shape In boardPieces
If (shp.WillCollide(shap, NewDirection.down)) Then
Me.tmrMove.Enabled = False
RaiseEvent GameOver(Me, New EventArgs())
Return
End If
Next
boardPieces.Add(shp)
currentPieceIndex = boardPieces.IndexOf(shp)
Else
'this is the overall movement and scoring along with the arrow key methods
Dim boolCollision As Boolean = False
boolCollision = boardPieces(currentPieceIndex).WillCollide(NewDirection.down)
For Each shp As Shape In boardPieces
If (Not shp Is boardPieces(currentPieceIndex)) Then
If (Not boolCollision) Then
boolCollision = boardPieces(currentPieceIndex).WillCollide(shp,_
NewDirection.down)
End If
End If
Next
If (Not boolCollision) Then
boardPieces(currentPieceIndex).MoveDown()
score = score + 5 * level
RaiseEvent ScoreChange(Me, New EventArgs())
Else
boardPieces(currentPieceIndex).IsFrozen = True
ManageCompleteLines()
End If
End If
Me.Invalidate()
End Sub
As you should be able to see, the The third segment handles a whole bunch more. Firstly, the segment checks to see if the current piece is capable of moving down or whether a collision will occur. If a collision occurs, then a method called Managing Complete LinesAs of right now, we have gone over all the code to create a functioning falling blocks game, but what about our last question? The very last question asked during the conceptual phase of the development of falling blocks was, "How do you manage completed lines?" This method is one of the most important parts of the program. Without this method, there would be no scoring or goal to the program. Private Sub ManageCompleteLines()
Dim counter(20) As Integer
'instantiate everything
For i As Integer = 0 To 19
counter(i) = 0
Next
'get lines completed
For Each shp As Shape In boardPieces
For Each pnt As Point In shp.Points
counter(pnt.Y) = counter(pnt.Y) + 1
Next
Next
'score for every completed line
Dim lineCount As Integer = 0
For Each i As Integer In counter
If (counter(i) = 10) Then
lineCount += 1
End If
Next
score = score + (20 * level * lineCount)
RaiseEvent ScoreChange(Me, New EventArgs())
'for all completed lines remove points
For i As Integer = 0 To 19
If (counter(i) = 10) Then
Dim shapes As New Collection(Of Shape)
For Each shp As Shape In boardPieces
If (shp.IsFrozen) Then
'collect all points then remove them
Dim pntsToRemove As New Collection(Of Point)
For Ipnt As Integer = 0 To shp.Points.Count - 1
If (shp.Points(Ipnt).Y = i) Then
pntsToRemove.Add(shp.Points(Ipnt))
End If
Next
For Each pnt As Point In pntsToRemove
shp.RemovePoint(pnt)
Next
End If
Next
End If
Next
'move lines down by moving only the points of the objects
For i As Integer = 19 To 0 Step -1
If (counter(i) = 10) Then
For Each shp As Shape In boardPieces
Dim moveDown As Boolean = False
Dim pointsToMove As New Collection(Of Point)
For Each pnt As Point In shp.Points
If (pnt.Y > i) Then
pointsToMove.Add(pnt)
End If
Next
For Each pnt As Point In pointsToMove
shp.MovePointDown(pnt)
Next
'If (moveDown And shp.WillCollide(NewDirection.down) = False) Then
' shp.MoveDown()
'End If
shp.IsFrozen = True
Next
End If
Next
'if the board is completely cleared add bonus points
If (boardPieces.Count = 0) Then
score = score + 1000 * level
End If
End Sub
This code basically searches every point contained in every shape of The last Using this ControlThe Points of InterestThere are quite a few odds and ends of this program that I have not covered, but this should be enough explanation for others to begin their own falling blocks variations. Some aspects that have not been included, but would be really good features include high scores and loading/saving of games. History
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||