I am participating in the nuggets Community Game Creativity Submission Contest. For details, please see: Game Creativity Submission Contest.

In this article, the small game “Shave” was developed with THE H5 game engine Egret 🐶. The game experience address and source code sharing address are listed below, and the game can be played on both PC and mobile terminals.

🌟 game experience address

💡 game source code sharing

Creative origin

One morning, I woke up and lay in bed, cutting the APP and swiping the phone. When I saw that the Nuggets were holding a creative submission contest again, I was very calm and didn’t care, thinking that I was too old to have any great ideas, so I’d better leave the opportunity to young people.

Again on the bed paralysis for a while, found no sleepy, then get up to wash your face and brush your teeth.

When I got to the bathroom, I saw myself in the mirror, with a beard all over my face. It turned out that I had been unemployed for a long time since I left my job in my last company. Large factories outside layoffs, repeated outbreaks, small and medium-sized factory employment demand is less and less, so that job hunting is becoming more and more difficult. I don’t know how many days I’ve been lying low.

Looking at his face full face beard, it is funny, thinking also should shave shave.

Open razor shaving beard, listening to the buzzing sound of electric razor, not from let me think of a child to play GBA in the “rhythm of heaven” in the pull beard, this level is really impressive, follow the rhythm of the beard pulled down.

But I think with the rhythm of plucking beard although fun but not refreshing, if I do and beard related games, I will make a shaving razor to shave the face of flying around small games, should be very interesting.

Well, now that we have an idea, and the Nuggets are having a game idea submission contest, let’s get started!

Ideas for

I’ve learned a little bit about THE H5 game engine Egret before, so THIS little game was developed using it.

The entry menu for the game

The first thing we need to do is draw the first scene is the menu interface, all games start from the menu ~

Uh, okay, simple. This small game is called “shave” bar, but also very thick skin wrote his name.

There is a start game button in the menu interface, so we can click start game to enter our game scene interface.

[Relevant source file path]

Let’s start with a face

Now let’s start drawing the game scene.

My idea is that if it’s shaving, we need to create a face,

First of all, I searched the Internet, but there seemed to be no material that met my idea.

People also fast poor can not afford to eat, no money to ask UI to help me design a.

Well, I’ll draw it myself,

So I was thinking and drawing,

I ended up with this big face.

It’s actually my own mental image of shaving, frowning and chin up…

I felt that the color match was a little too monotonous, so I added a little color to the facial skin

Then a breathing animation and a blinking eye were added to liven it up

I didn’t know this little gadget turned out to be cute.

[Relevant source file path]

The Beard at the heart of the game

Next comes the most important part of the game, “Beard”. My idea is to draw some line segments on the chin part of the game character’s face.

Random generation of beards

When I first tried to do it, I found a problem. The line segment of the beard should be randomly distributed, and the main character’s face is round.

So how do we randomly draw a beard line segment inside a circle.

So I did some research on the Internet,

It is found that this idea can be realized by the algorithm of uniformly distributed coordinates in a circle.

The following is a function that randomly generates the coordinates of the beard, which is called as each beard is drawn to get the coordinates.

  /** * Randomly generates the coordinates of the beard on the face *@param R is the radius of the face circle *@param CenterX is the X-axis coordinates * of the face in the game scene@param CenterY is the center of the y axis of the face in the game scene *@param TryCount is the number of retries *@returns Randomly generate the coordinates of the beard */
 private randomPoint(r:number,centerX:number,centerY:number,tryCount:number = 0):[number,number]{
       // generate a random Angle [0,2* PI]
       let theta = 2 * Math.PI * Math.random();
       // Random generated distance center length [0,r]
       let len = Math.sqrt(Math.random()) * r;

       let x = centerX + len * Math.cos(theta); 
       let y = centerY + len * Math.sin(theta);

       // If the y value generated is too small, a new coordinate will be generated
       if( y < this.faceY - BREAS_Y && tryCount ! =3) {return this.randomPoint(r,centerX,centerY,++tryCount)
       }
       return [x,y];
 }
Copy the code

