[Bumper Ball] Pinball Game-Detailed Explanation of WeChat Mini Program Project Development Process

Do you still remember the marble collision game you played when you were a child? Here I will explain its implementation principle in an easy-to-understand manner and see how to implement a bumper ball (marble ball) mini game. In addition to individual play, it can also be played by two people. Just like playing table tennis, you can practice on-the-spot reactions.

Create project

Open WeChat developer tools,

Mini program project

Choose to create a small program and fill in the project name yourself, for exampleminiprogram-pairs-play,

Select project properties

As shown below, just select one by one
tu1

  • AppID choose your own test number
  • Not using cloud development
  • JavaScript - JS base template

Mini game items

If you want to choose to create a small game project, you can do it. The implementation steps are similar.

You can rewrite the game source code of the mini program into the mini game project. If you are interested, you can read this article written by the author to do it.

[Snake] Detailed explanation of how to transfer games from WeChat mini program to mini games

Modify home page

Create a project and you will see that some files are automatically created in it.

Arrival positionpages/index/index, Opening positionindex.wxml,

This is the first page layout file, add abuttonbutton component layout in it,

In the correspondingindex.js logic file, implement a click method, click to enter the game,

Horizontal display

Add a setting in the correspondingindex.json configuration file, as shown below, to achieve horizontal screen operation

"pageOrientation": "landscape"

game page

The game page entered by is located atpages/game/game,

There is none here. You need to create a game page yourself.

Open the filegame.wxml and add the following content,

<!--pages/game/game.wxml-->
<canvas class="canvas" id="zs1028_csdn" type="2d" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd"></canvas>

The game page only needs a canvas component, which is very simple.

Game logic

Once you clarify the game logic, you can implement it.

Take a look at the game page, as shown below, and think about how it is implemented and whether it is simple.
Insert image description here

initialization

First, write the initialization code and bind the touch click method of the canvas component.

// pages/game/game.js
import ZS1028_CSDN from '../../utils/zs1028_CSDN.js'

const app = getApp()

