用kotlin來實現一個打方塊的小遊戲

codelang發表於2017-12-25

用kotlin來實現一個打方塊的小遊戲

前言

今天來做個打方塊的小遊戲,繼續熟悉kotlin的語法,看下要實現的效果圖

用kotlin來實現一個打方塊的小遊戲

看著效果圖好像挺難的樣子,但理清思緒後,你會發現特別的簡單,還是那句話,學習方法最重要

思路

  • 構造介面 :

    這個部分比較簡單,根據控制元件的比例來畫小球、擋板和擊打的方塊,所有擊打的方塊儲存在一個集合裡面,方塊裡面儲存的資訊有left、top、right、bottom位置資訊和是否被擊打過了的標誌

  • 擋板的滑動 :

    下面的擋板需要根據手勢的左右移動來反彈小球,所以,我們可以重寫onTouch來實現

  • 小球的運動 :

    我們線上程裡面開啟一個white迴圈,不停的改變小球的位置,然後重繪介面,小球的運動是有規則的,碰到四周的介面要回彈,碰到擊打的方塊要回彈,碰到擋板也要回彈,那麼,如何回彈呢?我們給小球做一個累加值,讓小球不停的去加這個值,碰到碰撞物我們就給這個累加值取反,舉個例子,現在offsetX是一個正整數,那麼ballX+=offsetX,現在小球是往右移動,當碰撞到最右邊的時候,我們給offsetX取反,也就是offsetX=offsetX*-1,這時候offsetX變成了一個負數,那麼小球ballX+=offset就會越加越少,也就是往左移動,移動到最左邊的時候我們又給offsetX=offsetX*-1,這時候offsetX又變回了正數,這時候,來回的反彈就實現了,ballY的移動也是如此

  • 小球擊打方塊 :

    小球擊打到方塊有四個方向:左、上、右、下,我們就說說擊打下方的判斷吧,小球頂部碰撞到方塊的區域為方塊的left和right區域,並且當小球的頂部剛好突破方塊的bottom位置時,算是一次有效的碰撞,然後我們給這次碰撞做一個標記,然後反彈小球,下次做碰撞的時候我們忽略已經碰撞過的地方,並且不繪製碰撞過的區域

  • 遊戲結束 :

    在每次迴圈結束時都去統計集合裡碰撞標誌數量是否等於集合的size,是的話就結束迴圈,遊戲結束

思路整理清晰後,我們來一一實現

構造介面

首先來繪製一下小球和擋板

    var width: Float = 0f
    var height: Float = 0f

    /**
     * 移動滑塊的寬度
     */
    var boardWdith: Float = 0f
    /**
     * 擋板的高度
     */
    var boardHeight: Float = 0f
    /**
     * 擋板距離頂部的距離
     */
    var board2Top: Float = 0f

    /**
     * 擋板距離左邊的距離
     */
    var board2Left: Float = 0f
    /**
     * 小球的半徑
     */
    var ballRadius: Float = 0f
    
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        width = w.toFloat()
        height = h.toFloat()

        //擋板的寬度
        boardWdith = width / 8
        //擋板距離頂部的距離
        board2Top = height / 8 * 7
        //擋板的left距離左邊的距離,目的使擋板居中
        board2Left = width / 2 - boardWdith / 2
        //設定小球的半徑為擋板的1/4
        ballRadius = boardWdith / 4
        //設定小球的x和y座標
        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)
    }   
    
複製程式碼

ok,擋板和小球已經畫好了

用kotlin來實現一個打方塊的小遊戲

然後,我們來畫一下被擊打的方塊,首先定義一個儲存方塊資訊的Bean類

/**
 * @author wangqi
 * @since 2017/12/10 17:26
 */

public class Brick {
    /**
     * 儲存方塊的顏色
     */
    private String color;
    /**
     * 儲存方塊的座標
     */
    private RectF rectF;
    /**
     * 判斷是否碰撞到了,預設為false未碰撞
     */
    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;
    }

}

複製程式碼

然後我們來看看怎麼繪製

    /**
     * 定義一個儲存方塊的集合
     */
    var brickList: MutableList<Brick> = mutableListOf()
    /**
     * 方塊的寬度
     */
    var brickWidth = 0f
    /**
     * 方塊的高度
     */
    var brickHeight = 0f   
   
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        ...
        
         //方塊的寬度是view的1/5
         brickWidth = width / 5
         //方塊的高度是寬度的一半
         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 / 50).toFloat()
        paintLine.style = Paint.Style.FILL
        
    }   

    /**
     * 建立方塊
     */
    fun 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)
    }
複製程式碼

ok,方塊完美的繪製

用kotlin來實現一個打方塊的小遊戲

