On this episode, we will be fixing our Floaters so that they have more functionality. Of course, this will come at a price: the
floaters have to have their own structure, and all important members of this structure will have to be saved to and loaded from
file when we save them.
Now, a Floater that moves in a circle is fine, but it would be nice if it moved in other shapes as well. This, of
course, requires us to make more periodic functions and, since we cannot store a function into a variable, we'll have to have a
number to represent which function to use. This screams "Enumeration", so we'll use an Enumeration to determine which function
to use. Of course, with all periodic functions, we need to determine an origin, a radius, and a frequency. The origin is just
the initial position of the floater, which can be represented as the initial rectangle values, so the origin can be omitted. Our
periodic functions are defining how much the floater moves during each frame, so we'll just call the radius a radispeed (or
whatever). Since there will be a periodic function for horizontal and another for vertical movement (right now we're using
Cos for horizontal and Sin for vertical), we need to have a pair of enumeration variables, a pair of radispeed, and of frequency.
In order to make frequency values be stored in an integer, we will be dividing the frequency by a number like 64 or 128, so
that we can have a value that represents a frequency of 1, and lower numbers will represent a frequency that is less than 1.
Side Note: Since our periodic functions are defining how much the floater moves each frame, the actual radius (let's call it r)
is multiplied by the frequency of the periodic function (let's say f) to give us the new coefficient (radispeed) of the
periodic function (s, let's say).
In our last example, the coefficient which multiplied the sine and cosine was 5 and the frequency is 1/8, so the actual radius
of the floater here is s/f = r. So 5 divided by one-eighth (not divided by eight) gives us 40.
Now, let's get to declaring the floater function enumeration and structure. First, the enumeration:
Public Enum PeriodicDesignation
Stable = 0 'Denotes that the object will not move in this direction.
CosineFxn 'Moves using the cosine function.
SineFxn 'Moves using the sine function.
End Enum |
We just have three periodic functions that we can use, but when we get more, we'll add them here.
Now, the structure:
Public Structure LevFloater
Private rect As Rectangle
Private horizfxn As PeriodicDesignation
Private vertfxn As PeriodicDesignation
Private xrspeed As Integer
Private yrspeed As Integer
Private xfreq As Integer
Private yfreq As Integer
End Structure |
These are the private members. Now we get to add some property procedures to access these private variables. Why didn't I
just make them Public variables? Because I want to get the gray icon for property for each of these items that I want to
access. Rectangles have them, so why not us too?
Public Property Loc() As Rectangle
Get
Return rect
End Get
Set(ByVal Value As Rectangle)
rect = Value
End Set
End Property
Public Property FunctionHorizontal() As PeriodicDesignation
Get
Return horizfxn
End Get
Set(ByVal Value As PeriodicDesignation)
horizfxn = Value
End Set
End Property
Public Property FunctionVertical() As PeriodicDesignation
Get
Return vertfxn
End Get
Set(ByVal Value As PeriodicDesignation)
vertfxn = Value
End Set
End Property
Public Property RadispeedX() As Integer
Get
Return xrspeed
End Get
Set(ByVal Value As Integer)
xrspeed = Value
End Set
End Property
Public Property RadispeedY() As Integer
Get
Return yrspeed
End Get
Set(ByVal Value As Integer)
yrspeed = Value
End Set
End Property
Public Property FrequencyX() As Integer
Get
Return xfreq
End Get
Set(ByVal Value As Integer)
xfreq = Value
End Set
End Property
Public Property FrequencyY() As Integer
Get
Return yfreq
End Get
Set(ByVal Value As Integer)
yfreq = Value
End Set
End Property |
Wow. Thank goodness for intellisense. That looks like it took quite a while to write, but it only took about 30 seconds.
Now, of course, I have to add a Sub New to initialize all of these properties (it's a habit now).
Public Sub New(ByVal R As Rectangle, ByVal RadispdX As Integer, ByVal RadispdY As Integer, ByVal FreqX As Integer, ByVal FreqY As Integer, ByVal HFxn As PeriodicDesignation, ByVal VFxn As PeriodicDesignation)
Me.horizfxn = HFxn
Me.rect = R
Me.vertfxn = VFxn
Me.xfreq = FreqX
Me.xrspeed = RadispdX
Me.yfreq = FreqY
Me.yrspeed = RadispdY
End Sub |
We will also need a sub that will offset the floater's rectangle, because we cannot use Offset on the property itself.
Public Sub Offset(ByVal dX As Integer, ByVal dY As Integer)
rect.Offset(dX, dY)
End Sub |
And that concludes our structure for now. Next, we need to get our floater to work on this structure. So,
back to our form class, where we change our declaration.
Dim Floater As LevFloater
'This is the floater's location and movement information on the field. |
And a constant.
Const FREQUENCYDIVIDER As Integer = 128
'The number that will divide the frequency of periodic functions. |
And, of course, we have to change the code everywhere where we have used the original floater when it was a rectangle.
In the Load event:
Floater = New LevFloater(New Rectangle(420, 400, FLOATERSIZE, FLOATERSIZE), 5, 5, 16, 16, PeriodicDesignation.CosineFxn, PeriodicDesignation.SineFxn)
'Information about the floater. The width and height are all determined by the floatersize constant. |
Artwork:
GFX.DrawImage(FloatBmp, Floater.Loc.X, Floater.Loc.Y)
'Draw the floater to the display. There's only one frame, so no selection involved. |
In CharacterMovement:
If Floater.Loc.IntersectsWith(PlayerLoc) Then
IsGoner = True
'The player has touched the floater. Lose a life.
PlayerVeloc = SBARINITIALVELOCITY
End If |
And now, the culmination of all of the structure building that we discussed earlier.
First, let's declare two variables in this CharacterMovement subroutine.
Dim dX, dY As Integer
'Indicates how much an object moves during this frame. |
And now, replacing our original floater moving code:
Select Case Floater.FunctionHorizontal
'Looking at the function that we have designated, we choose the proper periodic function for setting to dX and dY.
Case PeriodicDesignation.CosineFxn
dX = Convert.ToInt32(Floater.RadispeedX * Math.Cos(Convert.ToDouble(FloatTick) * Floater.FrequencyX / Me.FREQUENCYDIVIDER))
Case PeriodicDesignation.SineFxn
dX = Convert.ToInt32(Floater.RadispeedX * Math.Cos(Convert.ToDouble(FloatTick) * Floater.FrequencyX / Me.FREQUENCYDIVIDER))
Case PeriodicDesignation.Stable
dX = 0
End Select
Select Case Floater.FunctionVertical
'Looking at the function that we have designated, we choose the proper periodic function for setting to dX and dY.
Case PeriodicDesignation.CosineFxn
dY = Convert.ToInt32(Floater.RadispeedY * Math.Cos(Convert.ToDouble(FloatTick) * Floater.FrequencyY / Me.FREQUENCYDIVIDER))
Case PeriodicDesignation.SineFxn
dY = Convert.ToInt32(Floater.RadispeedY * Math.Cos(Convert.ToDouble(FloatTick) * Floater.FrequencyY / Me.FREQUENCYDIVIDER))
Case PeriodicDesignation.Stable
dY = 0
End Select
Floater.Offset(dX, dY)
'Move the floater using our offset subroutine. |
And now, our Floater moves in a circle (again), but now, you can customize the floater a bit by assigning different
radispeeds, frequencies, and periodic functions. It will be even more customizable when we add different periodic functions.
On the next episode, we'll add the floater information to the file.