用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绘制和游戏逻辑的实现。

实现效果

相关推荐
Jae den11 小时前
网络延迟为什么会影响游戏和实时业务?
网络·游戏
新时代牛马11 小时前
cyclictest 毛刺从哪来?从ftrace、latencytop、perf到IRQ/调度延迟定位
android·开发语言·python·kotlin
光电的一只菜鸡13 小时前
为什么调整LSC会对AF产生影响
android·开发语言·kotlin
hai_android14 小时前
FusibleFlow:Kotlin Flow 的融合机制原理
android·kotlin
思盛iOS签名上架14 小时前
ipa企业签名闪退是什么原因?
游戏·ios·testfight
狂人开飞机16 小时前
14、游戏手感与视觉打磨
游戏·游戏引擎·godot
alexhilton18 小时前
从零开始的架构测试套件
android·kotlin·android jetpack
YB137521 小时前
kotlin 关键字data
kotlin
挂科边缘1 天前
Android Studio 保姆级安装教程,Windows 电脑上安装 Android Studio
android·windows·android studio
Danny_姜1 天前
Android Studio Quail 4 稳定版发布:本地 Gemma 4 + Android Skills
android·ide·android studio