添加NPC小鱼
创建NpcInfo.ets文件,使用@Observed装饰器装饰要定义的NPC类,定义了NPC所需属性,NPC的速度,坐标,宽度,高度,游出方向,透明度。
坐标包括横坐标,纵坐标;宽度和高度相同;游出方向定义了NPC从屏幕左侧还是右侧游出;透明度定义了NPC的状态,当NPC被吃掉之后,透明度变为0。
@Observed
export default class NpcInfo {
//NPC
public npcSpeed: number = 3
public npcFishX: number = 200
public npcFishY: number = 200
public npcWidth:number=0
public npcHeight:number=0
public direction:number=0;//1代表从左边游出,0代表从右边游出
public npcLevel:number=0
public npcOpacity:number=1
constructor(npcSpeed: number
, npcFishY: number
,npcLevel:number
, npcWidth:number
, direction:number,
npcOpacity:number) {
this.npcLevel = npcLevel
this.npcSpeed = npcSpeed
this.npcFishY = npcFishY
this.npcWidth=npcWidth
this.npcHeight=npcWidth
this.direction=direction
this.npcOpacity=npcOpacity
if(this.direction==0){
this.npcFishX=10;
}else{
this.npcFishX=2200;
}
}
}
创建多个NPC
在index页面中定义并初始化了NpcInfo类型数组Npcfishes
@State Npcfishes:NpcInfo[]=[
new NpcInfo(5,200,3,240,0,1),
new NpcInfo(8,300,4,80,0,1),
new NpcInfo(10,400,5,60,0,1),
new NpcInfo(2,500,6,100,1,1),
new NpcInfo(9,800,7,100,1,1),
new NpcInfo(5,600,4,100,0,1),
]
NPC视图
利用数组和随机数字使得NPC的图片样式不同
import NpcInfo from '../viewmodel/NpcInfo';
@Component
export default struct npcView {
@ObjectLink item: NpcInfo;
private imageResources1: Resource[] = [
$r("app.media.f1"),
$r("app.media.f2"),
$r("app.media.f3"),
$r("app.media.f5"),
$r("app.media.f7"),
$r("app.media.f8"),
$r("app.media.f9"),
$r("app.media.f10"),
$r("app.media.f11"),
$r("app.media.f12"),
];
private imageResources2: Resource[] =[
$r("app.media.f1_rev"),
$r("app.media.f2_rev"),
$r("app.media.f3_rev"),
$r("app.media.f5_rev"),
$r("app.media.f7_rev"),
$r("app.media.f8_rev"),
$r("app.media.f9_rev"),
$r("app.media.f10_rev"),
$r("app.media.f11_rev"),
$r("app.media.f12_rev"),
]
@State currentImageIndex1:number=Math.floor(Math.random() * this.imageResources1.length/2);
@State currentImageIndex2:number=Math.floor(Math.random() * this.imageResources2.length/2);
build() {
Column() {
if(this.item.direction==0){
Image(this.imageResources1[this.currentImageIndex1])
.objectFit(ImageFit.ScaleDown)
.width(this.item.npcWidth)
.height(this.item.npcHeight)
.opacity(this.item.npcOpacity)
}else{
// Image($r(`${this.imageResources[Math.floor(Math.random() * this.imageResources.length)]}_rev`))
Image(this.imageResources2[this.currentImageIndex2])
.objectFit(ImageFit.ScaleDown)
.width(this.item.npcWidth)
.height(this.item.npcHeight)
.opacity(this.item.npcOpacity)
}
}.position({
x: this.item.npcFishX,
y: this.item.npcFishY
})
}
}
NPC的移动
关于NPC的移动是这样设计的。首先NPC的direction决定了NPC从屏幕哪一侧游出,然后在游戏开始按钮的onClick
事件中,通过setInterval
函数设置了一个定时器intervalIdNPC_1
。这个定时器会每隔一定时间(这里是40毫秒)执行一次回调函数。在定时器的回调函数中,遍历Npcfishes
数组,根据每个NPC的direction
属性(0表示向右移动,1表示向左移动)来更新其npcFishX
坐标。如果方向为0,npcFishX
增加npcSpeed
,否则减少npcSpeed
。形成NPC在向前移动的现象
if(!this.isBegin){
// 开始按钮
Button('GAME START')
.onClick(() => {
animateTo(
{duration: 1000},
() => {
//this.onGameStartButtonClicked()
//点击后显示小鱼
this.isBegin = true
//为了避免在游戏开始之前或之间有多个定时任务同时运行
clearInterval(this.intervalIdNPC_1)
this.intervalIdNPC_1 = setInterval(() => {
for(let i=0;i<this.Npcfishes.length;i++){
if(this.Npcfishes[i].direction==0){
this.Npcfishes[i].npcFishX+=this.Npcfishes[i].npcSpeed
}else{
this.Npcfishes[i].npcFishX-=this.Npcfishes[i].npcSpeed
}
}
},40)
}
)
})
.fontSize(85)
.width(600)
.height(200)
}
定义了关于主角鱼和NPC游到边界的处理方法
1.主角鱼
由于主角鱼会在屏幕内各个方向移动,因此要判断它的x和y坐标有没有超出设定范围
getBorderX(x: number) {
if (x < 0) {
x = 5
}
if (x > this.screenWidth-this.fishSize) {
x = this.screenWidth-this.fishSize ;
}
return x
}
getBorderY(y: number) {
if (y < 0) {
y =5
}
if (y > this.screenHeight-this.fishSize ) {
y = this.screenHeight-this.fishSize;
}
return y
}
效果演示:
2.NPC
由于NPC游动只会改变它的x坐标,因此只用处理它的x边界问题
getNPCBorderX(i:number,x: number) {
if(this.Npcfishes[i].direction==0){
if (x > this.screenWidth+this.Npcfishes[i].npcWidth) {
x = -this.NpcEscapeTime;
}
}else{
if(x<0-this.Npcfishes[i].npcWidth){
x=this.screenWidth+this.NpcEscapeTime
}
}
return x
}
根据它的游出方向来判断边界条件。如果是从左侧游出,就判断NPC的x坐标有没有超出屏幕宽度,即有没有超出右边界,如果超出就让x变为负的NPC逃离屏幕时间;如果是从右侧游出,就判断NPC的x坐标有没有超出屏幕宽度,即有没有超出左边界,如果超出就让x变为屏幕宽度加上逃离屏幕时间。
定义的逃离屏幕时间是为了让没有被吃掉并且游出屏幕范围的NPC再次回到屏幕范围内,实现NPC游出屏幕但还会再回来的效果。不过这个变量还可以优化为定义出具体的逃离长度除以NPC的速度,这样逻辑性似乎更强。
将上面定义的方法放在计时器里面使用
Button('GAME START')
.onClick(() => {
animateTo(
{duration: 1000},
() => {
//this.onGameStartButtonClicked()
//点击后显示小鱼
this.isBegin = true
clearInterval(this.intervalIdNPC_1)//为了避免在游戏开始之前或之间有多个定时任务同时运行
this.intervalIdNPC_1 = setInterval(() => {
this.fishX = this.getBorderX(this.fishX)
this.fishY = this.getBorderY(this.fishY)
for(let i=0;i<this.Npcfishes.length;i++){
if(this.Npcfishes[i].direction==0){
this.Npcfishes[i].npcFishX+=this.Npcfishes[i].npcSpeed
}else{
this.Npcfishes[i].npcFishX-=this.Npcfishes[i].npcSpeed
}
this.Npcfishes[i].npcFishX = this.getNPCBorderX(i,this.Npcfishes[i].npcFishX)
}
},40)
}
)
})
.fontSize(85)
.width(600)
.height(200)
效果演示: