Kotlin to achieve a small game playing a box

preface

Today let’s play a little game of square, continue to be familiar with Kotlin’s syntax, look at the effect of the implementation

It may seem difficult to look at the renderings, but after you clear your mind, you will find that it is extremely simple. Once again, it is the method that counts

Train of thought

  • Tectonic interface:

    This part is relatively simple, according to the proportion of the control to draw the ball, baffle and hit the square, all hit the square is stored in a set, the information stored in the square has left, top, right, bottom position information and whether it has been hit the mark

  • Sliding of baffle plate:

    The bottom baffle needs to move left and right to bounce the ball according to the gesture, so we can rewrite onTouch to do this

  • Motion of the ball:

    We open a white loop in the thread, constantly change the position of the ball, and then redraw the interface, the movement of the ball is regular, when it hits the interface around it will bounce back, when it hits the square it will bounce back, when it hits the baffle it will bounce back, so, how to bounce back? So we’re going to give the ball a sum, and we’re going to keep adding to it, and we’re going to invert the sum when it hits the collider, so for example, now offsetX is a positive integer, so ballX+=offsetX, and now the ball is moving to the right, and when it hits the far right, we’re going to invert offsetX, So offsetX=offsetX*-1, and offsetX becomes a negative number, so the ball ballX+=offset is going to get more and more and less, so it’s going to move to the left, and when it gets to the left we’re going to give offsetX=offsetX*-1, At this point offsetX becomes positive again, and the bounce back and forth is achieved, as is ballY’s movement

  • Ball hitting a cube:

    The ball hits the square in four directions: Left, up, right, down, let’s talk about the judgment of hitting the bottom, the area where the top of the ball hits the square is the left and right area of the square, and when the top of the ball just breaks through the bottom of the square, it is a valid collision, and then we mark the collision, and then we bounce the ball, The next time we do a collision we ignore where we have already collided and do not draw the area where we have collided

  • Game over:

    At the end of each cycle, count whether the number of collision marks in the set is equal to the size of the set. If so, end the cycle and end the game

After sorting out the ideas clearly, we will implement one by one

Structural interface

First draw the ball and baffle

Var width: Float = 0f var height: Float = 0f /** * Float = 0 f * / var / * * * the height of the baffle boardHeight: Float = 0 f / * * * * at the top of the baffle distance distance/var board2Top: Float = 0f /** * board2Left: Float = 0f /** * radius of the ball */ var ballRadius: Float = 0f override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, Oldh) width = w.ofloat () height = h.ofloat () boardWdith = width / 8 // board2Top = height / 8 * 7 // The left distance from the left of the baffle, Board2Left = width / 2 - boardWdith / 2 // set the ballRadius to 1/4 of the baffle /4 // set the ballX and y coordinates ballX = width / 2 ballY = board2Top - ballRadius - dip(10).toFloat() / 2 ballPaint.style = Paint.Style.FILL ballPaint.isAntiAlias = true ballPaint.color = resources.getColor(R.color.colorAccent) boardPaint.style = Paint.Style.STROKE boardPaint.isAntiAlias = true boardPaint.strokeWidth = dip(10).toFloat() boardPaint.color = resources.getColor(R.color.colorPrimary) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) setBackgroundColor(resources.getColor(R.color.black)) canvas.drawLine(board2Left, board2Top, board2Left + boardWdith, board2Top, boardPaint) canvas.drawCircle(ballX, ballY, ballRadius, ballPaint) }Copy the code

Ok, the baffle and the ball have been drawn

Next, let’s draw the hit square, first defining a Bean class that stores the information about the square

