“This is the sixth day of my participation in the August Challenge. For details, see [August Challenge]”

Based API learning canvas looked a lot these days, also with a lot of, here to do a summary of practice, do a slightly more interesting: meow microphones jump cake little game, to consolidate summary canvas study again, and this is my very favorite game, but because of personal canvas is a beginner level, so I can only make a simple version.

Basic feeling almost, advanced words do not know how to do, look at the open source library. Without further ado, let’s take a look at the renderings:

PC/ mobile online demo address (Github Page needs proxy, otherwise access may be slow or impossible) :

Play: Play online

Because the contact time of canvas is short, the working time is not long, and there is also a lack of materials, so the effect is very general, but it is barely qualified, at least can have the basic game logic.

start

There is no new API for drawing text, but there is a new API for drawing text. The API works very well, and strokeText works very well.

Let’s start with the four parameters of fillText:

parameter role
text Specifies the text to be output on the canvas.
x Start drawing the x-coordinate position of the text (relative to the canvas).
y Start drawing the y-coordinate position of the text (relative to the canvas).
maxWidth* Optional. The maximum allowable text width, in pixels.
ctx.font = '30px 'Microsoft Yahei'
ctx.fillText('Hello'.30.50)
Copy the code

In fact, using an API like fillText, we can use more than just these parameters. We can also modify the w text by using fillStyle, font, etc. For example, the useful textAlign property I’m showing you now: Interestingly, the left and right properties of textAlign are divided by coordinate points. A graph to understand this property:

For more specific attributes, see the text related API introduction: Canvas text APIMDN Document.

Kitty jumps the cake

Next, I’m going to talk about my idea of using Canvas for my Kitty hop cake mini game. In fact, IN my opinion, many canvas apis are not difficult to use alone, but the key is to combine them to complete a certain thing, which is quite difficult. Some of them also require mathematical knowledge. So without further ado, let’s see how Kitty jumps the cake.

elements

The cat hop cake mini game is like this:

  1. A cat can click and jump and then free fall;
  2. The cake moves from one side of the border to the other;
  3. If the cat is free-falling, it will stack up and die, Game Over!

So, the idea is basically clear: draw the cat and the cake, and then implement collision detection to calculate the new position!

Creating a square map

Why do I like to do some Demo, I like to draw a square map first, because it is convenient to observe and calculate the point of object painting, for just learning me, it is very useful.

initBoard(len) {
    const num = Math.floor(this.cvs.width / len)
    for (let i = num ; i >= 0 ; i --) {
      this.ctx.beginPath()
      this.ctx.strokeStyle = '#ccc'
      this.ctx.lineTo(i * len, 0)
      this.ctx.lineTo(i * len, this.cvs.width)
      this.ctx.stroke()
      this.ctx.beginPath()
      this.ctx.lineTo(0, i * len)
      this.ctx.lineTo(this.cvs.width, i * len)
      this.ctx.stroke()
    }
}
Copy the code

The squares can be smaller, and by looking at the squares, it’s easier to set the size, position, and speed of the object (based on how many squares).

Creating a Role is a kitten

The creation of the kitten is very simple, because the kitten’s Y-axis direction will not change except because of the cake stacking, so we just need to draw the cat at the beginning, and then you dynamically increase the Y value.

this.role = { x: 0.y: 0.t: 0 }
this.initRole(this.cvs.width - 10)
Copy the code
initRole(y) {
    this.role.x = this.cvs.width / 2
    this.role.y = y
    this.role.t = this.cvs.width - y
}
paintRole() {
    this.ctx.beginPath()
    this.ctx.arc(this.role.x - 10.this.role.y, 10.0.2 * Math.PI)
    this.ctx.fillStyle = 'red'
    this.ctx.fill()
}
Copy the code

Here I’m going to use this little ball to represent our Role cat, and you can see that cat is already in the middle of the bottom, and now you need to click and jump, and there are many ways to do this, but I’ll just do it in a simple way:

paintRole() {
    this.ctx.beginPath()
    if (this.isClick) {
      // Determine if the character has reached the landing spot (the landing spot may be on a block)
      if (this.role.y >= this.cvs.width - this.role.t && this.type === 'down') {
        this.speed = 2.8
        this.role.y = this.cvs.width - this.role.t
        this.isClick = false
        this.type = 'up'
        return
      }
      this.role.y -= this.speed
      if (this.speed <= 0) {
        this.type = 'down'
      } else {
        this.type = 'up'
      }
      this.speed -= 1.
    }
    this.ctx.arc(this.role.x - 10.this.role.y, 10.0.2 * Math.PI)
    this.ctx.fillStyle = 'red'
    this.ctx.fill()
}
Copy the code

Of course, paintRole will keep redrawing through the timer for a while, and the code logic will not stick. The main idea here is:

When I click on it, it decelerates up, and then it accelerates down again, so I’m just going to use speed here;

Then, when the ball drops again, that is, when the type is down, we need to calculate the current position of the ball and the position of the landing place (here we use the property T to record, because the landing place may be different because of cake stacking, so we save it separately), and then reset the state after arriving at the landing place.

Create the cake and move it

It’s easy to create the cake from side to side, starting with the simple case: the cake just changes the position of the X-axis, so we can increase the value of X each time we draw.

paintCake() {
    if (this.cake.x >= this.cvs.width) {
      this.cake.x = -this.cake.w
    }
    this.cake.x ++
    this.ctx.beginPath()
    this.ctx.fillStyle = 'skyblue'
    this.ctx.fillRect(this.cake.x, this.cake.y, this.cake.w, this.cake.h)
}
Copy the code