The r parameter is passed in to the radius of the character’s face to be drawn, and calling randomPoint will generate a random coordinate within the character’s round face for me to draw the position of the beard.

Then why call randomPoint to determine if the y value is too small and then re-call the function to compute a coordinate?

In the figure, the blue point is the center point of the face, and the y-coordinate of the red point is the minimum Y-coordinate I hope to generate. If it is smaller than the y-coordinate of the red point, the coordinate generated randomly by the beard may reach the mouth and eyes 😂. Therefore, when the Y-axis of the generated random coordinate is less than the Y-axis of the red point coordinate, a new random coordinate needs to be generated. After testing, it is usually enough to recalculate 2 and 3 times.

Then let’s look at the effect of drawing a beard

Wow, that’s good. That’s good

This completes the basic beard drawing.

If you are interested in the principle of uniformly distributed coordinate algorithm in a circle, you can take a look at this article written by the elder brother

Direction of beard

When I was done, there was something strange about my beard,

Only then did I realize that the direction of the beard was the same, vertical on the face,

At this point we need to make a direction for the beard.

My idea is to rotate the beard at an Angle towards a point in the middle of the face.

Here, I used [the center X coordinate of the face drawing, and the center Y coordinate of the face drawing * 0.5] as the reference coordinate of the rotation Angle to be rotated, because I felt that the rotation Angle calculated by this coordinate was more appropriate in the debugging.

So here we’re going to use two point coordinates to figure out the Angle between the axes

Since we need the Angle between the x axis, the formula we use is:

Math.atan2(y1 - y2 , x1 - x2) * 180 / Math.PI

// Calculate the Angle of the beard
const beardAngle = Math.atan2((this.faceY * 0.5) - y , this.faceX - x) * 180 / Math.PI;
// Set the Angle of the beard, because I drew a vertical line and need to subtract another 90 degrees
beard.rotation = beardAngle - 90;
Copy the code

Now let’s see what happens

Great! Now each beard has its own direction.

For the calculation of two point coordinates the Angle between the axes can be seen in this article

Shave off your beard

Now that we’re done, let’s do the interactive aspect of the beard.

The game is called shaving, and now we have a beard and we have to figure out how to shave it off.

At the beginning, I bound each beard with TouchMove event. When a finger or mouse moves across the beard on the face of the game character, the beard will make an animation that simulates falling by a distance from the Y-axis, and delete the beard after the animation is executed.

When testing this effect, I found that the range of the beard is too small, especially on the mobile terminal sometimes can not touch, resulting in the beard can not shave off. At this time the old front end will certainly understand, then expand the beard click range.

So I expanded the click range for each beard.

But a new problem arises. When we click or touch two beards near each other, we expect both beards to trigger the shaving event. But in reality, clicking on the range where the two beards intersect will only trigger the one with the last z-axis drawn in front of it. The other one will be blocked and will not trigger the event.

Situations like this, such as Dom event capture during Web page development, can easily solve this problem.

But elements like those in the Egret game engine are drawn on canvas, there is no such thing as event flow, and you need to re-implement them yourself.

We’ll just have to figure it out.

  • Thinking a

Monitor the TouchMove event on the face, increase the range of the finger or mouse, and then every time the finger or mouse moves, judge whether the beard still on the face is in the range of the trigger event, if it is in the range of the beard will be shaved off.

But this approach is performance-intensive. For example, when there are 100 beards on the face, when the mouse or finger is constantly operating, each frame needs to be traversed to determine whether the 100 beards are in the range.

(Above, if moving the circle with the mouse is the expanded range of judgment)

  • Idea 2

This method is also the one currently in use. It still monitors the TouchMove event of each beard. When a finger touches a beard and triggers the event, it will judge whether there is a beard around the beard and shave it off together if there is one.

Using this method, you don’t need to judge each frame during the operation, only when one of the whiskers triggers the event, so the performance is much better.

So we need to figure out if there are other beards around the target coordinates,

Here I’m using the method of calculating the distance between two points to get the distance between two points, and then with this distance value, we can use it to determine if we’re around the coordinate.

The formula used here to calculate the distance between two points is:

