鴻蒙開發遊戲(四)---大魚吃小魚(互吃升級)

TMusketeer發表於2024-03-04

鴻蒙開發遊戲(四)---大魚吃小魚(互吃升級)

鴻蒙開發遊戲(一)---大魚吃小魚(介面部署

鴻蒙開發遊戲(二)---大魚吃小魚(搖桿控制)

鴻蒙開發遊戲(三)---大魚吃小魚(放置NPC)

鴻蒙開發遊戲(四)---大魚吃小魚(互吃升級)

鴻蒙開發遊戲(五)---大魚吃小魚(新增音效)

鴻蒙開發遊戲(六)---大魚吃小魚(稱霸海洋)

前言:

該篇對NPC進行了升級,這裡可以投入多個NPC,且互不影響,npc之間不會觸發eat,只和玩家觸發eat,且每個NPC有自己的屬性,他們的等級在他們的頭頂

1、放置多個NPC

我們放置多個NPC就要把NPC屬性抽離出來,這裡很複雜,又牽扯到了知識點《狀態管理》,在鴻蒙中分為了多種狀態管理

鴻蒙開發遊戲(四)---大魚吃小魚(互吃升級)

這裡感興趣的話,可以去看看官方文件,我是覺得咋一看挺複雜的,用起來確實不簡單。這裡我就簡單說一下我用到的,就是鴻蒙開發都是按照元件來組建的,一個titleview,一個小魚等都可以封裝成一個元件

@Component

用這個修飾。在主元件中定義的變數我們如果傳遞到子元件,可以讓他只讀也可以讓他修改,這就用到了狀態管理,子元件中如果改變了變數,在主元件中做出了相應改變就說明是父子雙向同步。

下面開始寫npc屬性

@Observed
export default class NpcInfo {
  //NPC
  public npcSpeed: number = 3
  public npcFishX: number = 200
  public npcFishY: number = 200
  public npcAngle: number = 0
  public npcSin: number = 1
  public npcCos: number = 1
  public npcLevel: number = 2

  constructor(npcSpeed: number
              , npcFishX: number
              , npcFishY: number
              , npcAngle: number
              , npcLevel: number
              , npcCos: number
              , npcSin: number) {
    this.npcSpeed = npcSpeed
    this.npcFishX = npcFishX
    this.npcFishY = npcFishY
    this.npcAngle = npcAngle
    this.npcLevel = npcLevel
    this.npcCos = npcCos
    this.npcSin = npcSin
  }
}

單獨使用@Observed是沒有任何作用的,需要搭配@ObjectLink或者@Prop使用。

@Component
struct npcView {
  @ObjectLink item: NpcInfo;

  build() {
    Column() {
      Text(this.item.npcLevel + "")
        .fontColor('#f11')
        .fontSize(12)
      Image($r("app.media.icon_npc_2"))
        .objectFit(ImageFit.ScaleDown)
        .width(40)
        .height(40)
    }.position({
      x: this.item.npcFishX - 40,
      y: this.item.npcFishY - 40
    })
    .rotate({ angle: this.item.npcAngle, centerX: '50%', centerY: '50%' })

  }
}

@ObjectLink 同樣也不能在@Entry修飾的元件中使用。這裡面有幾個需要注意的點就是npcInfo需要用@ObjectLink修飾,而且也需要單獨寫一個npcView元件,這樣npcList資料發生改變,元件才會監控到,和咱們平常安卓是不是不一樣,我剛開始的寫法是直接在主元件中寫,如下

ForEach(
  this.npcList,
  (item: NpcInfo, index) => {
      //錯誤寫法
     Column() {
      Text(item.npcLevel + "")
        .fontColor('#f11')
        .fontSize(12)
      Image($r("app.media.icon_npc_2"))
        .objectFit(ImageFit.ScaleDown)
        .width(40)
        .height(40)
    }.position({
      x: item.npcFishX - 40,
      y: item.npcFishY - 40
    })
    .rotate({ angle: item.npcAngle, centerX: '50%', centerY: '50%' })
  }
)

這樣寫也可以建立出來npc,但是小魚不會動,我看日誌npc(x,y)座標是改變的,但就是view不動,後來查資料發現需要雙向資料同步,不然就是單向資料問題。正確寫法是

@Component
struct npcView {
  @ObjectLink item: NpcInfo;

  build() {
    Column() {
      Text(this.item.npcLevel + "")
        .fontColor('#f11')
        .fontSize(12)
      Image($r("app.media.icon_npc_2"))
        .objectFit(ImageFit.ScaleDown)
        .width(40)
        .height(40)
    }.position({
      x: this.item.npcFishX - 40,
      y: this.item.npcFishY - 40
    })
    .rotate({ angle: this.item.npcAngle, centerX: '50%', centerY: '50%' })

  }
}

主元件中

ForEach(
  this.npcList,
  (item1: NpcInfo, index) => {
    npcView({ item: item1 })
  }
)

這樣寫就可以了,那多條小魚在處理座標問題時應該如何操作呢,我這裡是用陣列的形式,用for迴圈動態設定所有npc的小魚座標。如果有其他的方式請給我留言。

if (this.isBegin == false) {
  Button('開始遊戲')
    .backgroundColor('#36d')
    .onClick(() => {
      this.isBegin = true
      clearInterval(this.intervalIdNPC_1)
      this.intervalIdNPC_1 = setInterval(() => {

        for (let i = 0; i < this.npcList.length; i++) {

          //6、設定小魚的移動位置,
          this.npcList[i].npcFishX += this.npcList[i].npcSpeed * this.npcList[i].npcCos
          this.npcList[i].npcFishY += this.npcList[i].npcSpeed * this.npcList[i].npcSin


          this.npcList[i].npcFishX = this.getNPCBorderX(i, this.npcList[i].npcFishX)
          this.npcList[i].npcFishY = this.getNPCBorderY(i, this.npcList[i].npcFishY)

          console.log("小魚走了嗎" + i + "   " + this.npcList[i].npcFishX)
        }


      }, 40)

    })
}

這裡需要注意的是,多個npc之間是沒有關聯的,只有當npc碰到螢幕邊緣或者某個點的時候掉頭,其他npc不受影響。這裡對getNPCBorderX,Y()方法做了修改,。

getNPCBorderX(i: number, x: number) {
  if (x <= this.fishRadius) {
    x = this.fishRadius + 10
    this.getRandom(i)
  }
  if (x > this.screenWidth - this.fishRadius) {
    x = this.screenWidth - this.fishRadius - 15
    this.getRandom(i)
  }
  return x
}

getNPCBorderY(i: number, y: number) {
  if (y <= this.fishRadius) {
    y = this.fishRadius + 10
    this.getRandom(i)
  }
  if (y > this.screenHeight - this.fishRadius) {
    y = this.screenHeight - this.fishRadius - 10
    this.getRandom(i)
  }
  return y
}

  /*隨機獲取一個角度*/
  getRandom(i: number) {
    this.npcList[i].npcAngle = this.selectFrom(0, 359)
    // let angle = Math.random()+Math.random()+Math.random()
    // this.npcAngle = angle * 180 / Math.PI
    //這是是求弧度,弧度 = 角度 * π / 180


    this.npcList[i].npcSin = Math.sin(this.npcList[i].npcAngle * Math.PI / 180)
    this.npcList[i].npcCos = Math.cos(this.npcList[i].npcAngle * Math.PI / 180)

  }

好了,到這多個NPC就已經放置完成了,並且已經動起來了。哦,對了還需要初始化

onPageShow() {
    //                         速度,x,y   角度/等級/cos/sin
  this.npcList.push(new NpcInfo(3, 300, 200, 0, 2, 1, 1))
  this.npcList.push(new NpcInfo(3, 300, 100, 0, 3, 1, 1))
  this.npcList.push(new NpcInfo(3, 200, 200, 0, 2, 1, 1))
  this.npcList.push(new NpcInfo(1, 200, 200, 0, 1, 1, 1))
}

2、互吃邏輯

竟然要pk,就要有血量或者等級,這裡暫時寫等級

//等級
@State level: number = 3

只要玩家碰到NPC了就要判斷level是否相等,如果大就要吃掉NPC,npcList就要減去一個,如果小於NPC等級就是被吃,遊戲結束。

//互吃邏輯
eatFish(){
  for (let i = 0; i < this.npcList.length; i++) {

    let vx =  this.xFish - this.npcList[i].npcFishX;
    let vy =  this.yFish - this.npcList[i].npcFishY;

    let distance = Math.sqrt(vx * vx + vy * vy)
    if(distance >0 && distance<20){
      if(this.level >= this.npcList[i].npcLevel){
          this.level+=1;
          this.npcList.splice(i,1)
      }else{
        //遊戲結束
      }
    }
  }

}

使用地方

handleTouchEvent(event: TouchEvent) {
  switch (event.type) {
    case TouchType.Down:
      this.intervalId = setInterval(() => {
        //6、設定小魚的移動位置,
        this.xFish += this.speed * this.cos
        this.yFish += this.speed * this.sin

        //目的是觸碰到邊緣時不溢位
        this.xFish = this.getBorderX(this.xFish)
        //還原角度
        // this.angle = 0

         //互吃邏輯
        this.eatFish()
      }, 40)
      break
    case TouchType.Move:
      this.setMovePosition(event)
      break
    case TouchType.Up:
      clearInterval(this.intervalId)
    //恢復搖桿位置
      animateTo({
        curve: curves.springMotion()
      }, () => {
        this.positionX = this.centerX
        this.positionY = this.centerY
      })

      this.speed = 0
      break
  }


}

完畢

相關文章