Drawing Pipes
Pipe Entity
Lets create a new Pipe
entity:
Not much new here except our usage of a TypeScript union type.
Pipe Positioning
Before we instantiate our pipes, I want to talk a little bit about how we're going to position them. We'll describe the positions relative to the center of the canvas - which will later be substituted for our spawn point. More importantly, relative to the y
position of the center of the canvas. I call out y
because later we'll spawn the pipes off screen in x
so they can scroll into the gameplay area.
This feels more intuitive than describing the top left corners of the images we'll be drawing. The pipes will handle the details in the draw
method.
This diagram demonstrates the positioning of the pipes:
With that, let's instantiate our pipes:
Note our positions. For the top and bottom pipe, we use the same x
position of: config.gameWidth / 2 - spriteMap.pipes.green.slice.width / 2
. Half the game width minus half the width of the slice sprite.
The y
position is very similar. Half the game height plus/minus the buffer of 50
pixels.
Note that we pass the rim sprite data that is opposite the pipe type. So when we draw a top
pipe, we draw the bottom
rim sprite, and vice versa.
Drawing a Few Helpers
Let's add a few reference lines to the canvas to help make sure we get our positioning correct.
Add this after the ground.draw(context)
within the frame
function:
You should now see three lines drawn on the canvas:
The Height Property
Before we can finally draw the pipes, we need to know the height of the pipe. We have all the information needed, we just need to determine and store the value in the Pipe
class:
When we are drawing the top
pipe, we want the height to be the distance from the top of the canvas (0) to the y
position. Or just position.y
, since that's the same value.
When we are drawing the bottom
pipe, we want the height to be the distance from the bottom of the canvas (config.gameHeight
) to the y
position, so we subtract.
The reference lines we drew earlier can help you visualize that distance. We can finally draw the pipes!
Drawing the Pipe Rims
Let's add our pipe draw calls to the frame
function:
We haven't done anything to draw the pipes yet. We'll add the logic in the draw
method next.
Let's start with drawing the proper rim of the pipe.
Let focus on the logic for the destination y
position of the draw call. The rest we've seen before.
We draw the rim of the pipe at a slightly different y
position, based on the type
. When the type
is "top"
, we need to offset the position of the draw call by the height of the rim sprite: height - rimSpriteData.height
. When the type
is "bottom"
, we don't need to perform any changes.
Recall that the destination position we provide to drawImage
is the top left corner of the image, that's why we perform this adjustment.
If you look at the canvas, you should see the pipe rims drawn at the correct positions. Right in line with our reference lines.
Drawing the Pipe Body
Lastly we need to draw the body of the pipe.
Let's start with the top. We need the body of the pipe to be drawn from a y
position of 0
, and a hieght of pipe height
minus rimSpriteData.height
. Try removing - this.rimSpriteData.height
and you'll see how the body is now in front of the pipe rim. Drawing the body last was intentional to help callout potential rendering issues overlapping the rim.
When we draw the bottom, we need the body to be drawn from the y
position and offset positively (down) by rimSpriteData.height
with a height of pipe height
minus rimSpriteData.height
. Technically we don't need to subtract the rimSpriteData.height
from the destination height. It would just draw off the bottom of the canvas, but we'll leave it for consistency sake.
Lets move on to adding some colliders.