/** * @author wangqi * @since 2017/12/10 17:26 */ public class Brick {/** * private String color; /** * private RectF RectF; /** * private Boolean isImpact; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public RectF getRectF() { return rectF; } public void setRectF(RectF rectF) { this.rectF = rectF; } public boolean isImpact() { return isImpact; } public void setImpact(boolean impact) { isImpact = impact; }}Copy the code

And then let’s see how do we draw it

/** * define a collection of blocks */ var brickList: MutableList<Brick> = mutableListOf() /** * brickWidth = 0f /** * brickHeight = 0f override fun  onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) ... BrickHeight = brickWidth / 2 /* For (row in 0.. 3) { for (col in 0.. 4) { createBricks(row, Col)}} paintline.strokeWidth = dip(1.0f).tofloat () paintline.isantiAlias = true paintline.textSize = dip(width /) ToFloat () paintline.style = paint.style.fill} /** * createBricks(row: Int, col: Int) { var brick = Brick() var rectF = RectF() rectF.left = brickWidth * col rectF.top = brickHeight * row rectF.right =  brickWidth * (col + 1) rectF.bottom = brickHeight * (row + 1) brick.rectF = rectF val hex = "#" + Integer.toHexString((-16777216 * Math.random()).toInt()) brick.color = hex brickList.add(brick) }Copy the code

Ok, the cube is perfectly drawn

Slide of baffle

For the sliding part of the baffle, we just need to rewrite the onTouch method, and then change the distance between the baffle and the left edge of the View during each move


    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {

            }

            MotionEvent.ACTION_MOVE -> {
                board2Left = event.x - boardWdith / 2
                invalidate()
            }

            MotionEvent.ACTION_UP -> {

            }
        }
        return true
    }

Copy the code

Motion of the ball

The motion of the ball is the core part of this, and we have to talk about it in detail

First of all, we need to define a single thread, define a while loop in the thread, sleep50 milliseconds to return to the interface, so we want to be in the 50 milliseconds, to change the ball’s trajectory, boundary value conditions, whether the collision to squares, and whether the collision to baffle and whether to end the game, we first give movement and the ball

/** */ var isOver: Boolean = false /** ** / var vx: Float = 8f /** * The offset of each movement of the ball in the Y direction * defaults to negative because the ball is moving up */ var vy: Float = -8f Override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) ... Thread {while (! IsOver) {ballX + + = = vx ballY vy / * boundary value judgement If the ball is less than or greater than the left border right boundary is take the x direction * / if (ballX + ballRadius > width | | ballX - ballRadius < 0) = 1} {vx * / * boundary value judgement If the ball is greater than the boundary or less than the top boundary is at the bottom of the Y direction invert * / if (ballY - ballRadius < 0 | | ballY + ballRadius > height) {vy *= -1 } Thread.sleep(50) postInvalidate() } }.start() }Copy the code

The ball started to move, gee, why did the ball suddenly disappear, ha ha, because it was blocked by the square

Now that the ball has moved, let’s deal with the ball bouncing off the baffle

Thread {while (! IsOver) {// check the boundary value... /* If (ballX >= board2Left && ballX <= board2Left +) /* If (ballX >= board2Left && ballX <= board2Left + BoardWdith && ballY >= board2top-ballradius-dip (10).tofloat () / 2) {// Change Y direction vy *= -1}... }}Copy the code

The judgment of the baffle is known, so the collision between the ball and the square is naturally clear

Thread {while (! IsOver) {// Check whether the ball falls on the slider... /* * loop each square of the set, For (I in brickList.indices) {val brick = brickList[I] if (brick.isimpact) { Val rectF = brick. RectF /* Determine if the ball hits the center of the bottom ball on the square's X-axis. The center of the left ball on the X-axis is smaller than the center of the right ball on the Y-axis. Is it less than or equal to the bottom of the cube, */ if (ballX >= rectf.left && ballX <= rectf.right && ballY - ballRadius <= rectf.bottom) {// Set the block to hit Brick. IsImpact = true (vy *= -1}} /* *) Stop the while loop */ if (BrickList.count {it.isimpact} == BrickList.size) {isOver = true}... } } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) ... If (isOver) {val text = "pass" // get the width of the text, Val textWidth = paintline.measureText (text) cancanvas. DrawText (text, width / 2 - textWidth / 2, 100, measureText) drawText(text, width / 2 - textWidth / 2, 100, measureText) paintLine) } }Copy the code

Final rendering

Customs clearance success

conclusion

The ball collision bottom boundary judgment I did not go to do, the reason is to be able to hit the square, increase the interest, and the four directions of the collision square, I only did the direction of the collision bottom, interested students can try to fill, check the complete source code

Theory and practice complement each other, theory is to plan the implementation of practice, practice is to prove theory