自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android 原生控件打造經(jīng)典貪吃蛇游戲?qū)崙?zhàn)指南

移動開發(fā) Android
貪吃蛇是一款經(jīng)典的游戲,以其簡單易上手、策略性強(qiáng)、挑戰(zhàn)性高等特點(diǎn)深受玩家喜愛。下面我們使用Android原生控件來實(shí)現(xiàn)這個小游戲。

游戲說明

貪吃蛇是一款經(jīng)典的游戲,以其簡單易上手、策略性強(qiáng)、挑戰(zhàn)性高等特點(diǎn)深受玩家喜愛。

游戲玩法:

  • 玩家使用方向鍵操控一條長長的蛇不斷吞下豆子,蛇身隨著吞下的豆子不斷變長
  • 游戲的目標(biāo)是盡可能長時間地生存下去,同時避免蛇頭撞到自己的身體或屏幕邊緣

游戲特點(diǎn):

  • 簡單易上手:游戲操作簡單,玩家只需要控制蛇的移動和轉(zhuǎn)向,吃掉食物即可
  • 策略性:雖然游戲看似簡單,但需要玩家靈活運(yùn)用策略,在有限的空間內(nèi)避免碰撞
  • 挑戰(zhàn)性:游戲難度逐漸增加,隨著蛇身的增長,玩家需要更加謹(jǐn)慎地操作

下面我們使用Android原生控件來實(shí)現(xiàn)這個小游戲(PS:不包含自定義View的方式)。

實(shí)現(xiàn)思路

1.游戲場景

使用GridLayout作為游戲板,大小為20x20,同時包含游戲分?jǐn)?shù)和控制按鈕,下面是布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/scoreTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:text="分?jǐn)?shù): 0"
        android:textSize="18sp" />

    <GridLayout
        android:id="@+id/gameBoard"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:columnCount="20"
        android:rowCount="20" />

    <RelativeLayout
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_gravity="center">

        <Button
            android:id="@+id/upButton"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_centerHorizontal="true"
            android:text="↑" />

        <Button
            android:id="@+id/leftButton"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_centerVertical="true"
            android:text="←" />

        <Button
            android:id="@+id/rightButton"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            android:text="→" />

        <Button
            android:id="@+id/downButton"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:text="↓" />
    </RelativeLayout>
</LinearLayout>

預(yù)覽效果

private fun initializeGame() {
    // 初始化蛇
    snake.add(Pair(boardSize / 2, boardSize / 2))
    // 生成食物
    generateFood()
    // 初始化游戲板
    for (i in 0 until boardSize) {
        for (j in 0 until boardSize) {
            val cell = TextView(this)
            cell.width = 50
            cell.height = 50
            cell.setBackgroundColor(Color.WHITE)
            gameBoard.addView(cell)
        }
    }
    updateBoard()
}

private fun generateFood() {
    do {
        food = Pair(Random.nextInt(boardSize), Random.nextInt(boardSize))
    } while (snake.contains(food))
}
    
private fun updateBoard() {
    for (i in 0 until boardSize) {
        for (j in 0 until boardSize) {
            val cell = gameBoard.getChildAt(i * boardSize + j) as TextView
            when {
                Pair(i, j) == snake.first() -> cell.setBackgroundColor(Color.RED)
                snake.contains(Pair(i, j)) -> cell.setBackgroundColor(Color.GREEN)
                Pair(i, j) == food -> cell.setBackgroundColor(Color.BLUE)
                else -> cell.setBackgroundColor(Color.WHITE)
            }
        }
    }
}

初始化游戲板,大小為20*20,使用TextView作為每個單元格,用于表示可移動的范圍網(wǎng)格。初始化蛇的位置在游戲板中央,蛇被表示為MutableList<Pair<Int, Int>>,每個Pair代表蛇身體的一個部分的坐標(biāo)。同時隨機(jī)在范圍中生成食物,最后更新游戲板給蛇和食物生成不同的顏色樣式。

2.游戲主循環(huán)

