首页 > 其他分享 >鸿蒙开发游戏(四)---大鱼吃小鱼(互吃升级)

鸿蒙开发游戏(四)---大鱼吃小鱼(互吃升级)

时间:2024-03-04 21:33:36浏览次数:32  
标签:大鱼吃小鱼 number 40 --- item npcList NPC 互吃 npcFishX

鸿蒙开发游戏(一)---大鱼吃小鱼(界面部署

鸿蒙开发游戏(二)---大鱼吃小鱼(摇杆控制)

鸿蒙开发游戏(三)---大鱼吃小鱼(放置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
  }


}

完毕

 

标签:大鱼吃小鱼,number,40,---,item,npcList,NPC,互吃,npcFishX
From: https://www.cnblogs.com/cmusketeer/p/18050890

相关文章

  • ems-jsp 用户登录模块
    整体思路1.登录首先要从前端获取账号和密码2.根据用户名去查询用户,用户存在比对密码(注意由于密码是加密存入数据库的所以比对时要用用户输入的密码加密后跟数据库中的密码比对)。  用户不存在,登陆失败,返回登录界面并且显示失败原因。代码:展示部分Controller层:@Requ......
  • MySQL-18 MySQL8其他新特性
    C-18.MySQL8其他新特性1.MySQL8新特性概述MySQL从5.7版本直接跳跃发布了8.0版本,可见是一个令人兴奋的里程碑的版本。MySQL8版本在功能上,做了显著的改进与增强,开发者对MySQL的源代码进行了重构,最突出的一点是对MySQLOptimizer优化器进行了改进。不仅在速度上得到了改善,还为用......
  • C++U6-06 - 一维线性动态规划
    上节课作业:链接:https://pan.baidu.com/s/17Fei1SuGEk5pnSspf_hprg?pwd=hq04提取码:hq04 动态规划  [最长上升子序列]  本题采用动态规划。数据储存,设定数组a[]用于存储数字序列,设定dp[]数组用于统计上升的序列个数;遍历组数a[],在遍历的过程中如果出现了数......
  • p7915-solution
    P7915Solutionlink考虑枚举第一个操作选L还是R。这样原序列就被分为了两个栈,用四个指针\(p1,p2,p3,p4\)分别指向这两个栈的栈顶栈底。感性理解一下,某一个栈的栈顶\(x\)可以被pop当且仅当某一个栈的栈底等于\(x\)。于是直接dfs,每次优先选L,同时确定第\(2n-i+1\)......
  • p7914-solution
    P7914Solutionlink先考虑Subtask\(4\)。设\(dp_i\)表示长度为\(i\)的方案数,按题目定义转移:AB,ASB:\(\displaystyledp_n\getsdp_n+\sum_{i=1}^{n-1}\sum_{j=0}^kdp_i\timesdp_{n-i-j}\)(A):\(\displaystyledp_n\getsdp_n+dp_{n-2}\)(SA),(AS):\(\displa......
  • p7913-solution
    P7913Solutionlink先考虑有\(n\)个廊桥的分配。假设廊桥编号为\(1\simn\),我们用两个堆\(h1,h2\)分别存当前空闲的廊桥编号和正在使用廊桥的飞机的离开时间。对于国内和国外的飞机分别做一次以下操作:先按到达时间排序,从左到右扫,到第\(i\)架飞机的到达、离开时间为\(l_......
  • p5384-solution
    P5384Solutionlink弱化这题空间\(\mathcalO(n\logn)\)会MLE。考虑怎么搞到\(\mathcalO(n)\)。首先求k级祖先用树剖空间是\(\mathcalO(n)\)的。然后看看我们建线段树的过程,我们发现每次查询都是在对应深度的线段树里查,那么考虑把询问离线,把节点、询问对应到深度......
  • p4845-solution
    P4845Solutionlink考虑树形dp,对于每个\(u\)直接钦定它的三种可能状态:有灯,没灯但是被其他灯照亮,没灯也没被照亮。这样钦定会导致一些需要被照亮的点在当前子树中还未被照亮,需要依靠子树外的灯来照亮。称这种点为闲置点。状态内只需要记录闲置点中最深的到子树根的距离,以及......
  • p4632-solution
    P4632Solutionlink对时间扫描线,就变成支持单点加入删除一个颜色点,求所有颜色距离某个点的距离最大值。考虑二分答案,现在就是要检验\([x-mid,x+mid]\)内是否有\(1\simk\)颜色的点各至少一个。数颜色可以考虑维护\(pre_i\)表示上一个与该点同色的位置。然后区间\([l,r]......
  • p4555-solution
    P4555Solutionlink双回文串的左右两半部分显然是互相独立的。于是考虑求出\(lm_i\)表示以\(i\)结尾的最长回文子串长度,\(rm_i\)表示以\(i\)开头的最长回文子串长度,最后扫一遍所有的分隔符求\(lm+rm\)的最大值即可。考虑如何求\(lm,rm\)。先用manacher拉出\(p\),......