Math.pow(x1 - x2,2) + math.pow (y1 - y2,2))

/** * Find the surrounding beards by the target position coordinates *@param X Target beard's X-axis position *@param Y The Y-axis position of the target beard *@return Array of beards at target location */
private findClosestBeards(x:number,y:number){
    // This. beards is an array. All beards created in the game are stored in this.beards.
    const nearBeards = this.beards.filter(beard= >{
        // When findClosestBeard was last found, it was removed with a drop equal to true flag
        // The next time findClosestBear traverses the beard, it will be skipped
        if(beard.drop) return false;
        // Calculate the distance between each beard coordinate and the target coordinate
        const distance = Math.abs(Math.sqrt(Math.pow(x - beard.x,2) + Math.pow(y - beard.y,2)))
        return distance <= 25
    })
    return nearBeards
}
Copy the code

Ok, now that we have the findClosestBeard function, let’s apply it to our beard touch event.

/** ** The touch of the beard triggers the event */
private touchBeard = async (event:egret.TouchEvent)=> {
   // Find the beard around the location by findClosestBeard
   const closestBeards = this.findClosestBeards(event.$stageX,event.$stageY);
   // closestBeards finds the array of beards around the beards and removes them
   closestBeards.forEach(beard= >this.removeBeard(beard))
}
Copy the code

The event. StageX and event. StageY passed in by findClosestBeard are the current mouse or finger positions. Why don’t have to touch the root beard coordinates, because through the above have know me for all beard expanded click range, sometimes touch the beard of location is either above or below the fingers, I hope it’s touched a beard, is the center of the coordinates of the finger or the mouse around to find to shave off the beard.

The beard flying all over the place

I’m not happy with the drop animation when shaving off the beard, it’s still a long way from what I imagined the game would look like.

Usually when we go to the barber shop and Tony holds the clipper in our hair and it goes flying all over the place. Something like this is what I would expect from shaving in the game.

So I thought about how the beard in the game would be like the hair in real life, and when it was cut off, it would fly around.

That’s when THE physics engine came to mind.

Use the physics engine to create a bounce effect on the beard based on the direction of the mouse or finger, and then the beard falls to the ground with gravity.

The physics engine recommended in the Egret document was P2.js, so I used it.

We first created our physical world using p2.js where we initialized it

// Initializes the physical world, setting the gravity of the physical world
this.word = new p2.World({ gravity: [0.0.01]});// Put the rigidbody to sleep
this.word.sleepMode = p2.World.BODY_SLEEPING;
Copy the code

Then create a rigidbody while drawing the beard and set the beard map in the rigidbody.

Because the units in Egret are PX and P2.js are MKS (meters kilogram seconds),

So when p2.js sets the location of the physical element it needs to be transformed,

I set the factor variable used in the conversion to 50, and the length of one meter relative to p2.js is 50px of the screen in Egret.

// Draw the beard function
private drawBeard(x:number,y:number,face:egret.Sprite):BeardItem{
    // Set the beard collision box
    var boxShape = new p2.Box({ radius:20 / factor });
    // Set the rigidbody of the beard
    const boxBody : p2.Body = new p2.Body({  
        mass:1.position:[x/factor , y/factor],
        collisionResponse:false.ccdIterations:false 
    });
    // Add the collision box to the rigidbody
    boxBody.addShape(boxShape);
    // Create a Sprite object for the beard
    const shp:egret.Sprite = newegret.Sprite(); .// Add the rigid body to the world
    this.word.addBody(boxBody);
    // Add the beard map to the rigidbodyboxBody.displays = [shp]; .// this. Breads stores the beard object
    // SHP indicates the circle of the click range, line indicates the line segment of the beard, body indicates p2 rigid body, and drop indicates whether to shave the mark
    const beardItem = { shape:shp,line:line,body:boxBody,drop:false}}Copy the code

Next, the TouchStart event is bound to the face of the main character of the game, and the starting position of the finger or mouse is set in the static property touchPositionRecrod.

// Record the starting position of the finger or mouse
private touchStart(event:egret.TouchEvent){
    this.touchPositionRecrod = { x:event.$stageX , y:event.$stageY }
}
Copy the code