擋板的滑動

擋板的滑動部分,我們只需要重寫onTouch方法,然後再每次move的過程中去改變擋板距離View左邊界的距離


    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
    }

複製程式碼

用kotlin來實現一個打方塊的小遊戲

小球的運動

小球的運動是這裡面最核心的部分了,我們得細細的講講

首先,我們需要定義一個執行緒,線上程裡面定義一個while迴圈,sleep50毫秒去重回介面,所以,我們要在這50毫秒的時間裡,去改變小球的運動軌跡、邊界值情況、是否碰撞到方塊、是否碰撞到擋板和遊戲是否結束,我們先把小球給運動起來再說

    /**
     * 結束迴圈的標誌位
     */
    var isOver: Boolean = false
    /**
     * 小球x方向每次移動的偏移量
     */
    var vx: Float = 8f
    /**
     * 小球y方向每次移動的偏移量
     * 預設為負數,因為小球是向上運動   
     */
    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

                /*
                   邊界值判定
                   如果小球小於左邊界或大於右邊界則x方向取反
                 */
                if (ballX + ballRadius > width || ballX - ballRadius < 0) {
                    vx *= -1
                }
                /*
                   邊界值判定
                   如果小球大於底部邊界或小於頂部邊界則Y方向取反
                 */
                if (ballY - ballRadius < 0 || ballY + ballRadius > height) {
                    vy *= -1
                }

                Thread.sleep(50)
                postInvalidate()
            }
        }.start()

    }

複製程式碼

小球開始運動了,咦,小球怎麼突然不見了,哈哈,因為被方塊遮擋住了

用kotlin來實現一個打方塊的小遊戲

小球移動解決了,接下來我們來處理下小球彈到擋板反彈

        //開啟執行緒
        thread {
            while (!isOver) {
            //邊界值判斷
            ...
                /*
                判斷小球是否落在滑塊上
                小球x軸的中心大於擋板的left並且小球x軸中心小於擋板的右邊並且小球的y軸中心加上半徑加上擋板高度的一半
                 */
                if (ballX >= board2Left && ballX <= board2Left + boardWdith
                        && ballY >= board2Top - ballRadius - dip(10).toFloat() / 2
                        ) {
                    //改變Y軸的運動方向    
                    vy *= -1
                }
            ...
            
            }        
        }
複製程式碼

用kotlin來實現一個打方塊的小遊戲

擋板的判斷知道了,那麼小球和方塊的碰撞也就自然清晰了

        //開啟執行緒
        thread {
            while (!isOver) {
            //判斷小球是否落在滑塊上
            ...

                /*
                 * 迴圈集合的每一個方塊,判斷小球當前的位置是否碰撞到方塊
                 */
                for (i in brickList.indices) {
                    //拿到方塊
                    val brick = brickList[i]

                    //忽略撞擊過的方塊
                    if (brick.isImpact) {
                        continue
                    }
                    //獲取方塊的座標
                    val rectF = brick.rectF

                    /*
                        判斷小球是否撞擊到方塊的底部
                        小球x軸的中心大於方塊的left
                        小球x軸的中心小於方塊的right
                        小球y軸中心減去半徑,也就是小球的頂部,是否小於等於方塊的底部,也就是穿過方塊底部的一瞬間
                     */
                    if (ballX >= rectF.left && ballX <= rectF.right && ballY - ballRadius <= rectF.bottom) {
                        //設定該方塊已被撞擊
                        brick.isImpact = true
                        //方向取反
                        vy *= -1
                    }
                }
                /*
                 * 統計被撞擊方塊的數量是否等於集合,是的話表明遊戲結束,設定結束標誌位,停止while迴圈
                 */
                if (brickList.count { it.isImpact } == brickList.size) {
                    isOver = true
                }
                
            ...
            
            }        
        }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        ...
        
        if (isOver) {
            val text = "通關成功"
            //獲取文字的寬度,目的是為了文字居中
            val textWidth = paintLine.measureText(text)
            canvas.drawText(text, width / 2 - textWidth / 2, 100, paintLine)
        }
        
    }

複製程式碼

最終效果圖

用kotlin來實現一個打方塊的小遊戲

通關成功

用kotlin來實現一個打方塊的小遊戲

總結

小球碰撞到底部邊界的判斷我沒有去做,原因是為了能擊打到方塊,增加趣味性,還有碰撞方塊的四個方向,我只做了碰撞到底部的方向,有興趣的同學可以自己試著補上,檢視完整原始碼

理論和實踐相輔相成,理論是規劃實踐的實施性,實踐是為了證明理論

qq群號492386431

關注公眾號codelang

用kotlin來實現一個打方塊的小遊戲

相關文章