此時的游戲是不會動的,需要一個游戲主循環(huán)讓游戲不斷更新才能使游戲畫面動起來,使用Handler定期調(diào)用游戲更新邏輯,每200毫秒更新一次游戲狀態(tài)。

private val updateDelay = 200L // 游戲更新間隔,毫秒

private fun startGameLoop() {
    handler.postDelayed(object : Runnable {
        override fun run() {
            moveSnake()
            checkCollision()
            updateBoard()
            handler.postDelayed(this, updateDelay)
        }
    }, updateDelay)
}

每發(fā)送一次事件對蛇進(jìn)行移動,檢查游戲是否結(jié)束(蛇是否咬到自己),更新GridLayout網(wǎng)格顯示,發(fā)送下一次更新事件

3.蛇的移動

蛇移動的核心邏輯,計算新的蛇頭位置,使用模運(yùn)算確保蛇能夠穿過游戲邊界,檢查是否吃到食物,如果是,增加分?jǐn)?shù)并生成新食物;否則,移除蛇尾。

private fun moveSnake() {
    val head = snake.first()
    val newHead = Pair(
        (head.first + direction.first + boardSize) % boardSize,
        (head.second + direction.second + boardSize) % boardSize
    )
    snake.add(0, newHead)

    if (newHead == food) {
        score++
        scoreTextView.text = "分?jǐn)?shù): $score"
        generateFood()
    } else {
        snake.removeAt(snake.size - 1)
    }
}

(1) 獲取蛇頭位置:

val head = snake.first()

蛇被表示為一個坐標(biāo)對(Pair)的列表,第一個元素是蛇頭。

(2) 計算新的蛇頭位置:

val newHead = Pair(
    (head.first + direction.first + boardSize) % boardSize,
    (head.second + direction.second + boardSize) % boardSize
)

direction(控制的方向)來移動蛇頭,加上 boardSize 并對 boardSize 取模,確保新位置總是在游戲板內(nèi)

direction = Pair(-1, 0) //上
direction = Pair(1, 0)  //下
direction = Pair(0, -1) //左
direction = Pair(0, 1)  //右

(3) 將新的蛇頭添加到蛇身列表的開頭:

snake.add(0, newHead)

蛇一直是在移動的,蛇頭坐標(biāo)一直在變化。

(4) 檢查是否吃到食物:

if (newHead == food) {
    score++
    scoreTextView.text = "Score: $score"
    generateFood()
} else {
    snake.removeAt(snake.size - 1)
}

如果新的蛇頭位置與食物位置相同,增加分?jǐn)?shù),更新分?jǐn)?shù)顯示,并生成新的食物。如果沒有吃到食物,則移除蛇尾,保持蛇的長度不變。

4.碰撞檢測

private fun checkCollision() {
    val head = snake.first()
    if (snake.subList(1, snake.size).contains(head)) {
        // 游戲結(jié)束
        handler.removeCallbacksAndMessages(null)
    }
}

檢查蛇頭是否與蛇身相撞,如果是,游戲結(jié)束。

5.生成食物

private fun generateFood() {
    do {
        food = Pair(Random.nextInt(boardSize), Random.nextInt(boardSize))
    } while (snake.contains(food))
}

隨機(jī)生成新的食物位置,確保不與蛇身重疊

6.顯示更新

private fun updateBoard() {
    for (i in 0 until boardSize) {
        for (j in 0 until boardSize) {
            val cell = gameBoard.getChildAt(i * boardSize + j) as TextView
            when {
                Pair(i, j) == snake.first() -> cell.setBackgroundColor(Color.RED)
                snake.contains(Pair(i, j)) -> cell.setBackgroundColor(Color.GREEN)
                Pair(i, j) == food -> cell.setBackgroundColor(Color.BLUE)
                else -> cell.setBackgroundColor(Color.WHITE)
            }
        }
    }
}

遍歷游戲板的每個單元格,根據(jù)其狀態(tài)(蛇頭、蛇身、食物或空白)設(shè)置不同的顏色。

游戲效果

7.游戲開始結(jié)束

