Egret Mini Game Practical Tutorial Jump Jump (Handling 2)

Start page logic

Since the start page is relatively monotonous, there is only one button to start the game, so we only need to add an event listener to the button

The specific logic is that when the touch event occurs, we will add the SceneGame to the stage and remove the BeginScene from the stage

The code of SceneBegin.ts is as follows:

public beginBtn:eui.Button;
private init() {
    // here once In fact, it is addEventListner , except that it only listens once
    this.beginBtn.once(egret.TouchEvent.TOUCH_TAP, this.start, this);
private start() {
    // Add a game scene to the stage
    this.parent.addChild(new SceneGame());
    // Remove the initial scene from the Stage

The logic of the game page

Now we can jump to the game interface by clicking the start button. The next step is the game logic of the tall, which is also the essence of this article. Roll up your sleeves and come on!

variable declaration

Here we need to declare a bunch of variables in SceneGame.ts, you can go to the source code at the end of the article for details, not all of them will be copied

// SceneGame.ts The main variable declaration in
// The current box (the newest box, the target box that you are going to jump over)
private currentBlock: eui.Image;
// next box direction (1 right,-1 left)
public direction: number = 1;
// tanθ angle value (you can fine-tune it yourself), and direction Coordinate to calculate the coordinates of the next box
public tanAngle: number = 0.556047197640118;
// max min (horizontal) distance of random boxes
private minDistance = 220;
private maxDistance = 320;
// The distance of the jump (that is, calculated according to the time you press), here refers to the distance in the horizontal direction
public jumpDistance: number = 0;
// Left jump point (fixed, you can fine-tune it yourself)
private leftOrigin = { "x": 180, "y": 350 };
// Right jump point (fixed, you can fine-tune it yourself)
private rightOrigin = { "x": 505, "y": 350 };

Initialize the interface

First, we need to see what the initial interface of the game looks like before we know what to write in the init function:

As can be seen from the above picture, all we have to do is to generate squares, and then set the position of the villain and the square.

It should be noted that the position of the first square and the villain is fixed, at the bottom left; the default direction of the first jump is the right, and the second square is also on the right

Why do you need to fix the initial position? Personally, I think it is mainly convenient. You can judge and calculate if you save it. I will explain it again below.

You can also take a look at the genuine WeChat jump game, its first position and direction are also fixed

ok, let's briefly look at how the next block is generated and added to the stage (we usually write a class or method for repeated things, here is a method)

In fact, it is a texture, just like we use new Image(), and then set src and other attributes, please see the following code for details:

// create a block
private createBlock(): eui.Image {✌
    // random background image
    let n = Math.floor(Math.random() * this.blockSourceNames.length);
    // instantiated and added to the stage
    let blockNode = new eui.Image();
    blockNode.source = this.blockSourceNames[n];
    // Set the anchor point of the box (it's not the center point of the picture, but the center point of the box in the picture)
    blockNode.anchorOffsetX = 222;
    blockNode.anchorOffsetY = 78;
    blockNode.touchEnabled = false;
    // Add the newly created block into blockArr , unified management
    return blockNode;
// Add a block and set xy value
private addBlock() {
    // create a block
    let blockNode = this.createBlock();
    // Random horizontal position (a number between max and min, the screen is that big after all)
    let distance = this.minDistance + Math.random() * (this.maxDistance - this.minDistance);
    if (this.direction > 0) { // jump right
        blockNode.x = this.currentBlock.x + distance;
        blockNode.y = this.currentBlock.y - distance * this.tanAngle;
    } else { // jump left
        blockNode.x = this.currentBlock.x - distance;
        blockNode.y = this.currentBlock.y - distance * this.tanAngle;
    this.currentBlock = blockNode;

ok, now that we know how to create a block, let's take a look at the code in the init function, and see what is done during initialization:

// SceneGame.ts
private init() {
    // All box resources
    this.blockSourceNames = ["block1_png", "block2_png", "block3_png"];
    // Load press and jump sounds
    this.pushVoice = RES.getRes('push_mp3');
    this.jumpVoice = RES.getRes('jump_mp3');
    // Initialize the scene (blocks and villains)
    // Add touch events
    this.blockPanel.touchEnabled = true;
    this.blockPanel.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.onTapDown, this);
    this.blockPanel.addEventListener(egret.TouchEvent.TOUCH_END, this.onTapUp, this);
    // Heartbeat timer (purpose: to calculate the duration of pressing and to calculate the jumping distance)
    egret.startTick(this.computeDistance, this);
private initBlock() {
    // Initialize the first block and set the relevant properties (mainly the position in the stage, which is xy value)
    this.currentBlock = this.createBlock();
    this.currentBlock.x = this.leftOrigin.x;
    this.currentBlock.y = this.stage.stageHeight - this.leftOrigin.y;
    // Initialize the villain (the anchor of the villain is in the middle of the bottom)
    this.player.y = this.currentBlock.y;
    this.player.x = this.currentBlock.x;
    this.player.anchorOffsetX = this.player.width / 2;
    this.player.anchorOffsetY = this.player.height - 20;
    // initialization score
    this.score = 0;
    this.scoreLabel.text = this.score.toString();
    // initialization direction
    this.direction = 1;
    // add next box

The above code comments should be very clear, let's talk about the difficulties egret.startTick(this.computeDistance, this)

The api startTick will call the method this.computDistance at a frame rate of 60, don't understand? It doesn't matter, just imagine it as setInterval.

The main purpose of this.computDistance method is to calculate the horizontal distance of the jump by pressing the time. It should not be difficult to understand the following code:

// SceneGame.ts
// This function needs to return a boolean value (provided), it is not very clear what it does, but it does not affect our writing code
private computeDistance(timeStamp:number):boolean {
    // timeStamp is an auto-incrementing time (the time elapsed from execution to the current time, such as 0, 500, 1000...,unit ms)
    let now = timeStamp;
    let time = this.time;
    let pass = now - time;
    pass /= 1000;
    if (this.isReadyJump) {
        // By pressing the time (that is, s = vt)to calculate the jump distance (here refers to the horizontal displacement)
        this.jumpDistance += 300 * pass; // 300 It is a parameter debugged and can be changed by yourself
    this.time = now;
    return true;

In fact, the above content is a foreshadowing, the following is the official start to write the logic of the game part, take a deep breath, keep your mind stable, and the car must continue to drive

before take off

That is, when we touch the interface, what needs to be done, first recall in our minds. . . .

That's right, just two things, play the sound effect of the press, and then in order to be more realistic, add the deformation on the y-axis to the villain (simple to explode), the code is as follows:

// SceneGame.ts
private onTapDown() {
    // Play the press sound effect, the parameters are (where to start playing, the number of times to play)
    this.pushSoundChannel =, 1);
    // The effect of making the villain shorter to accumulate energy is scaling Y axis
    egret.Tween.get(this.player).to({scaleY: 0.5}, 3000);
    // jumping mark
    this.isReadyJump = true;

when taking off

That is, when we leave the interface with our fingers, we have to do the following things in total:

1. Set the stage to a non-clickable state;

2. Switch the sound;

3. Calculate the horizontal distance of the jump by pressing the time;

4. The villain jumps and rotates along the curve;

Code first and then explain:

// SceneGame.ts
private onTapUp() {
    if (!this.isReadyJump) return;
    if (!this.targetPos) this.targetPos = new egret.Point(); // point just a point, there xy value etc.
    // As soon as you let go, the villain should take off. At this time, you should first prohibit clicking on the screen and switch the sound.
    this.blockPanel.touchEnabled = false;
    this.pushSoundChannel.stop();, 1);
    // clear all animations
    this.isReadyJump = false;
    // Calculate drop coordinates
    this.targetPos.x = this.player.x + this.direction * this.jumpDistance;
    this.targetPos.y = this.player.y + this.direction * this.jumpDistance * (this.currentBlock.y - this.player.y) / (this.currentBlock.x - this.player.x);
    // perform jump animation
    egret.Tween.get(this).to({ factor: 1 }, 400).call(() => { // This represents a Bezier curve, in 400ms, this of factor The property will slowly approach the value of 1, where the factor is in the curve t property, which is a closed interval from 0 to 1.
        this.player.scaleY = 1;
        this.jumpDistance = 0;
        // Determine if the jump is successful
    // Execute the villain flip animation, first process the rotation center point
    this.player.anchorOffsetY = this.player.height / 2;
        .to({ rotation: this.direction > 0 ? 360 : -360 }, 200)
        .call(() => { this.player.rotation = 0 })
        .call(() => { this.player.anchorOffsetY = this.player.height - 20; });
// Add to factor of set,get method
public get factor():number {
    return 0;
// here getter Make factor Attributes start from 0, combined with just tween 1 passed in to make it match the formula in t value range.
// And the point is here setter,inside player The object is the display object we want to apply the quadratic bezier curve to, and in setter middle to give player object xy The formula for attribute assignment is exactly the quadratic Bezier curve formula listed earlier.
public set factor(t:number) {
    // just a formula
    this.player.x = (1 - t) * (1 - t) * this.player.x + 2 * t * (1 - t) * (this.player.x + this.targetPos.x) / 2 + t * t * (this.targetPos.x);
    this.player.y = (1 - t) * (1 - t) * this.player.y + 2 * t * (1 - t) * (this.targetPos.y - 300) + t * t * (this.targetPos.y);

What is more difficult to understand should be how the villain moves along the Bezier curve (this kind of confusing mathematical term), here we will analyze it a little.

Look at the factor in egret.Tween.get(this).to({factor: 1}, 400), what does this mean?

factor is an attribute, you can treat it as a variable, its initial value is 0, we use egret.Tween this easing function to make the value of factor change from 0 to 1 within 400 ms

The value of factor has changed. According to public set factor(t:number) {} , the coordinates of the villain will also change, so the villain will move, feel it well

Maybe you will ask again, why are the coordinates of the villain written like that? In fact, to put it bluntly, this is a formula, what formula is it, as shown below:


What is this formula, it is the following:


Are you familiar with it a little bit, wait, what are P0, P1, and P2? It's just three coordinate points. Let's look at the picture below to understand it better:

It can be seen from the above figure that P0 is the coordinates of the villain, P2 is the coordinates of the target point, and P1 is a point above the middle of the two (you can modify it yourself), and then bring in the above formula.

If you are still confused, it doesn't matter, it doesn't matter, you just need to know that if we know three points (starting point, middle point and end point) and bring in the above formula, we can draw a Bezier curve.

Experience it again, if you have spare time, you can learn about the Bezier curve on Baidu

If you really can't understand it, it doesn't matter, you don't use it, just move the villain to the end point directly, without any jumping effect, like the following:

egret.Tween.get(this.player).to({ x: this.targetPos.x, y: this.targetPos.y }, 400).call(() => {})

It's really not easy to see here, I have crossed a hurdle, I have to applaud myself

after take off

Non-stop, comrades, please continue to cheer, the dawn is just around the corner, fighting

Now, the villain has jumped to the target block. At this time, we need to judge whether the position of the villain's landing is within the allowable error range, so as to judge the success and failure, first look at the following picture:

We know the position of the villain and the square, and we can find out the error between the two (that is, to find the hypotenuse). If it is less than a certain range, we consider the jump to be successful. The code is as follows:

// SceneGame.ts
private checkResult() {
    // actual error
    let err = Math.pow(this.player.x - this.currentBlock.x, 2) + Math.pow(this.player.y - this.currentBlock.y, 2)
    // Maximum allowable error
    const MAX_ERR_LEN = 90 * 90;
    if (err <= MAX_ERR_LEN) { // Jump successfully
        // update score
        this.scoreLabel.text = this.score.toString();
        // direction to jump
        this.direction = Math.random() > 0.5 ? 1 : -1;
        // The distance the current block should move to the corresponding jump point
        let blockX, blockY;
        blockX = this.direction > 0 ? this.leftOrigin.x : this.rightOrigin.x;
        blockY = this.stage.stageHeight / 2 + this.currentBlock.height;
        // the point to which the villain will move
        let diffX = this.currentBlock.x - blockX;
        let diffY = this.currentBlock.y - blockY;
        let playerX, playerY;
        playerX = this.player.x - diffX;
        playerY = this.player.y - diffY;
        // Update page, update all block positions
        this.updateAll(diffX, diffY);
        // Update the villain's position
            x: playerX,
            y: playerY
        }, 800).call(() => {
            // Start creating the next block
            // Make the screen clickable again;
            this.blockPanel.touchEnabled = true;
    } else { // Failed to jump
        this.restartBtn.addEventListener(egret.TouchEvent.TOUCH_TAP, this.reset, this);
        this.overPanel.visible = true;
        this.overScoreLabel.text = this.score.toString();
private updateAll(x, y) {
    for (var i: number = this.blockArr.length - 1; i >= 0; i--) {
        var blockNode = this.blockArr[i];
        // The center point of the box (not the center point of the picture) is on the left side of the screen or on the right side of the screen or on the bottom of the screen
        if (blockNode.x + blockNode.width - 222 < 0 || blockNode.x - this.stage.stageWidth - 222 > 0 || blockNode.y - this.stage.stageHeight - 78 > 0) {
        // block out of screen,remove from display list
        if (blockNode) this.blockPanel.removeChild(blockNode);
        this.blockArr.splice(i, 1);
        } else {
        // without going beyond the screen,then move
            x: blockNode.x - x,
            y: blockNode.y - y
        }, 800)

Let's talk about the case of jump failure. Obviously, we need to update the score to the end interface and display the end interface

At the same time, an event monitor should be added to the button in the end interface. For reset, it is to re-initialize various variables. This is relatively simple, so I won't go into details.

Let's take a look at the success of the jump. What we need to do is to update the score, randomize the next direction, move all the squares and villains, and create the next square

The difficulty of this step is how to move the screen, so you have to install 13 again

Let's think about it this way, all things moving together have one thing in common: everyone moves the same distance to the left or right, and the same distance up or down.

So we only need to know how one of the squares moves, how much to move to the x-axis, how much to move to the y-axis, and the other squares and the villain also move the same distance, right? can experience it again

So now our first task is to know how to move one of the squares, let's take a look at the picture below:

The meaning of the above picture is: our villain always takes off at one of these two fixed points (you can fine-tune the position yourself), which is very important

I also mentioned at the beginning, why the block at the beginning is fixed on the left, in fact, it can also be placed on the right, but it must be one of the two fixed points

In this way, we only have a reference point when we move the block. As shown in the right picture in the above figure, the block where the villain is after jumping needs to be moved to one of the jumping points.

Since the next direction is to the left, we will move the block where the villain is to the jumping point on the right. This makes things easier

The square where the villain is located and the coordinates of the jumping point on the right have all the coordinates. Once subtracted, diffY and diffX can be calculated, and then a for loop is performed on the array of squares to move the same distance. The same is true for the villain.

After moving, generate the next block, and you're done

It's not easy to see here, and give the readers a few big applause, really excellent!

run game

We open the Egret Launcher and click on the release settings, as shown below:

Select WeChat Mini Game, click Set as Default Release, enter your AppID (register one on the official website of the Mini Program, soon), write a name, and click OK:

Then the following popup will pop up:

We click to open with WeChat developer tools (provided that you have to install WeChat developer tools), and we can preview the game effect written by ourselves, as shown below. Yeah yeah yeah!

One thing to add here is that you may feel stuck when playing, it doesn't matter, we just need to change the default frame rate of the game, like the following:


At this point, you can run a little game of jumping and jumping as rough as you want.

What are you waiting for, hurry up and play, play the game you wrote, it feels really bad (waste)

Of course, there are still many problems, such as adaptation, some mobile phone sizes are prone to black borders (it's ugly)

Or the details are not in place (you can try to change it yourself); the function is not perfect (try to add it yourself)

But these are not important. The important thing is the game idea. The idea is the most valuable. The difference between people is reflected again.




Jump to the source address: GitHub

original: Yushui goes down

*** | The above content is only for study reference and study notes | ***


Tags: egret

Posted by tlavelle on Wed, 25 May 2022 21:58:45 +0300