【HarmonyOS开发】吃鱼吧——简易版大鱼吃小鱼(三)

添加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)

效果演示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值