Welcome back.  We are going to have vertical scrolling here.
Before we get into all that, first, we are going to add editor support for platforms and walls past the old
horizontal movement boundary-that-we-got-rid-of in the previous episode.

So, let's go ahead and change the TESTversion to 1.
Next, we'll go on down to the MouseUp event, which is responsible for placing the newly created platforms, lava, etc.
In this procedure, there are eight locations where I place an object based on the X location of where the mouse was clicked.
At each of these locations, we must add ScreenLeft to get the object in the right location.
See if you can find all eight without looking at the solution below.  OK, the solution is too close to the question for it
to be difficult, but give it a try.
                Placed = Rectangle.FromLTRB(Math.Min(InitialPt.X, Mu.X) + ScreenLeft, Math.Min(InitialPt.Y, Mu.Y), Math.Max(InitialPt.X, Mu.X) + ScreenLeft, Math.Max(InitialPt.Y, Mu.Y))
                'For platforms.
                Placed = Rectangle.FromLTRB(Math.Min(InitialPt.X, Mu.X) + ScreenLeft, Math.Min(InitialPt.Y, Mu.Y), Math.Max(InitialPt.X, Mu.X) + ScreenLeft, Math.Max(InitialPt.Y, Mu.Y))
                'For walls.
                Placed = Rectangle.FromLTRB(Math.Min(InitialPt.X, Mu.X) + ScreenLeft, Math.Min(InitialPt.Y, Mu.Y), Math.Max(InitialPt.X, Mu.X) + ScreenLeft, Math.Max(InitialPt.Y, Mu.Y))
                'For lava.
                Coins.Insert(0, New Rectangle(Mu.X + ScreenLeft, Mu.Y, COINSIZE, COINSIZE))
                'Place a coin at the point where the mouse was released.
                Fltr.Loc = New Rectangle(Mu.X + ScreenLeft, Mu.Y, FLOATERSIZE, FLOATERSIZE)
                'For the floater...
When you find all of these occurences, then this part is complete. (This section should appear in Section 16, where it belongs, after a while.)
Now, we'll get our player position to be stored in the file. First, we'll work on the SaveLevel so that we can save our old maps with this new information. In our maps, we will save the player's startup location as the first two numbers in the file. We'll take the player's current location as the startup position. We cannot just save this information. If the player starts out on a platform or a wall, we need to know the end boundaries of the platform. So, we also need to store LeftEnd and RightEnd in the file. In our SaveLevel, we will do this immediately after we create the BinaryWriter:
            'At the head of the file, we will write the player's startup location.
            BW.Write(PlayerLoc.Left)
            BW.Write(PlayerLoc.Top)
            'Also, the ending points for the platform the player is on.
            BW.Write(LeftEnd)
            BW.Write(RightEnd)
And this will save the startup location of our player. Now, save all of your existing level files with the player's startup location.
When you are finished saving all of your levels, we begin setting up the program so that we can load these levels. First, we'll go to the LoadLevel routine so that we properly load these parameters for our level. This will come immediately after the creation of the BinaryReader.
        PlayerLoc = New Rectangle(BR.ReadInt32(), BR.ReadInt32(), CHARWIDTH, CHARHEIGHT)
        'Read the player location.
        LeftEnd = BR.ReadInt32() : RightEnd = BR.ReadInt32()
        'And the ending points of the current platform.
Then, in the Load event, we need to keep these values from being overwritten. So, we remove the following lines from the Load procedure.
        LeftEnd = 0 : RightEnd = LEVELWIDTH
        'Set the current end points.
        PlayerLoc = New Rectangle(100, LANDHEIGHT - CHARHEIGHT, CHARWIDTH, CHARHEIGHT)
        'Instantiate the player location.
The player can now begin the level anywhere. The scrolling screen is updated in the CharacterMovement sub, so the screen will start up centering the player.
Due to the interests of vertical scrolling... and level holes, we now have to get rid of our beloved grassy land at the bottom of the screen. First, we have to get rid of the check we make while the player is falling and if it reaches this point. That looks like this:
                If PlayerLoc.Bottom >= Me.LANDHEIGHT Then
                    'Check if we are touching the ground (another RECT advantage).

                    IsJumping = False
                    'Stop the jump: we have hit the ground.
                    LeftEnd = 0 : RightEnd = LEVELWIDTH
                    'Set the endpoints for the ground.
                    AnimCycler = 0
                    'Reset the animation cycler: it has been counting during the jump.
                    PlayerLoc.Offset(0, LANDHEIGHT - PlayerLoc.Bottom)
                    'Move the player to stand on the land properly.
                Else
Don't forget the corresponding End If. The player will now fall into the abyss instead of landing on the grass. Next, we keep this grass from even being drawn, which requires us to just remove this part.
        'And the remainder of the area is LAND, starting from LANDHEIGHT.
        GFX.FillRectangle(Brushes.ForestGreen, Rectangle.FromLTRB(0, LANDHEIGHT, MAPWIDTH, MAPHEIGHT))
