3.添加蛇和食物
/**
- 贪吃蛇游戏 - 主页面
- 精简版:保留蛇和食物的显示功能
- 核心功能:
- 绘制游戏画布(背景、网格)
- 绘制蛇(绿色身体 + 红色头部)
- 绘制食物(黄色圆形)
*/
- 绘制食物(黄色圆形)
/**
- 游戏主组件
- @Entry 标记为页面入口组件
- @Component 标记为UI组件
*/
@Entry @Component struct ParentComp { // ==================== 画布相关变量 ==================== /** 画布渲染设置:开启抗锯齿,使图形边缘更平滑 */ private settings: RenderingContextSettings = new RenderingContextSettings(true) /** 画布绘图上下文:所有绘图操作都通过此对象执行 */ private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) /** 画布尺寸(正方形),根据屏幕宽度动态计算 */ @State canvasSize: number = 300 /** 每个网格方块的宽度 = canvasSize / 20(游戏区域是20x20的网格) */ @State rectWidth: number = 15 // ==================== 蛇的属性变量 ==================== /** 蛇的长度(初始4节) */ @State snakeLength: number = 4 /** 蛇身每节的X坐标数组(网格坐标,0-19) */ @State snakeX: number[] = [0, 1, 2, 3] /** 蛇身每节的Y坐标数组(网格坐标,0-19) */ @State snakeY: number[] = [0, 0, 0, 0] // ==================== 食物属性变量 ==================== /** 食物的X坐标(网格坐标) */ @State foodX: number = 8 /** 食物的Y坐标(网格坐标) */ @State foodY: number = 5 /** 画布是否准备就绪(用于确保在Canvas初始化后再绘图) */ @State isCanvasReady: boolean = false /** * 构建UI界面 */ build() { // Column:垂直布局容器,子组件从上到下排列 Column() { // ---------- 标题区域 ---------- Text('🐍 贪吃蛇') .fontSize(48) // 字体大小 .fontWeight(FontWeight.Bold) // 粗体 .fontColor('#fff') // 白色字体 .margin({ top: 20, bottom: 10 }) // 外边距 .textAlign(TextAlign.Center) // 居中对齐 // ---------- 游戏画布 ---------- Canvas(this.context) .width(this.canvasSize) // 宽度 .height(this.canvasSize) // 高度(正方形) .backgroundColor('#1a1a2e') // 深蓝色背景 .borderRadius(20) // 圆角 .shadow({ // 阴影效果 radius: 20, color: 'rgba(0, 0, 0, 0.5)', offsetX: 0, offsetY: 10 }) // onReady:画布准备就绪时的回调(在此回调中才能开始绘图) .onReady(() => { this.isCanvasReady = true // 标记画布已就绪 this.draw() // 绘制初始画面 }) // ---------- 开始游戏按钮 ---------- Button("🎮 开始游戏") .width('40%') .height(40) .fontSize(16) .backgroundColor('#4a90d9') .fontColor('#fff') .borderRadius(20) .margin({ top: 10 }) // ---------- 操作提示 ---------- Text('使用方向键控制蛇的移动') .fontSize(10) .fontColor('#888') .margin({ top: 20, bottom: 20 }) .textAlign(TextAlign.Center) } .width('100%') .height('100%') .backgroundColor('#0f0f23') // 页面背景色(深紫色) .justifyContent(FlexAlign.Center) // 垂直居中对齐 } /** * 绘制游戏画面 * 绘图顺序(从后到前): * 1. 背景 → 2. 网格线 → 3. 蛇身 → 4. 蛇头 → 5. 食物 */ private draw(): void { // ---------- 1. 绘制背景 ---------- this.context.fillStyle = '#1a1a2e' // 背景颜色 this.context.fillRect(0, 0, this.canvasSize, this.canvasSize) // 填充整个画布 // ---------- 2. 绘制网格线 ---------- this.context.strokeStyle = '#2d2d44' // 网格线颜色(浅灰色) this.context.lineWidth = 1 // 线宽 // 绘制21条垂直线(分隔20个网格) for (let i = 0; i <= 20; i++) { this.context.beginPath() this.context.moveTo(i * this.rectWidth, 0) this.context.lineTo(i * this.rectWidth, this.canvasSize) this.context.stroke() } // 绘制21条水平线 for (let i = 0; i <= 20; i++) { this.context.beginPath() this.context.moveTo(0, i * this.rectWidth) this.context.lineTo(this.canvasSize, i * this.rectWidth) this.context.stroke() } // ---------- 3. 绘制蛇身(绿色,带发光效果) ---------- this.context.fillStyle = '#00ff88' // 蛇身颜色(绿色) this.context.shadowColor = '#00ff88' // 发光颜色 this.context.shadowBlur = 8 // 发光模糊度 // 遍历蛇身(不包括蛇头) for (let i = 0; i < this.snakeLength - 1; i++) { this.roundRect( this.snakeX[i] * this.rectWidth + 2, // X坐标(加2像素内边距) this.snakeY[i] * this.rectWidth + 2, // Y坐标 this.rectWidth - 4, // 宽度(减4像素内边距) this.rectWidth - 4, // 高度 5 // 圆角半径 ) } // ---------- 4. 绘制蛇头(红色,带更强的发光效果) ---------- this.context.fillStyle = '#ff6b6b' // 蛇头颜色(红色) this.context.shadowColor = '#ff6b6b' // 发光颜色 this.context.shadowBlur = 12 // 更强的发光效果 this.roundRect( this.snakeX[this.snakeLength - 1] * this.rectWidth + 2, // 蛇头X坐标 this.snakeY[this.snakeLength - 1] * this.rectWidth + 2, // 蛇头Y坐标 this.rectWidth - 4, this.rectWidth - 4, 6 // 蛇头圆角更大 ) // ---------- 5. 绘制食物(黄色圆形,带最强发光效果) ---------- this.context.fillStyle = '#ffd93d' // 食物颜色(黄色) this.context.shadowColor = '#ffd93d' // 发光颜色 this.context.shadowBlur = 15 // 最强发光效果 this.context.beginPath() // 绘制圆形:圆心在食物位置,半径为方块宽度的一半 this.context.arc( this.foodX * this.rectWidth + this.rectWidth / 2, // 圆心X this.foodY * this.rectWidth + this.rectWidth / 2, // 圆心Y this.rectWidth / 2 - 3, // 半径(减3像素内边距) 0, // 起始角度 Math.PI * 2 // 结束角度(360度) ) this.context.fill() // 重置阴影效果(避免影响后续绘制) this.context.shadowBlur = 0 } /** * 绘制圆角矩形的辅助方法 * Canvas 没有直接的圆角矩形API,需要使用路径手动绘制 * * @param x 左上角的X坐标 * @param y 左上角的Y坐标 * @param width 矩形宽度 * @param height 矩形高度 * @param radius 圆角半径 */ private roundRect(x: number, y: number, width: number, height: number, radius: number): void { this.context.beginPath() this.context.moveTo(x + radius, y) // 从左上角右边开始 this.context.lineTo(x + width - radius, y) // 上边直线 this.context.quadraticCurveTo(x + width, y, x + width, y + radius) // 右上角圆角 this.context.lineTo(x + width, y + height - radius) // 右边直线 this.context.quadraticCurveTo(x + width, y + height, x + width - radius, y + height) // 右下角圆角 this.context.lineTo(x + radius, y + height) // 下边直线 this.context.quadraticCurveTo(x, y + height, x, y + height - radius) // 左下角圆角 this.context.lineTo(x, y + radius) // 左边直线 this.context.quadraticCurveTo(x, y, x + radius, y) // 左上角圆角 this.context.closePath() // 闭合路径 this.context.fill() // 填充颜色 } }