项目下载

https://download.csdn.net/download/AnalogElectronic/90421306
在这里插入图片描述

项目结构

在这里插入图片描述
就是通过android studio 建空项目,改下MainActivity.kt的内容就完事了

ctrl+shift+alt+s 看项目结构如下
在这里插入图片描述

核心代码

MainActivity.kt

package com.example.snakegame1

// MainActivity.kt
import android.content.ContentValues.TAG
import android.view.KeyEvent
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.*
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import java.util.*
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
import kotlin.math.abs
import kotlin.math.roundToInt

// 游戏配置常量
const val CELL_SIZE = 30f      // 每个网格单元大小
const val GRID_SIZE = 20       // 网格行列数
const val GAME_SPEED = 150L    // 游戏刷新速度(毫秒)

// 方向枚举类
enum class Direction { UP, DOWN, LEFT, RIGHT }

// 坐标数据类
data class Point(val x: Int, val y: Int)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SnakeGame()
        }
    }
}

@Composable
fun SnakeGame() {
    // 游戏状态控制
    var isPlaying by remember { mutableStateOf(true) }
    var score by remember { mutableStateOf(0) }
    Log.d(TAG, "游戏是否启动: $isPlaying")

    // 蛇的初始状态
    val snake = remember {
        mutableStateOf(
            Snake(
                body = listOf(Point(GRID_SIZE/2, GRID_SIZE/2)),
                direction = Direction.RIGHT
            )
        )
    }

    // 食物位置
    var food by remember { mutableStateOf(generateFood(snake.value.body)) }

    // 游戏循环控制
    LaunchedEffect(key1 = isPlaying) {
        while (isPlaying) {
            delay(GAME_SPEED)
            snake.value = snake.value.move()

            // 检测是否吃到食物
            if (snake.value.body.first() == food) {
                score += 10
                food = generateFood(snake.value.body)
                snake.value = snake.value.grow()
            }

            // 检测碰撞
            if (checkCollision(snake.value.body)) {
                isPlaying = false
            }
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xFF2B2B2B))
            .pointerInput(Unit) {
                // 处理触摸或鼠标事件
                detectDragGestures { _, dragAmount ->
                    // 根据拖动方向改变蛇的方向
                    if (abs(dragAmount.x) > abs(dragAmount.y)) {
                        if (dragAmount.x > 0) {
                            snake.value = snake.value.turn(Direction.RIGHT)
                        } else {
                            snake.value = snake.value.turn(Direction.LEFT)
                        }
                    } else {
                        if (dragAmount.y > 0) {
                            snake.value = snake.value.turn(Direction.DOWN)
                        } else {
                            snake.value = snake.value.turn(Direction.UP)
                        }
                    }
                }
            }
            .focusable(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        // 游戏画布
        Canvas(
            modifier = Modifier
                .size((CELL_SIZE * GRID_SIZE).dp)
                .background(Color.Black)
        ) {
            // 绘制网格线
            for (i in 0..GRID_SIZE) {
                drawLine(
                    color = Color.Gray.copy(alpha = 0.3f),
                    start = Offset(i * CELL_SIZE, 0f),
                    end = Offset(i * CELL_SIZE, size.height),
                    strokeWidth = 1f
                )
                drawLine(
                    color = Color.Gray.copy(alpha = 0.3f),
                    start = Offset(0f, i * CELL_SIZE),
                    end = Offset(size.width, i * CELL_SIZE),
                    strokeWidth = 1f
                )
            }

            // 绘制食物
            drawCircle(
                color = Color.Red,
                center = Offset(
                    food.x * CELL_SIZE + CELL_SIZE / 2,
                    food.y * CELL_SIZE + CELL_SIZE / 2
                ),
                radius = CELL_SIZE / 3
            )

            // 绘制蛇身
            snake.value.body.forEachIndexed { index, point ->
                val color = if (index == 0) Color.Green else Color(0xFF4CAF50)
                drawCircle(
                    color = color,
                    center = Offset(
                        point.x * CELL_SIZE + CELL_SIZE / 2,
                        point.y * CELL_SIZE + CELL_SIZE / 2
                    ),
                    radius = CELL_SIZE / 2.5f,
                    style = Stroke(width = 3f)
                )
            }
        }

        // 重新开始按钮
        if (!isPlaying) {
            Button(
                onClick = {
                    // 重置游戏状态
                    isPlaying = true
                    score = 0
                    snake.value = Snake(
                        body = listOf(Point(GRID_SIZE/2, GRID_SIZE/2)),
                        direction = Direction.RIGHT
                    )
                    food = generateFood(snake.value.body)
                },
                modifier = Modifier.padding(8.dp)
            ) {
                Text("重新开始")
            }
        }
    }
}