And now, the line above this one in the file for drawing the sky has to change.
        'I am going to draw a lightblue rectangle for the sky.
        'It will go down until it reaches the land, so therefore the bottom of the rectangle has to be
        'the LANDHEIGHT.
        GFX.FillRectangle(Brushes.LightSkyBlue, Rectangle.FromLTRB(0, 0, MAPWIDTH, LANDHEIGHT))
Specifically, the LANDHEIGHT has to change to MAPHEIGHT
        'I am going to draw a lightblue rectangle for the sky.
        'It will fill the whole screen.
        GFX.FillRectangle(Brushes.LightSkyBlue, Rectangle.FromLTRB(0, 0, MAPWIDTH, MAPHEIGHT))
And now, for the hole effect (no pun intended), we will perform a check in IsJumping that will kill the player if he jumps past the bottom part of the screen. So, in CharacterMovement, we peruse the IsJumping block. We spot the old place where our land location used to be. This looks like an excellent spot to set up our falling doom check. However, we are checking for platforms after this, so this check would probably be better suited after we check all of our walls and platforms.
                If PlayerLoc.Bottom > MAPHEIGHT Then
                    'The character has fallen off the screen and can't get up.
                    IsGoner = True
                    'So, the character is dead.
                    PlayerVeloc = SBARINITIALVELOCITY
                End If
Now, there is certain doom from falling off the screen.
For vertical scrolling, we do the same thing we did for horizontal scrolling. However, due to our recently installed death trap at the bottom of the screen, we have to build up, which means that our variable (add it):
    Dim ScreenTop As Integer
    'Where the display's top end is on the map.
will remain 0 or less, opposing the ScreenLeft, which is 0 or greater. Let's go ahead and implement this in our CharacterMovement alongside of the ScreenLeft implementation. We first need to declare the constant matching with SCREENOFFSET.
    Const VERTICOFFSET As Integer = (MAPHEIGHT >> 1) - (CHARHEIGHT >> 1)
    'Distance from the player's top edge to top edge of scrolling screen.
The implementation, just like in the horizontal scrolling case, is like the following:
        ScreenTop = PlayerLoc.Y - VERTICOFFSET
        'Same for the top end of screen.
        If ScreenTop > 0 Then
            ScreenTop = 0
            'The screen top coordinate will be non-positive.
        End If