此時的蛇可以掉頭和在游戲場景里穿越,下面我們改進(jìn)一下,蛇撞到游戲邊界游戲結(jié)束

private fun moveSnake() {
    val head = snake.first()
    val newHead = Pair(
        head.first + direction.first,
        head.second + direction.second
    )

    // 檢查是否撞到邊界
    if (newHead.first < 0 || newHead.first >= boardSize || 
        newHead.second < 0 || newHead.second >= boardSize) {
        endGame()
        return
    }

    snake.add(0, newHead)

    if (newHead == food) {
        score++
        scoreTextView.text = "分?jǐn)?shù): $score"
        generateFood()
    } else {
        snake.removeAt(snake.size - 1)
    }
}

添加邊界檢測,檢測到坐標(biāo)在游戲板邊界,游戲結(jié)束

findViewById<Button>(R.id.upButton).setOnClickListener { 
    if (isGameRunning) {
        direction = Pair(-1, 0)
    } else {
        restartGame()
    }
}
findViewById<Button>(R.id.downButton).setOnClickListener { 
    if (isGameRunning) {
        direction = Pair(1, 0)
    } else {
        restartGame()
    }
}
findViewById<Button>(R.id.leftButton).setOnClickListener { 
    if (isGameRunning) {
        direction = Pair(0, -1)
    } else {
        restartGame()
    }
}
findViewById<Button>(R.id.rightButton).setOnClickListener { 
    if (isGameRunning) {
        direction = Pair(0, 1)
    } else {
        restartGame()
    }
}

修改方向按鈕的點(diǎn)擊監(jiān)聽器,使其能夠重新開始游戲

private fun endGame() {
    isGameRunning = false
    handler.removeCallbacksAndMessages(null)
    scoreTextView.text = "游戲結(jié)束!最終分?jǐn)?shù): $score\n點(diǎn)擊任意方向鍵重新開始"
}

private fun restartGame() {
    snake.clear()
    snake.add(Pair(boardSize / 2, boardSize / 2))
    direction = Pair(0, 1)
    score = 0
    generateFood()
    isGameRunning = true
    startGameLoop()
    updateBoard()
    scoreTextView.text = "分?jǐn)?shù): 0"
}

游戲結(jié)束和重新開始,通過isGameRunning變量控制游戲主循環(huán)

private fun startGameLoop() {
    handler.post(object : Runnable {
        override fun run() {
            if (isGameRunning) {
                moveSnake()
                if (isGameRunning) {  // 再次檢查,因?yàn)?moveSnake 可能會結(jié)束游戲
                    checkCollision()
                    updateBoard()
                    handler.postDelayed(this, updateDelay)
                }
            }
        }
    })
}

完整代碼

游戲效果

Github源碼https://github.com/Reathin/Sample-Android/tree/master/module_snake

責(zé)任編輯:趙寧寧 來源: 沐雨花飛蝶
相關(guān)推薦

2021-06-15 09:18:51

鴻蒙HarmonyOS應(yīng)用

2012-06-05 14:42:57

Silverlight

2022-10-28 09:33:10

Linux貪吃蛇

2015-07-31 11:26:24

Swift貪吃蛇

2022-11-07 11:27:00

JS游戲開發(fā)

2020-08-20 20:30:49

C語言小游戲貪吃蛇

2021-09-02 15:25:53

鴻蒙HarmonyOS應(yīng)用

2024-01-18 11:22:41

C++Windows開發(fā)

2022-07-25 14:17:04

JS應(yīng)用開發(fā)

2023-10-17 10:20:53

VueReact

2021-04-20 11:40:12

Linux圖形庫curses

2024-12-09 09:18:21

Android原生控件

2024-12-06 09:20:22

Android游戲新數(shù)字

2025-02-27 09:31:05

2016-09-19 21:24:08

PythonAsyncio游戲

2016-09-14 21:17:47

PythonAsyncio游戲

2016-09-22 21:12:14

2010-02-05 15:00:44

Android 調(diào)用u

2018-08-31 15:48:33

2015-07-07 15:47:57

Razer雷蛇
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號