// 蛇类定义
class Snake(
    val body: List<Point>,
    val direction: Direction
) {
    // 移动方法
    fun move(): Snake {
        val head = body.first()
        val newHead = when (direction) {
            Direction.UP -> head.copy(y = head.y - 1)
            Direction.DOWN -> head.copy(y = head.y + 1)
            Direction.LEFT -> head.copy(x = head.x - 1)
            Direction.RIGHT -> head.copy(x = head.x + 1)
        }
        return copy(body = listOf(newHead) + body.dropLast(1))
    }

    // 转向方法
    fun turn(newDirection: Direction): Snake {
        // 禁止反向移动
        if ((direction == Direction.UP && newDirection == Direction.DOWN) ||
            (direction == Direction.DOWN && newDirection == Direction.UP) ||
            (direction == Direction.LEFT && newDirection == Direction.RIGHT) ||
            (direction == Direction.RIGHT && newDirection == Direction.LEFT)
        ) {
            return this
        }
        return copy(direction = newDirection)
    }

    // 增长方法
    fun grow(): Snake {
        val tail = body.last()
        val newTail = when (direction) {
            Direction.UP -> tail.copy(y = tail.y + 1)
            Direction.DOWN -> tail.copy(y = tail.y - 1)
            Direction.LEFT -> tail.copy(x = tail.x + 1)
            Direction.RIGHT -> tail.copy(x = tail.x - 1)
        }
        return copy(body = body + newTail)
    }

    private fun copy(
        body: List<Point> = this.body,
        direction: Direction = this.direction
    ) = Snake(body, direction)
}

// 生成食物位置
fun generateFood(snakeBody: List<Point>): Point {
    val random = Random()
    while (true) {
        val newFood = Point(
            random.nextInt(GRID_SIZE),
            random.nextInt(GRID_SIZE)
        )
        if (newFood !in snakeBody) return newFood
    }
}

// 碰撞检测
fun checkCollision(body: List<Point>): Boolean {
    val head = body.first()
    return head.x < 0 || head.x >= GRID_SIZE ||
            head.y < 0 || head.y >= GRID_SIZE ||
            head in body.drop(1)
}

这段代码实现了一个经典的贪吃蛇游戏,使用Kotlin和Jetpack Compose来构建。让我逐步解释代码的功能和结构:

  1. 包和导入

    • package com.example.snakegame1:定义了应用的包名。
    • 导入了Android和Jetpack Compose的相关库,用于构建UI和处理输入事件。
  2. MainActivity类

    • 继承自ComponentActivity,这是Compose支持的Activity基类。
    • onCreate方法设置内容为SnakeGame Composable函数,启动游戏。
  3. SnakeGame Composable函数

    • 使用remember保存游戏状态,如isPlaying(游戏是否进行)和score(得分)。
    • 定义了蛇的初始状态和食物的位置。
  4. 游戏逻辑

    • LaunchedEffect用于启动游戏循环,当isPlaying为true时持续运行。
    • 每隔GAME_SPEED毫秒更新一次蛇的位置。
    • 检测蛇是否吃到食物,增加得分并生成新的食物。
    • 检测碰撞,如果蛇撞到边界或自身,游戏结束。
  5. UI绘制

    • 使用Canvas绘制游戏画面。
    • 绘制网格线、食物和蛇的身体。
    • 蛇的头部和身体部分使用不同的颜色,便于区分。
  6. 输入控制

    • 使用PointerInput处理拖动事件,根据拖动方向改变蛇的移动方向。
    • 禁止蛇反向移动,确保游戏逻辑正确。
  7. 辅助函数和类

    • generateFood函数生成不在蛇身上的食物位置。
    • Snake类包含蛇的身体和方向,并有移动、转向和增长的方法。
    • checkCollision函数检测蛇是否撞到边界或自身。

总结
这段代码通过Jetpack Compose构建了一个贪吃蛇游戏,涵盖了游戏循环、输入控制、碰撞检测和得分系统。对于Kotlin和Compose的初学者来说,这是一个很好的学习项目,可以帮助理解状态管理、UI绘制和游戏逻辑的实现。
实现效果
在这里插入图片描述

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