Of course, in the Artwork section, we will have to implement this alongside the ScreenLeft subtractions. So, for example, our .Offset for the platforms is now like this: Platform.Offset(-ScreenLeft, -ScreenTop) And the matching positive: Platform.Offset(ScreenLeft, ScreenTop) This should be done for the walls, coins, lava, and floaters. Also, in the character drawing section, the five places where we subtracted screenleft now have to be accompanied by subtracting screentop to the respective Y arguments. Now, the screen should scroll upwards.
Before I forget, we now need to update the map editing process so that we can add platforms and walls to this section. We have never defined the upper limit to how high the player can go, so we have nothing that we need to remove like we did for the walking section. The edition process needs tweaking right in the same area we just tweaked for the horizontal scrolling. So, in the MouseUp event (set TESTversion to 1 if you need to), add + ScreenTop to the eight places that we added the ScreenLeft. This should unleash worlds of scrollable opportunity to you. Have fun. (And no, we're not done yet). New version of character drawing in the Artwork sub.
        If IsGoner Then

            If Doompause >= DOOMTIMEOUT Then
                GFX.TranslateTransform(Convert.ToSingle(PlayerLoc.X) + (Me.CHARWIDTH >> 1) - ScreenLeft, Convert.ToSingle(PlayerLoc.Y) + (Me.CHARWIDTH >> 1) - ScreenTop)
                'Move the center point to the center of the character's midpoint.
                GFX.RotateTransform(RotateAngle)
                'Spin the character clockwise.
                RotateAngle += SPINOUTSPEED

                If IsJumping Then
                    GFX.DrawImage(PlayerBmp, -Me.CHARWIDTH >> 1, -Me.CHARHEIGHT >> 1, New Rectangle((CHARWIDTH * WALKINGFRAMECOUNT << 1) + CharDirec * CHARWIDTH, 0, CHARWIDTH, CHARHEIGHT), GraphicsUnit.Pixel)
                    'Draw the character centered on the point 0, 0
                Else
                    GFX.DrawImage(PlayerBmp, -Me.CHARWIDTH >> 1, -Me.CHARHEIGHT >> 1, New Rectangle(CHARWIDTH * CharDirec * WALKINGFRAMECOUNT + AnimCycler * CHARWIDTH, 0, CHARWIDTH, CHARHEIGHT), GraphicsUnit.Pixel)
                    'This draws the character to the display, based on the current character location, frame, and state.
                End If

                PlayerLoc.Offset(0, PlayerVeloc + GRAVITY >> 1)
                'Gravity-based calculations that we have already done in CharacterMovement.
                PlayerVeloc += GRAVITY

                GFX.ResetTransform()
            Else

                RotateAngle = 0.0F
                'Reset the rotateangle for our tumbling-spinning character.

                Doompause += 1
                'Increment the delay counter.
                If IsJumping Then
                    GFX.DrawImage(PlayerBmp, PlayerLoc.X - ScreenLeft, PlayerLoc.Y - ScreenTop, New Rectangle((CHARWIDTH * WALKINGFRAMECOUNT << 1) + CharDirec * CHARWIDTH, 0, CHARWIDTH, CHARHEIGHT), GraphicsUnit.Pixel)
                    'Jumping draw only.
                Else
                    GFX.DrawImage(PlayerBmp, PlayerLoc.X - ScreenLeft, PlayerLoc.Y - ScreenTop, New Rectangle(CHARWIDTH * CharDirec * WALKINGFRAMECOUNT + AnimCycler * CHARWIDTH, 0, CHARWIDTH, CHARHEIGHT), GraphicsUnit.Pixel)
                    'This draws the character to the display, based on the current character location, frame, and state.
                End If

            End If

        Else
            If IsJumping Then
                GFX.DrawImage(PlayerBmp, PlayerLoc.X - ScreenLeft, PlayerLoc.Y - ScreenTop, New Rectangle((CHARWIDTH * WALKINGFRAMECOUNT << 1) + CharDirec * CHARWIDTH, 0, CHARWIDTH, CHARHEIGHT), GraphicsUnit.Pixel)
                'Jumping draw only.
            Else
                GFX.DrawImage(PlayerBmp, PlayerLoc.X - ScreenLeft, PlayerLoc.Y - ScreenTop, New Rectangle(CHARWIDTH * CharDirec * WALKINGFRAMECOUNT + AnimCycler * CHARWIDTH, 0, CHARWIDTH, CHARHEIGHT), GraphicsUnit.Pixel)
                'This draws the character to the display, based on the current character location, frame, and state.
            End If

        End If
New MouseUp object adding code:
        Select Case MDObject
            Case "P"c
                Placed = Rectangle.FromLTRB(Math.Min(InitialPt.X, Mu.X) + ScreenLeft, Math.Min(InitialPt.Y, Mu.Y) + ScreenTop, Math.Max(InitialPt.X, Mu.X) + ScreenLeft, Math.Max(InitialPt.Y, Mu.Y) + ScreenTop)
                'For platforms.
                Platforms.Insert(0, Placed)
                'Add a platform.
            Case "W"c
                Placed = Rectangle.FromLTRB(Math.Min(InitialPt.X, Mu.X) + ScreenLeft, Math.Min(InitialPt.Y, Mu.Y) + ScreenTop, Math.Max(InitialPt.X, Mu.X) + ScreenLeft, Math.Max(InitialPt.Y, Mu.Y) + ScreenTop)
                'For walls.
                Walls.Insert(0, Placed)
                'Add a wall.
            Case "L"c
                Placed = Rectangle.FromLTRB(Math.Min(InitialPt.X, Mu.X) + ScreenLeft, Math.Min(InitialPt.Y, Mu.Y) + ScreenTop, Math.Max(InitialPt.X, Mu.X) + ScreenLeft, Math.Max(InitialPt.Y, Mu.Y) + ScreenTop)
                'For lava.
                Lavas.Insert(0, Placed)
                'Add a wall.
            Case "C"c
                Coins.Insert(0, New Rectangle(Mu.X + ScreenLeft, Mu.Y + ScreenTop, COINSIZE, COINSIZE))
                'Place a coin at the point where the mouse was released.
            Case "F"c
                Clock.Enabled = False
                'Lighten the processing load while we set up our floater.
                Dim Fm As FloaterEd = New FloaterEd
                Fm.ShowDialog()
                'After the showdialog, the floater's final six properties will be set.
                'But, the rectangle will be empty.
                Dim Fltr As LevFloater = Fm.Floater
                'Copy the floater to our variable.
                Fltr.Loc = New Rectangle(Mu.X + ScreenLeft, Mu.Y + ScreenTop, FLOATERSIZE, FLOATERSIZE)
                'For the floater...
                'Set the rectangle.  The size is fixed, so we just make the rectangle like we did for the coin.
                Floaters.Insert(0, Fltr)
                'And this final floater will be added.
                'The movement of the floater after it is added to the form is not the exact movement that it will have
                'when it is loaded from file, because the value of FloatTick is not always 0 when the floater is created, and its
                'movement depends on the value of floattick with the periodic function.
                Clock.Enabled = True
                'Continue game.
        End Select