Android Studio贪吃蛇游戏完整开发教程 - 5关卡可调节速度
Android Studio贪吃蛇游戏完整开发教程 - 5关卡可调节速度
项目介绍
这是一个基于Android Studio开发的贪吃蛇游戏,具有5个难度关卡,随着关卡提升蛇的移动速度会逐渐加快。游戏采用按钮控制方式,适合移动设备操作。
功能特点
- 🎮 经典的贪吃蛇游戏玩法
- ⚡ 5个难度关卡,速度逐级提升
- 🎯 直观的按钮方向控制
- 🏆 分数系统和关卡进度显示
- 🎨 不同关卡食物颜色不同
- 🔄 游戏结束可重新开始
开发环境
- 开发工具: Android Studio
- 编程语言: Kotlin
- 最低API级别: 21 (Android 5.0)
- 目标API级别: 33 (Android 13)
项目结构
app/
├── src/main/
│ ├── java/com/example/myapplication/
│ │ ├── MainActivity.kt # 主界面
│ │ ├── GameActivity.kt # 游戏界面
│ │ ├── SnakeGameView.kt # 游戏视图
│ │ └── models/
│ │ ├── Snake.kt # 蛇模型
│ │ └── Food.kt # 食物模型
│ └── res/
│ ├── layout/
│ │ ├── activity_main.xml # 主界面布局
│ │ └── activity_game.xml # 游戏界面布局
│ └── values/
│ ├── colors.xml # 颜色资源
│ └── strings.xml # 字符串资源
完整代码实现
1. 主界面 (MainActivity.kt)
package com.example.myapplicationimport android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Buttonclass MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val startButton: Button = findViewById(R.id.startButton)startButton.setOnClickListener {val intent = Intent(this, GameActivity::class.java)startActivity(intent)}}
}
2. 游戏界面 (GameActivity.kt)
package com.example.myapplicationimport android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.myapplication.models.Snakeclass GameActivity : AppCompatActivity() {private lateinit var gameView: SnakeGameViewprivate lateinit var scoreTextView: TextViewprivate lateinit var levelTextView: TextViewprivate lateinit var upButton: Buttonprivate lateinit var downButton: Buttonprivate lateinit var leftButton: Buttonprivate lateinit var rightButton: Buttonprivate lateinit var handler: Handler// 关卡设置private var currentLevel: Int = 1private val maxLevel: Int = 5private var score: Int = 0private var targetScore: Int = 50 // 第一关目标分数// 不同关卡的速度(毫秒)private val levelSpeeds = mapOf(1 to 300L, // 最慢2 to 250L,3 to 200L,4 to 150L,5 to 100L // 最快)private var currentDelay: Long = levelSpeeds[1] ?: 300Loverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_game)initializeViews()setupButtonListeners()handler = Handler(Looper.getMainLooper())startGameLoop()updateUI()}private fun initializeViews() {gameView = findViewById(R.id.gameView)scoreTextView = findViewById(R.id.scoreTextView)levelTextView = findViewById(R.id.levelTextView)upButton = findViewById(R.id.upButton)downButton = findViewById(R.id.downButton)leftButton = findViewById(R.id.leftButton)rightButton = findViewById(R.id.rightButton)// 设置关卡监听器gameView.setOnScoreChangeListener { newScore ->score = newScorecheckLevelUp()updateUI()}}private fun setupButtonListeners() {upButton.setOnClickListener {if (gameView.snake.direction != Snake.Direction.DOWN) {gameView.snake.direction = Snake.Direction.UP}}downButton.setOnClickListener {if (gameView.snake.direction != Snake.Direction.UP) {gameView.snake.direction = Snake.Direction.DOWN}}leftButton.setOnClickListener {if (gameView.snake.direction != Snake.Direction.RIGHT) {gameView.snake.direction = Snake.Direction.LEFT}}rightButton.setOnClickListener {if (gameView.snake.direction != Snake.Direction.LEFT) {gameView.snake.direction = Snake.Direction.RIGHT}}}private fun checkLevelUp() {if (currentLevel < maxLevel && score >= targetScore) {currentLevel++targetScore = when (currentLevel) {2 -> 100 // 第二关目标3 -> 200 // 第三关目标4 -> 300 // 第四关目标5 -> 500 // 第五关目标else -> 500}currentDelay = levelSpeeds[currentLevel] ?: 100L// 重新开始游戏循环以应用新速度handler.removeCallbacksAndMessages(null)startGameLoop()// 重置游戏但保留关卡gameView.resetGameWithLevel(currentLevel)}}private fun updateUI() {scoreTextView.text = "得分: $score / $targetScore"levelTextView.text = "关卡: $currentLevel/$maxLevel (速度: ${getSpeedDescription()})"}private fun getSpeedDescription(): String {return when (currentLevel) {1 -> "很慢"2 -> "慢"3 -> "中等"4 -> "快"5 -> "很快"else -> "中等"}}private fun startGameLoop() {handler.postDelayed(object : Runnable {override fun run() {gameView.update()gameView.invalidate()handler.postDelayed(this, currentDelay)}}, currentDelay)}override fun onPause() {super.onPause()handler.removeCallbacksAndMessages(null)}override fun onResume() {super.onResume()startGameLoop()}
}
3. 游戏视图 (SnakeGameView.kt)
package com.example.myapplicationimport android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.example.myapplication.models.Snake
import com.example.myapplication.models.Food
import kotlin.random.Randomclass SnakeGameView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {lateinit var snake: Snakeprivate lateinit var food: Foodprivate var score: Int = 0private var gameOver: Boolean = falseprivate var currentLevel: Int = 1// 不同关卡的食物颜色private val levelFoodColors = mapOf(1 to Color.RED,2 to Color.YELLOW,3 to Color.CYAN,4 to Color.MAGENTA,5 to Color.rgb(255, 165, 0) // 橙色)// 画笔private val snakePaint = Paint().apply {color = Color.GREENstyle = Paint.Style.FILL}private val foodPaint = Paint().apply {color = Color.REDstyle = Paint.Style.FILL}private val gridPaint = Paint().apply {color = Color.GRAYstyle = Paint.Style.STROKEstrokeWidth = 1f}private val textPaint = Paint().apply {color = Color.WHITEtextSize = 50ftextAlign = Paint.Align.CENTER}private val levelTextPaint = Paint().apply {color = Color.WHITEtextSize = 30ftextAlign = Paint.Align.LEFT}// 游戏区域参数private var cellSize: Float = 0fprivate var gridWidth: Int = 20private var gridHeight: Int = 30private var onScoreChangeListener: ((Int) -> Unit)? = nullinit {initializeGame()}private fun initializeGame() {snake = Snake(gridWidth / 2, gridHeight / 2)food = generateFood()updateFoodColor()}private fun updateFoodColor() {foodPaint.color = levelFoodColors[currentLevel] ?: Color.RED}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)cellSize = w.toFloat() / gridWidthgridHeight = (h / cellSize).toInt()initializeGame()}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)// 绘制背景canvas.drawColor(Color.BLACK)// 绘制网格for (i in 0..gridWidth) {canvas.drawLine(i * cellSize, 0f, i * cellSize, height.toFloat(), gridPaint)}for (i in 0..gridHeight) {canvas.drawLine(0f, i * cellSize, width.toFloat(), i * cellSize, gridPaint)}// 绘制关卡信息canvas.drawText("关卡 $currentLevel", 20f, 50f, levelTextPaint)if (gameOver) {// 游戏结束显示canvas.drawText("游戏结束!", width / 2f, height / 2f - 80, textPaint)canvas.drawText("最终得分: $score", width / 2f, height / 2f - 20, textPaint)canvas.drawText("达到关卡: $currentLevel", width / 2f, height / 2f + 40, textPaint)canvas.drawText("点击重新开始", width / 2f, height / 2f + 120, textPaint)} else {// 绘制食物canvas.drawRect(food.x * cellSize,food.y * cellSize,(food.x + 1) * cellSize,(food.y + 1) * cellSize,foodPaint)// 绘制蛇for (segment in snake.body) {canvas.drawRect(segment.x * cellSize,segment.y * cellSize,(segment.x + 1) * cellSize,(segment.y + 1) * cellSize,snakePaint)}}}fun update() {if (gameOver) return// 移动蛇snake.move()// 检查是否吃到食物if (snake.body[0].x == food.x && snake.body[0].y == food.y) {snake.grow()food = generateFood()score += 10onScoreChangeListener?.invoke(score)}// 检查游戏结束条件if (checkCollision()) {gameOver = true}}private fun checkCollision(): Boolean {val head = snake.body[0]// 检查是否撞墙if (head.x < 0 || head.x >= gridWidth || head.y < 0 || head.y >= gridHeight) {return true}// 检查是否撞到自己for (i in 1 until snake.body.size) {if (head.x == snake.body[i].x && head.y == snake.body[i].y) {return true}}return false}private fun generateFood(): Food {var newFood: Fooddo {val x = Random.nextInt(0, gridWidth)val y = Random.nextInt(0, gridHeight)newFood = Food(x, y)} while (snake.body.any { segment -> segment.x == x && segment.y == y })return newFood}override fun onTouchEvent(event: MotionEvent): Boolean {if (event.action == MotionEvent.ACTION_DOWN) {if (gameOver) {// 重新开始游戏resetGame()return true}}return super.onTouchEvent(event)}private fun resetGame() {currentLevel = 1snake = Snake(gridWidth / 2, gridHeight / 2)food = generateFood()score = 0gameOver = falseupdateFoodColor()onScoreChangeListener?.invoke(score)invalidate()}fun resetGameWithLevel(level: Int) {currentLevel = levelsnake = Snake(gridWidth / 2, gridHeight / 2)food = generateFood()gameOver = falseupdateFoodColor()invalidate()}fun setOnScoreChangeListener(listener: (Int) -> Unit) {onScoreChangeListener = listener}
}
4. 数据模型
Snake.kt (在 models 包中):
package com.example.myapplication.modelsdata class Point(var x: Int, var y: Int)class Snake(startX: Int, startY: Int) {enum class Direction {UP, DOWN, LEFT, RIGHT}var direction = Direction.RIGHTvar body = mutableListOf<Point>()init {// 初始化蛇身,长度为3body.add(Point(startX, startY))body.add(Point(startX - 1, startY))body.add(Point(startX - 2, startY))}fun move() {// 移动蛇身for (i in body.size - 1 downTo 1) {body[i].x = body[i - 1].xbody[i].y = body[i - 1].y}// 移动蛇头when (direction) {Direction.UP -> body[0].y--Direction.DOWN -> body[0].y++Direction.LEFT -> body[0].x--Direction.RIGHT -> body[0].x++}}fun grow() {// 在蛇尾添加新的身体部分val tail = body.last()body.add(Point(tail.x, tail.y))}
}
Food.kt (在 models 包中):
package com.example.myapplication.modelsdata class Food(var x: Int, var y: Int)
5. 布局文件
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center"android:background="@color/black"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="贪吃蛇游戏"android:textColor="@color/white"android:textSize="36sp"android:layout_marginBottom="50dp" /><Buttonandroid:id="@+id/startButton"android:layout_width="200dp"android:layout_height="wrap_content"android:text="开始游戏"android:textSize="24sp" /></LinearLayout>
activity_game.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="@color/black"><!-- 顶部信息栏 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="10dp"android:gravity="center"><TextViewandroid:id="@+id/scoreTextView"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="得分: 0/50"android:textColor="@color/white"android:textSize="18sp"android:gravity="center" /><TextViewandroid:id="@+id/levelTextView"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="关卡: 1/5 (速度: 很慢)"android:textColor="@color/white"android:textSize="18sp"android:gravity="center" /></LinearLayout><com.example.myapplication.SnakeGameViewandroid:id="@+id/gameView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /><!-- 方向控制按钮 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:gravity="center"android:padding="20dp"android:background="@color/purple_200"><!-- 上方向 --><Buttonandroid:id="@+id/upButton"android:layout_width="80dp"android:layout_height="60dp"android:text="↑"android:textSize="24sp"android:layout_marginBottom="10dp"android:backgroundTint="@color/teal_200" /><!-- 左右方向 --><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:id="@+id/leftButton"android:layout_width="80dp"android:layout_height="60dp"android:text="←"android:textSize="24sp"android:layout_marginEnd="20dp"android:backgroundTint="@color/teal_200" /><Buttonandroid:id="@+id/rightButton"android:layout_width="80dp"android:layout_height="60dp"android:text="→"android:textSize="24sp"android:layout_marginStart="20dp"android:backgroundTint="@color/teal_200" /></LinearLayout><!-- 下方向 --><Buttonandroid:id="@+id/downButton"android:layout_width="80dp"android:layout_height="60dp"android:text="↓"android:textSize="24sp"android:layout_marginTop="10dp"android:backgroundTint="@color/teal_200" /></LinearLayout></LinearLayout>
6. 资源文件
colors.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources><color name="black">#FF000000</color><color name="white">#FFFFFFFF</color><color name="purple_200">#FFBB86FC</color><color name="purple_500">#FF6200EE</color><color name="purple_700">#FF3700B3</color><color name="teal_200">#FF03DAC5</color><color name="teal_700">#FF018786</color>
</resources>
strings.xml:
<resources><string name="app_name">贪吃蛇游戏</string>
</resources>
7. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.myapplication"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:theme="@style/Theme.MyApplication"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name=".GameActivity"android:screenOrientation="portrait" /></application>
</manifest>
游戏特性说明
关卡系统
- 第1关: 速度很慢 (300ms),目标50分
- 第2关: 速度慢 (250ms),目标100分
- 第3关: 速度中等 (200ms),目标200分
- 第4关: 速度快 (150ms),目标300分
- 第5关: 速度很快 (100ms),目标500分
控制方式
- 使用屏幕底部的方向按钮控制蛇的移动
- 防止直接反向移动导致游戏结束
- 点击屏幕重新开始游戏
视觉效果
- 不同关卡食物颜色不同
- 网格背景便于定位
- 实时显示分数和关卡信息
开发要点
- 自定义View: 使用
SnakeGameView继承View实现游戏绘制 - 游戏循环: 使用
Handler实现定时游戏更新 - 碰撞检测: 检测墙壁碰撞和自身碰撞
- 状态管理: 管理游戏状态(进行中/结束)
- 关卡逻辑: 实现分数累计和关卡升级机制
运行效果
游戏启动后进入主界面,点击"开始游戏"进入游戏界面。使用底部方向按钮控制蛇的移动,吃到食物增加分数,达到目标分数后进入下一关,速度逐渐加快。撞墙或撞到自身游戏结束,点击屏幕重新开始。

总结
这个贪吃蛇游戏项目展示了Android开发中的自定义View、游戏循环、触摸事件处理等关键技术。通过5个关卡的设置,增加了游戏的可玩性和挑战性。代码结构清晰,适合Android初学者学习和参考。
项目源码:贪吃蛇游戏源码 提取码:9999
欢迎在评论区交流学习心得,如有问题请随时提问!