It’s the same familiar touchBeard function, with some new additions.

We know the starting coordinates of the mouse or finger by recording in the touchPositionRecrod. Then we can calculate the current moving direction by sliding the finger or mouse to the position of the beard, and then make a force in the direction of the beard to be shaved.

/** ** The touch of the beard triggers the event */
private touchBeard = async (event:egret.TouchEvent)=> {
    // Find the beard around the location by findClosestBeard
    const closestBeards = this.closestBeards(event.$stageX,event.$stageY);
    // The position of the top and bottom -1 is up and 1 is down
    const verticalDirection = event.$stageY > this.touchPositionRecrod.y ? -1 : 1;
    // Left and right positions -1 is left and 1 is right
    const horizontalDirection = event.$stageX > this.touchPositionRecrod.x ? 1 : -1;
    // Re-record the current finger or mouse position
    this.touchPositionRecrod = { x:event.$stageX , y:event.$stageY }
    
    closestBeards.forEach(beard= >{
        // Apply a force to the beard bound rigidbody
        beard.body.applyForceLocal([0.1 * horizontalDirection,0.1 * verticalDirection],[beardItem.shape.x / factor,beardItem.shape.y / factor]);
        // closestBeards finds the array of beards around the beards and removes them
        this.removeBeard(beard)    
    })
}
Copy the code

The final step is to get the p2.js world moving.

Egret listens for frame events

this.addEventListener(egret.Event.ENTER_FRAME,this.Update,this);
Copy the code

Associated with the p2.js world in Egret frame events,

The current physical position of the p2.js beard element is obtained in each frame, and the position of the beard drawing in Egret in the frame is updated along with that.

private Update(){    
    // Set p2 world update
    this.word.step(2.5);
    // get all the beards that drop equals true, that is, the shaved beards
    // When the position of the rigid body element in the beard is inconsistent with the current position of the beard element on the screen, the physical movement is being performed, and the position of the beard element on the screen is updated.
    this.beards.filter(i= >i.drop).forEach((i) = >{
        if(Math.floor(i.shape.x) ! = =Math.floor(i.body.position[0] * factor)) i.shape.x = i.body.position[0] * factor;
        if(Math.floor(i.shape.y) ! = =Math.floor(i.body.position[1] * factor)) i.shape.y = i.body.position[1] * factor; })}Copy the code

Now that we’ve added physics to each beard, let’s see how the game works.

For those of you who are interested in p2.js, this is its official documentation.

[Relevant source file path]

How to make this idea more fun

We’re almost done with the idea part of the game, and the next thing we need to think about is how to make it fun.

Basic gameplay

So I put the shaving into a little game of hand speed.

See how many beards you can shave off in a limited time. When the beards are shaved off, new ones will grow 😁. Players who shave more will get more points.

reward

To encourage players to increase their hand speed, I added a reward mechanism 😜.

Shaving a certain number of hours in a second triggers bonus points,

Rewards are divided into three levels, and different animations are performed when triggering rewards of different levels. The higher the reward, the higher the bonus score.

sound

To add to the fun of the game, the sound of a razor is added while the player is shaving.

And different sound effects can also be triggered when triggering rewards 🐶.

list

In order to keep players motivated to play again and again, a leaderboard feature was added, allowing players to challenge the scores on the leaderboard.

At the end of each game, the score is checked to see if it enters the leaderboard

Leaderboards record the scores of the top 20 players (the screenshot shows fake stats)

[Relevant source file path]

The last

This article records the small game “Shaving” from the initiation of creative ideas, the realization of ideas, the difficulties encountered in the realization of ideas and the solutions to the problems encountered, to the final completion of the small game to improve the gameplay. In fact, the process of making this small game is very interesting, in which constantly thinking, to the final completion of the small game heart full of joy and learn a lot of things.

The game experience address and source link are at the top of the article, you can experience. If you have any suggestions or questions, please leave them in the comments section.

At the end of the article, I wish all the brothers who study hard in Nuggets higher and higher wages, better and better life! See you in the next article