游戏区域表示(@State area:number [][]):
area
是一个二维数组,它在整个贪吃蛇游戏中扮演着至关重要的角色,代表着游戏的运动区域。这个二维数组中的每个元素都存储着一个数字,这些数字代表着不同的游戏元素状态。initArea
函数进行初始化。初始化后,它将变成一个 25x25 的二维数组,其中每个位置的值初始为 0。这个 0 值在游戏中代表着空白区域,即没有蛇的身体部分也没有能量(食物)的位置。方向控制(@State directions 和 @State tryDirections):
directions
表示当前运动方向,它可以取值为 'up'(上)、'down'(下)、'left'(左)或 'right'(右)。初始时被设置为 'right',这意味着蛇在游戏开始时将向右移动。tryDirections
用于存储玩家尝试改变的方向,同样可以取上述四个方向的值。它的作用是当玩家按下方向控制按钮时,这个变量会被更新为玩家想要尝试的方向。但是,蛇不能立即向相反的方向移动,所以在更新实际的移动方向时,需要检查tryDirections
与当前的directions
是否冲突。如果不冲突,才会更新directions
为tryDirections
的值。历史位置记录(@State historyPosition:number [][]):
historyPosition
是一个数组,其中的每个元素也是一个数组,存储着蛇在游戏过程中经过的位置。这个数组在蛇移动时起着关键作用,用于更新蛇头和蛇尾的位置。头部坐标和长度(@State headX、@State headY 和 @State length):
headX
和headY
分别表示蛇头的横坐标和纵坐标。它们确定了蛇在游戏区域中的具体位置。初始时,蛇头的位置是通过initArea
函数设置的,通常在游戏区域的特定位置。length
表示蛇的长度。这个变量在游戏开始时根据初始蛇的长度进行设置,并且会随着蛇吃到能量而增加。蛇的长度是衡量游戏进程的一个重要指标,它不仅影响着游戏的难度,还决定了蛇是否占满全屏从而导致游戏结束。颜色定义(@State colors:ResourceColor []):
colors
是一个数组,定义了不同状态所对应的颜色。这个数组中的元素分别对应着游戏中的不同元素。例如,数组中的第一个元素通常是透明颜色,代表空白区域;第二个元素可能是黑色,代表蛇的身体部分;第三个元素可能是橙色,代表能量的颜色。结束提示(@State end:string):
end
是一个字符串变量,用于存储游戏结束的提示信息。初始状态下,它是空字符串,表示游戏正在进行中。end
将被设置为 “吃到自己了 游戏结束!”;如果蛇撞墙了,end
将被设置为 “撞墙了 游戏结束!”。这个提示信息可以在游戏界面中显示给玩家,让他们知道游戏已经结束以及结束的原因。 // 二维数组 表示运动区域
@State area:number[][] = []
// 当前运动方向
@State directions: 'up'| 'down'| 'left'| 'right' = 'right'
// 控制方向
@State tryDirections: 'up'| 'down'| 'left'| 'right' = 'right'
// 历史位置 用于蛇移动时更新蛇头和蛇尾
@State historyPosition:number[][]=[]
// 头部坐标
@State headX:number = 0
@State headY:number = 0
// 蛇的长度
@State length:number = 0
// 颜色 不同的表示渲染不同等等颜色
@State colors:ResourceColor[]=[Color.Transparent, Color.Black, Color.Orange]
//结束提示
@State end:string=''
area
。它通过两个嵌套的循环,将area
初始化为一个 25x25 的二维数组,每个位置的值都设置为 0。这一步确保了游戏在每次开始或重新开始时,游戏区域都是干净的,没有任何蛇的身体部分或能量。area[4][6]
到area[4][10]
的值设置为 1,表示蛇的身体部分。这意味着在游戏开始时,蛇的身体占据了这些位置。同时,将这些位置添加到historyPosition
数组中,以便在游戏过程中能够跟踪蛇的移动。headX
和headY
设置为蛇头的初始位置,确保蛇头在游戏开始时位于正确的位置。同时,初始化蛇的长度length
为初始蛇的长度,通常是根据初始蛇的身体部分数量来确定。此外,清空结束提示信息end
,将其设置为空字符串,表示游戏正在进行中。最后,将方向directions
和tryDirections
都设置为右,确定蛇的初始移动方向为向右。 // 初始化
initArea(){
// 25 * 25 初始化地图
this.area = []
for(let i=0;i<25;i++){
this.area.push([])
for(let j=0;j<25;j++){
this.area[i].push(0)
}
}
// 对蛇初始化
this.area[4][6]=1
this.area[4][7]=1
this.area[4][8]=1
this.area[4][9]=1
this.area[4][10]=1
// 初始化历史位置
this.historyPosition=[]
this.historyPosition.push([6,4])
this.historyPosition.push([7,4])
this.historyPosition.push([8,4])
this.historyPosition.push([9,4])
this.historyPosition.push([10,4])
// 初始化头部位置
this.headX = 10
this.headY = 4
// 初始化蛇的长度
this.length = 5
// 初始化 结束提示
this.end=''
// 初始化方向以及想尝试的方向 为右
this.directions = 'right'
this.tryDirections = 'right'
}
initArea
函数进行初始化,确保游戏以初始状态开始。然后,它会随机生成能量(食物)的位置。通过生成两个随机数foodX
和foodY
,分别代表能量在游戏区域中的横坐标和纵坐标。这两个随机数的范围是游戏区域的宽度和高度,确保能量可以出现在游戏区域的任何位置。setInterval
实现游戏的定时更新,每隔 200 毫秒执行一次更新操作。这个定时更新是游戏的核心逻辑之一,它确保了游戏的持续进行和动态变化。end
为空字符串),首先判断是否需要生成新的能量。如果当前位置的能量未被吃到(即area[foodY][foodX]!=2
)且蛇的长度未占满全屏(即this.length<25*25
),则进入生成新能量的逻辑。通过不断随机生成坐标,直到找到一个值为 0 的位置(即空白位置),将该位置的值设置为 2,表示能量的位置。这个过程确保了在游戏过程中,始终有能量可供蛇吃到,从而增加游戏的趣味性和挑战性。tryDirections
是否与当前移动方向directions
不冲突,如果不冲突,则更新当前移动方向为尝试改变的方向。然后根据当前移动方向计算蛇头的新坐标。例如,如果方向为向右,则新的横坐标newHeadX
为当前横坐标headX + 1
,纵坐标newHeadY
保持不变。这个计算过程确保了蛇的移动是符合逻辑的,并且不会出现不合理的移动方向。historyPosition
中,并更新蛇头的坐标headX
和headY
。同时,蛇的长度length
加一。如果新位置不是能量,则需要更新蛇尾的位置。获取尾巴的位置(即历史位置数组的第一个元素),将旧尾巴的位置在游戏区域中的值设置为 0,表示尾巴移动。然后从历史位置数组中移除尾巴的位置。这个处理过程实现了蛇的移动和生长,根据蛇是否吃到能量来决定如何更新蛇的位置和长度。 aboutToAppear(): void {
// 初始化
this.initArea()
// 随机生成能量的位置
let foodX:number = Math.floor(Math.random()*this.area[0].length)
let foodY:number = Math.floor(Math.random()*this.area[0].length)
// 控制蛇的移动(更新坐标)
setInterval(()=>{
// 游戏结束后不做操作
if(!this.end){
// 上一个还没吃到 或 蛇的长度占满全屏 不用生成
if(this.area[foodY][foodX]!=2&&this.length<25*25){
while(true){
// 生成的随机坐标为0时即为空位置时才生成能量退出循环
if(this.area[foodY][foodX]==0){
this.area[foodY][foodX]=2
break
}
// 继续生成
foodX = Math.floor(Math.random()*this.area[0].length)
foodY = Math.floor(Math.random()*this.area[0].length)
}
}
// 移动后头部的新位置
let newHeadY:number
let newHeadX:number
// 当控制方向和当前移动的方向满足条件时 才改变当前位置 避免蛇头往身后走
if(this.tryDirections == 'right' && this.directions != 'left') this.directions = 'right'
else if(this.tryDirections == 'left' && this.directions != 'right') this.directions = 'left'
else if(this.tryDirections == 'up' && this.directions != 'down') this.directions = 'up'
else if(this.tryDirections == 'down' && this.directions != 'up') this.directions = 'down'
// 不同的方向更新不同的头部
if(this.directions == 'right'){
newHeadY = this.headY
newHeadX = this.headX+1
}else if(this.directions == 'up'){
newHeadY = this.headY-1
newHeadX = this.headX
}else if(this.directions == 'down'){
newHeadY = this.headY+1
newHeadX = this.headX
}else{
newHeadY = this.headY
newHeadX = this.headX-1
}
// 判断是否撞墙
if(newHeadY>=0 && newHeadY=0 && newHeadX
在 build
方法中,整体构建了贪吃蛇游戏的界面和交互逻辑。
Column
布局,作为游戏的主要容器。Stack
布局包含游戏区域和结束提示。
Column
和 ForEach
循环遍历 area
数组,对每个位置进行渲染。对于每个位置,根据位置是否为蛇头、是否为能量以及是否为蛇的身体部分,设置不同的背景颜色和显示内容。如果位置是蛇头,则显示一个特定的字符(如 “・”),并设置为白色、加粗字体。如果位置是蛇的身体部分,则根据历史位置数组判断该位置是否为蛇尾,如果是蛇尾,则设置为粉色背景,否则根据颜色数组设置为蛇的身体颜色。如果位置是能量,则设置为橙色背景。end
不为空字符串时,显示结束提示信息,并显示重新开始按钮。重新开始按钮点击时调用 initArea
函数重新初始化游戏。 Stack(){
// 区域
Column(){
// 遍历每一行
ForEach(this.area,(itemR:number[], indexR:number)=>{
Row(){
// 遍历每行的每一个位置
ForEach(itemR,(itemC:number, indexC:number)=>{
// 对每一个位置进行渲染 0:空位置 1:蛇的部位 2:能量 以及是否为蛇头
Text(indexR == this.headY && indexC == this.headX?'·' : '')
.textAlign(TextAlign.Center)
.fontSize(12)
.fontWeight(900)
.fontColor(Color.White)
.width(12)
.aspectRatio(1)
.backgroundColor(this.historyPosition[this.historyPosition.length-1][0] == indexC &&
this.historyPosition[this.historyPosition.length-1][1] == indexR?
Color.Pink : this.colors[this.area[indexR][indexC]])
})
}
})
}
.borderWidth(2)
.borderColor(Color.Orange)
// 结束时展示提示
Column({space:10}){
Text(this.end)
.fontSize(20)
.fontColor(Color.Red)
Text(this.end?'重新开始':'')
.fontSize(22)
.fontColor(Color.Red)
.onClick(()=>{
this.initArea()
})
}
.zIndex(this.end?1:-1) //结束时置z轴为1 展示出来
}
Stack
布局包含上、下、左、右四个方向的控制按钮。每个按钮设置了不同的属性,如字体大小、高度、宽度、位置等。点击每个按钮时,更新 tryDirections
的值,以尝试改变蛇的移动方向。当游戏结束时(即end
不为空字符串),控制按钮被禁用,通过设置enabled
属性为 false 实现。 // 控制键
Stack(){
// 上、下、左、右 逻辑一样 以"上"操作为例
Button('上')
.fontSize(20)
.height(50)
.width(80)
.position({top:0, left:'50%'})
.translate({x:'-50%'})
.onClick(()=>{
this.tryDirections = 'up'
})
Button('下')
.fontSize(20)
.height(50)
.width(80)
.position({bottom:0, left:'50%'})
.translate({x:'-50%'})
.onClick(()=>{
this.tryDirections = 'down'
})
Button('左')
.fontSize(20)
.height(50)
.width(70)
.position({left:0, top:'50%'})
.translate({y:'-50%'})
.offset({left:0})
.onClick(()=>{
this.tryDirections = 'left'
})
Button('右')
.fontSize(20)
.height(50)
.width(70)
.position({right:0, top:'50%'})
.translate({y:'-50%'})
.onClick(()=>{
this.tryDirections = 'right'
})
}
.enabled(this.end?false:true) // 游戏结束时为禁用态 按钮失效
.width('45%')
.aspectRatio(1)
游戏区域表示(@State area:number [][]):
area
作为游戏区域的抽象表示,为游戏的进行提供了一个可视化的基础。通过这个二维数组,游戏可以清晰地展示蛇的位置、能量的位置以及空白区域,使玩家能够直观地了解游戏的状态。方向控制(@State directions 和 @State tryDirections):
directions
和tryDirections
这两个变量共同控制着蛇的移动方向。它们的存在使得玩家可以通过控制按钮来尝试改变蛇的移动方向,增加了游戏的互动性和趣味性。历史位置记录(@State historyPosition:number [][]):
historyPosition
记录了蛇在游戏过程中的历史位置,这对于更新蛇的外观和判断游戏是否结束至关重要。头部坐标和长度(@State headX、@State headY 和 @State length):
headX
和headY
确定了蛇头在游戏区域中的具体位置,是蛇在游戏中的关键部分。它们的变化直接影响着游戏的进程和结果。length
表示蛇的长度,随着游戏的进行而变化。它不仅是衡量游戏进程的重要指标,还决定了游戏的难度和挑战程度。当蛇的长度增加时,游戏的难度也会相应增加,因为蛇更容易撞到自己或撞墙。颜色定义(@State colors:ResourceColor []):
colors
数组为游戏中的不同元素提供了特定的颜色,增强了游戏的视觉效果。通过不同的颜色区分空白区域、蛇的身体部分和能量,玩家可以更轻松地识别游戏中的不同元素,提高了游戏的可玩性和趣味性。结束提示(@State end:string):
end
变量用于存储游戏结束的提示信息,为玩家提供了明确的游戏结果反馈。当游戏结束时,这个变量会被设置为相应的提示信息,让玩家知道游戏已经结束以及结束的原因。这有助于玩家更好地理解游戏规则和自己的游戏表现。初始化函数:initArea:
area
,它确保了每次游戏开始时,游戏区域都是干净的,没有任何残留的蛇的身体部分或能量。这为玩家提供了一个公平的游戏环境,让他们可以从相同的起点开始游戏。游戏逻辑更新函数:aboutToAppear:
initArea
函数进行初始化,为游戏的开始做好准备。然后随机生成能量的位置,为蛇提供了一个目标,增加了游戏的趣味性和挑战性。setInterval
实现了游戏的定时更新,确保了游戏的流畅性和动态性。每隔 200 毫秒执行一次更新操作,使得游戏的变化不会过于迅速,让玩家有足够的时间做出反应。build
方法中,整体构建了贪吃蛇游戏的界面和交互逻辑。
Column
布局作为游戏的主要容器,为游戏的各个元素提供了一个统一的布局框架。Stack
布局将游戏区域和结束提示整合在一起,为玩家提供了一个直观的游戏界面。
Column
和ForEach
循环遍历area
数组,对每个位置进行渲染。这种方式使得游戏区域的显示非常灵活,可以根据游戏的状态动态地更新每个位置的显示内容。initArea
函数重新初始化游戏,使得玩家可以方便地重新开始游戏,提高了游戏的可玩性。Stack
布局包含上、下、左、右四个方向的控制按钮,为玩家提供了操作游戏的方式。每个按钮设置了不同的属性,使得它们在游戏界面中易于识别和操作。tryDirections
的值,以尝试改变蛇的移动方向。这种交互方式使得玩家可以通过简单的点击操作来控制蛇的移动,增加了游戏的互动性和趣味性。@Entry
@Component
struct Page2 {
// 二维数组 表示运动区域
@State area:number[][] = []
// 当前运动方向
@State directions: 'up'| 'down'| 'left'| 'right' = 'right'
// 控制方向
@State tryDirections: 'up'| 'down'| 'left'| 'right' = 'right'
// 历史位置 用于蛇移动时更新蛇头和蛇尾
@State historyPosition:number[][]=[]
// 头部坐标
@State headX:number = 0
@State headY:number = 0
// 蛇的长度
@State length:number = 0
// 颜色 不同的表示渲染不同等等颜色
@State colors:ResourceColor[]=[Color.Transparent, Color.Black, Color.Orange]
//结束提示
@State end:string=''
// 初始化
initArea(){
// 25 * 25 初始化地图
this.area = []
for(let i=0;i<25;i++){
this.area.push([])
for(let j=0;j<25;j++){
this.area[i].push(0)
}
}
// 对蛇初始化
this.area[4][6]=1
this.area[4][7]=1
this.area[4][8]=1
this.area[4][9]=1
this.area[4][10]=1
// 初始化历史位置
this.historyPosition=[]
this.historyPosition.push([6,4])
this.historyPosition.push([7,4])
this.historyPosition.push([8,4])
this.historyPosition.push([9,4])
this.historyPosition.push([10,4])
// 初始化头部位置
this.headX = 10
this.headY = 4
// 初始化蛇的长度
this.length = 5
// 初始化 结束提示
this.end=''
// 初始化方向以及想尝试的方向 为右
this.directions = 'right'
this.tryDirections = 'right'
}
aboutToAppear(): void {
// 初始化
this.initArea()
// 随机生成能量的位置
let foodX:number = Math.floor(Math.random()*this.area[0].length)
let foodY:number = Math.floor(Math.random()*this.area[0].length)
// 控制蛇的移动(更新坐标)
setInterval(()=>{
// 游戏结束后不做操作
if(!this.end){
// 上一个还没吃到 或 蛇的长度占满全屏 不用生成
if(this.area[foodY][foodX]!=2&&this.length<25*25){
while(true){
// 生成的随机坐标为0时即为空位置时才生成能量退出循环
if(this.area[foodY][foodX]==0){
this.area[foodY][foodX]=2
break
}
// 继续生成
foodX = Math.floor(Math.random()*this.area[0].length)
foodY = Math.floor(Math.random()*this.area[0].length)
}
}
// 移动后头部的新位置
let newHeadY:number
let newHeadX:number
// 当控制方向和当前移动的方向满足条件时 才改变当前位置 避免蛇头往身后走
if(this.tryDirections == 'right' && this.directions != 'left') this.directions = 'right'
else if(this.tryDirections == 'left' && this.directions != 'right') this.directions = 'left'
else if(this.tryDirections == 'up' && this.directions != 'down') this.directions = 'up'
else if(this.tryDirections == 'down' && this.directions != 'up') this.directions = 'down'
// 不同的方向更新不同的头部
if(this.directions == 'right'){
newHeadY = this.headY
newHeadX = this.headX+1
}else if(this.directions == 'up'){
newHeadY = this.headY-1
newHeadX = this.headX
}else if(this.directions == 'down'){
newHeadY = this.headY+1
newHeadX = this.headX
}else{
newHeadY = this.headY
newHeadX = this.headX-1
}
// 判断是否撞墙
if(newHeadY>=0 && newHeadY=0 && newHeadX{
Row(){
// 遍历每行的每一个位置
ForEach(itemR,(itemC:number, indexC:number)=>{
// 对每一个位置进行渲染 0:空位置 1:蛇的部位 2:能量 以及是否为蛇头
Text(indexR == this.headY && indexC == this.headX?'·' : '')
.textAlign(TextAlign.Center)
.fontSize(12)
.fontWeight(900)
.fontColor(Color.White)
.width(12)
.aspectRatio(1)
.backgroundColor(this.historyPosition[this.historyPosition.length-1][0] == indexC &&
this.historyPosition[this.historyPosition.length-1][1] == indexR?
Color.Pink : this.colors[this.area[indexR][indexC]])
})
}
})
}
.borderWidth(2)
.borderColor(Color.Orange)
// 结束时展示提示
Column({space:10}){
Text(this.end)
.fontSize(20)
.fontColor(Color.Red)
Text(this.end?'重新开始':'')
.fontSize(22)
.fontColor(Color.Red)
.onClick(()=>{
this.initArea()
})
}
.zIndex(this.end?1:-1) //结束时置z轴为1 展示出来
}
// 控制键
Stack(){
// 上、下、左、右 逻辑一样 以"上"操作为例
Button('上')
.fontSize(20)
.height(50)
.width(80)
.position({top:0, left:'50%'})
.translate({x:'-50%'})
.onClick(()=>{
this.tryDirections = 'up'
})
Button('下')
.fontSize(20)
.height(50)
.width(80)
.position({bottom:0, left:'50%'})
.translate({x:'-50%'})
.onClick(()=>{
this.tryDirections = 'down'
})
Button('左')
.fontSize(20)
.height(50)
.width(70)
.position({left:0, top:'50%'})
.translate({y:'-50%'})
.offset({left:0})
.onClick(()=>{
this.tryDirections = 'left'
})
Button('右')
.fontSize(20)
.height(50)
.width(70)
.position({right:0, top:'50%'})
.translate({y:'-50%'})
.onClick(()=>{
this.tryDirections = 'right'
})
}
.enabled(this.end?false:true) // 游戏结束时为禁用态 按钮失效
.width('45%')
.aspectRatio(1)
}
.height('100%')
.width('100%')
}
}