Page({
    
    
    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    async onReady() {
    
    
        const {
    
     width, height, node:canvas } = await ZS1028_CSDN.query('#zs1028_csdn')
        Object.assign(canvas, {
    
     width, height })
		// 创建一个小游戏的引擎框架对象
        let game = ZS1028_CSDN.createMiniGameEngine({
    
    
            canvas,
            bgColor: 'black', //背景色
            // isTest:true //性能测试用途,需要时取消这里注释
        })
        this.game = game
        // 初始化一些游戏数据对象
        const leftPlayerData = {
    
    }
        const rightPlayerData = {
    
    }
        const aBallData = {
    
    }
        // 定义需要存储的游戏数据
        const gameData = {
    
    
            leftPlayerData, //左侧的球拍
            rightPlayerData, //右侧的球拍
            aBallData, //弹珠的
            timeNum: 0, //计时
            scopeCount: 0, //记录分
            isGameEnd: false //游戏状态
        }
        // 定义游戏平面布局需要用到一些数据
        const r = canvas.height * 0.18  // 球拍半径
        const dR = r*0.25 // 弹珠半径
        const dH = canvas.height/2 //垂直居中位置
        const powR = Math.pow(r, 2) // 勾股定理中斜边长的平方
        this.gameData = gameData
		// 这里处理将上面定义的一些游戏数据对象添加到游戏引擎中
		game.addBgObject({
    
    
            data: leftPlayerData,
            reset(){
    
    ...},
            redraw(data){
    
    ...}
        })
        game.addBgObject({
    
    
            data: rightPlayerData,
            reset(){
    
    ...},
            redraw(data){
    
    ...}
        })
        game.addBgObject({
    
    
            data: aBallData,
            reset(){
    
    ...},
            redraw(data){
    
    ...}
        })
        game.addForeObject({
    
    
            data:{
    
    ...},
            reset(){
    
    ...},
            redraw(data){
    
    ...}
        })
        // 最后,开始游戏,这个方法就是调用了game.start()
        this.restart()
    },

	onTouchStart(e) {
    
    
		//...触摸开始时处理
	},

	onTouchMove(e) {
    
    
		//...触摸移动时处理
	},

	onTouchEnd(e) {
    
    
		//...触摸结束时处理
	}

Among themZS1028_CSDN is a module, which is some methods of the game engine (framework) written by the author. The total code is less than 200 lines and can be studied and studied

After writing the initialization logic, you can run it. The interface display effect is as expected.

Game Engine

Here is an explanation of the moduleZS1028_CSDN Game Enginegame object usage:

  • addBgObject()It is a method to add participating game objects to the background;
  • addForeObject()It is a method to add participating game objects to the foreground, which can cover the drawn background objects;
  • add..()There are other methods in the module. I won’t go into details here if they are not used. Please explore by yourself!
  • run()is what runs the game;
  • stop()It stops the game and is called when the game ends.

racket

Next, draw two rackets on the left and right, then allow the user to drag it up and down

Draw a racket

The game engine was used earlier, which makes drawing easier.

In the method of adding game objectsaddBgObject(),

Implement drawing here in the passedredraw() method, the code is as follows

game.addBgObject({
    
    
   data: leftPlayerData,
   reset(){
    
    
   		//初始化数据
	   Object.assign(this.data,{
    
    
         r,
           x:0, 
           y:dH,
           relY:0,
           direY:0 
       })
   },
   redraw(data){
    
    
   const {
    
     context:ctx } = data
   		//获取球拍距离垂直中心点的长度
       let relY = that.calcRelY(this.data)
       //绘制球拍的背景色
       ctx.fillStyle = 'red'
       ctx.beginPath()
       //绘制一个半球形状
       ctx.arc(this.data.x,this.data.y+relY,this.data.r,Math.PI*1.5,Math.PI/2)
       ctx.fill()
   }
 })
  • Methodreset() is for resetting data. Just write the data to initialize it. It will be called when starting or resetting the game;
  • MethodcalcRelY() is to calculate the length of the racket from the vertical center point. The implementation is very simple

Drawing the racket on the right is the same as the method of drawing the racket above. Copy this code and then change it to draw the racket on the right.

pick up racket

After the racket is drawn, the next step is to realize the player's control over it.

To implement the racket picking up operation, start writing from the touch start method.

The following code traverses to determine whether the touch position touches the position of the racket.

onTouchStart(e) {
    
    
    //此处省略了...
    
    for(let i=0; i<e.touches.length; i++){
    
    
    	//一个触摸点,有三个重要属性 x, y, identifier
        let touch = e.touches[i]
        //此处省略了...
    }
},

Look at the above code, touches is an array, indicating that it supports multi-touch operations ,
Using this feature, two rackets can be moved simultaneously,
that is, you can use your own hands Operation, oryou and me to control the racket to play

moving racket

To implement the dragging racket operation, just write it in the touch movement method,

The following code is used to determine the data of the touch point and modify the racket data.

onTouchMove(e) {
    
    
    const {
    
     leftPlayerData, rightPlayerData } = this.gameData

    for(let i=0; i<e.touches.length; i++){
    
    
        let touch = e.touches[i]
        //此处省略了...
    }
},

Modifying the data of the racket is the object's relY attribute.
In the game engine drawing method, the racket it draws looks like it will move< /span>

put down the racket

In the touch end method, you have to handle putting down the racket,

The following code resets the racket data. It should be considered to handle the scenario when one is touching and the other is not touching.

onTouchEnd(e) {
    
    
  const {
    
     leftPlayerData, rightPlayerData } = this.gameData

    if (e.touches.length>0){
    
    
        for(let i=0; i<e.touches.length; i++){
    
    
            let touch = e.touches[i]
            //此处省略了...
        }
    }else{
    
    
    	//重置球拍的数据
        this.resetTouchRelY(leftPlayerData)
        this.resetTouchRelY(rightPlayerData)
    }
},

marbles (balls)

There is also a marble, which should be drawn in the shape of a ball.

Don’t think so complicated at first, just draw it first

Draw marbles

It is also necessary to implement drawing in the added game object.

The following code is the same as the previous method of adding objects,

game.addBgObject({
    
    
   data: aBallData,
   reset(){
    
    
       Object.assign(this.data,{
    
    
           x:canvas.width/2, //初始坐标位置
           y:dH,
           moveDirection: {
    
    
               speed:8, //移动速度
               angle:0.1 //方向角度
           },
       })
   },
   redraw(data){
    
    
       const {
    
     context:ctx } = data
       // 实现让弹珠动起来
       // 此处省略了...
       
		//绘制弹珠
       ctx.fillStyle = 'green'
       ctx.beginPath()
       ctx.arc(this.data.x,this.data.y,dR,0,Math.PI*2)
       ctx.fill()

		//保存改变,判断游戏是否结束
		//此处省略了...
   }
})

Make the marbles move

In the above drawing method, where to start,

Add the following code to make the marbles move. Isn’t it amazing?

let {
    
     speed, angle } = this.data.moveDirection

// 球拍按照角度angle方向移动位置
this.data.y += Math.sin(angle/180*Math.PI) * speed
this.data.x += Math.cos(angle/180*Math.PI) * speed

Junior high school mathematics knowledge is used here: Pythagorean theorem;
Students who have been to junior high school, have you ever remembered sinesin(A) and cosine What about the formula of cos(A);
where A is the side length in radians, angle is the angle between the horizontal line and the horizontal line ;
The relationship between radians and angles is:A = angle / 180 * Math.PI

Impact checking

Now the marbles can move. Next, continue writing code.

The code is as follows. The idea of ​​implementing collision detection is roughly like this, just change the speed and angle.

// 计算弹珠的四个顶点位置,用于实现碰撞检测
let left = this.data.x-dR
let right = this.data.x+dR
let top = this.data.y-dR
let bottom = this.data.y+dR
// 开始判断,先看看看移动方向到了哪个位置
// 判断是否碰撞上下边界
// ...此处省略了
// 再判是否断碰撞左边和右边的球拍
// ...此处省略了
// 再判是否断碰撞左边和右边界
// ...此处省略了

// 这里是绘制弹珠,上面已经讲了...此处省略

//最后,保存改变的速度和角度
Object.assign(this.data.moveDirection, {
    
     speed, angle })

//到左边和右边边界就判断出界,到此游戏结束
if (left<=0 || right>=canvas.width) {
    
    
    game.stop()
    gameData.isGameEnd = true
    // 记录最高分
    app.setHistoryScopeCount(gameData.scopeCount)
    // 弹窗提示游戏结束
    ZS1028_CSDN.showModal('游戏结束, 记录'+gameData.scopeCount,'重新开始','退出游戏').then(res=>{
    
    
        if (res.confirm) that.restart()
        else wx.navigateBack()
    })
}

Angle direction change

The idea of ​​​​implementing collision detection will be more complicated, and it is necessary to study and understand the movement trajectory of the marbles.

You have to study it carefully, otherwise you will not understand the code.

How to change the direction of the marble after it moves to collision? Let’s talk about it roughly. The code is as follows

// 判断上下边界的
if (top<=0 || bottom>=canvas.height) {
    
    
    angle *= -1 //改变负号,弹珠在靠近水平线时会弹跳起来,是不是很神奇
} else if (r >= left) {
    
    
	//判断到左边,接近球拍半径就会执行到这里
	//...此处省略了
} else if (canvas.width > right && canvas.width-r <= right) {
    
    
	//判断到右边,接近球拍半径就会执行到这里
	//...此处省略了
}
//...此处省略了

Show records

How to display records

MethodaddForeObject()The same as the method of adding objects above, just draw and display the game data,

The last added one will be displayed on the top layer. It’s very simple, so I won’t go into details if it’s not important.

game running

After writing this, follow the above implementation ideas, run it while writing a part, and learn in practice.

Once done, you can basically run the game test.

Go back to the game initialization mentioned at the beginning. The method to call the game start isthis.restart(), the code is as follows

restart() {
    
    
 	const {
    
     game } = this
	// 调用游戏引擎的run方法即可运行
    game.run()
}

To restart the game, just call the restart() method,

Using the game engine framework, you don’t have to think about the implementation yourself during the running process, it’s that simple!

The animation of the running effect of the bumper ball game is as follows:
Please add image description

The racket on the right can be dragged. The recorded computer mouse can only control one.
On the real machine, two can be controlled at the same time, so the playing effect will be better< /span>

For the complete code, you can check out the source code of the bumper ball mini-program mini-game project, which has been placed in the resources.Please click to view ( You may not be able to find it on your mobile phone, please use a computer browser to check it out), thank you for your support! ❤
Please add image description

Guess you like

Origin blog.csdn.net/zs1028/article/details/134478924