项目下载
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)
}
实现效果