项目下载
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 += 10food = 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 = truescore = 0snake.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)
}
实现效果