So now we’re going to move the block, and now we’re going to implement the detection mechanism, which is a little bit more complicated here, because not only is the ball going to hit it, but the falling body is going to add up when it steps on the cake.

Collision detection

Collision detection is still quite troublesome, here is the idea:

  1. When the ball falls, it’s within the scope of the cakeThe X axisIn the range, whenYThe distance is greater than or equal to the cakeYWhen the point is stepping on the cake, other collisions are failure.
  2. When the ball stepped on the cake, we had to record the current position of the cake that was stepped on and introducerecordObject array to record;
  3. Because the cake is stacked, reset where the ball is and where the cake appearsYPoints.

Look at a simple implementation of the code:

check() {
    if (
      this.role.x + 5> =this.cake.x &&
      this.role.x - 5< =this.cake.x + this.cake.w && 
      this.role.y >= this.cake.y
    ) {
      if (this.type === 'down') {
        if (this.cakes.length) {
          // Increase the range of decisions to facilitate overlapping cakes
          const last = this.cakes[this.cakes.length - 1]
          const range = 5
          if((this.cake.x > last.x - range && this.cake.x < last.x + range) ||
            (this.cake.x > last.x + last.w - range && this.cake.x < last.x + last.w + range)
          ) {
            this.scoreRatio += 1
            this.cake.x = last.x
            this.paintRatioText()
          } else {
            this.scoreRatio = 1}}this.moveEnd = true
        this.score += this.scoreRatio
        this.cakes.push({ x: this.cake.x, y: this.cake.y })
        this.initCake(this.cake.y - 10)
        this.initRole(this.role.y - 10)}else {
        this.ctx.font = '30px 'Microsoft Yahei'
        this.ctx.textAlign = 'center'
        this.ctx.fillStyle = 'red'
        this.ctx.fillText('Game Over! '.this.cvs.width / 2.this.cvs.width / 2)
        clearTimeout(this.timer)
        this.timer = null
        this.end = true}}}Copy the code

Because when you step on the cake, if it’s in the same position, it’s going to get higher and higher, but when you do it by 1px, it’s not going to overlap completely, so I set a range; Here I introduce a range value, as long as the difference between the current position and the previous position is within this range, the two cakes will be counted as overlapping, and appropriate combo hint:

// Draw a combo
paintRatioText() {
    this.showRatioText = true
    this.ctx.font = '15px 'Microsoft Yahei'
    this.ctx.textAlign = 'left'
    this.ctx.fillStyle = 'red'
    this.ctx.fillText('X' + this.scoreRatio, this.role.x + 30.this.role.y - 15)
    setTimeout(() = > {
      this.showRatioText = false
    }, 1e3)}Copy the code
// Redraw the record, that is, to redraw the cake in the step
paintReocrd() {
    this.cakes.forEach(cake= > {
      this.ctx.beginPath()
      this.ctx.fillStyle = 'orange'
      this.ctx.fillRect(cake.x, cake.y, this.cake.w, this.cake.h)
    })
}
Copy the code

Reduce the height

Since the cakes will overlap one by one, we need to lower the height of the cake after a certain height. There are two ways:

  1. Delete the element from the array directly;
  2. Take the objects in the arrayYThe value is changed;

The second method I’m using here, because I just did it without thinking too much…

// Move the cake so that the stack is not too high out of range
check(){...if(this.cakes.length && this.cakes.length % 10= = =0 && this.moveEnd) {
        this.moving = true
        this.moveEnd = false
        setTimeout(() = > {
        this.moving = false
        this.cake.y += this.movingY
        this.role.t -= this.movingY
        this.movingY = 0
        }, 550)}this.initCake(this.cake.y - 10)
    this.initRole(this.role.y - 10)... }Copy the code
initRole(y) {
    this.role.x = this.cvs.width / 2
    this.role.y = y
    this.role.t = this.cvs.width - y
}
initCake(y) {
    this.cake.x = -this.cake.w
    this.cake.y = y
}
// Move the cake
moveCake() {
    const y = this.cakes.length > 10 ? 2 : 1
    this.cakes = this.cakes.map(cake= > {
      return {
        ...cake,
        y: cake.y + y
      }
    })
}

// Move the character
moveRole() {
    const y = this.cakes.length > 10 ? 2 : 1
    this.movingY += y
    this.role.y += y
}
Copy the code

Here I save by the state moving: whether the cake is in the downward state, when the downward state is finished, the new cake will be redrawn. MovingY is then used to save the downward distance, bringing the ball out of sync with the y-wheelbase of the current cake.

At this point, the basic logic of the game is largely complete, and all that remains is some refinement.

To optimize the

For example, we can replace the ball with the cake and remove the checkered background.

In the case of meow, we can also find a set of material, here I found a jumping and a sitting, so that the movement of the jumping free fall does not look monotonous, it seems to have a rich character.

Pictures, or need to be pre-loaded, otherwise without pictures to start the game logic is silly.

You can also add some cakes according to the change of speed and become faster, out of the direction of different gameplay, so that the game is more rich.

Add special effects to combos if you can, and even websockets to make them matchable, but that’s for later.

conclusion

The game is simple and the effect is very general, maybe because I did not deal with some of the logic of the painting too well, so it seems that the function is relatively normal, but the vision is poor.

However, after a few days, the entry of Canvas should still be started. The use of APIS is still very simple, but it is difficult to complete a certain function through the combination of these apis.

So, I think canvas is fun. I think first of all, I’m good at code design, and on the other hand, I’m good at math. Otherwise, I can’t calculate the width of coordinates and so on.

Follow-up words do not know how to advance, too difficult to make, energy is not enough, but also to learn Vue dinner, and then to understand the open source library.

Look at the effect again:

Play: Play online.

Get the title: New Canvas Player!