用AI写游戏3——deepseek实现kotlin android studio greedy snake game 贪吃蛇游戏

项目下载

https://download.csdn.net/download/AnalogElectronic/90421306

项目结构

就是通过android studio 建空项目,改下MainActivity.kt的内容就完事了

ctrl+shift+alt+s 看项目结构如下

核心代码

MainActivity.kt

kotlin 复制代码
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绘制和游戏逻辑的实现。

实现效果

相关推荐
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
FunnySaltyFish2 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker2 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
Kapaseker3 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xiezhr3 天前
米哈游36岁程序员被曝复工当晚猝死出租屋内
游戏·程序员·游戏开发
A0微声z5 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton6 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream6 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam6 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker6 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin