当前位置: 首页 > article >正文

unity实现回旋镖函数

最近学习unity2D,想实现一个回旋镖武器,发出后就可以在角色周围回旋。

一、目标

1.不是一次性的,扔出去、返回、没有了;而是扔出去,返回到角色后方相同距离,再次返回;再次返回,永远不停止。

2.销毁的时机,是这个武器与角色再次重合的时候,相当于回收回旋镖;如果不回收(例如跳跃躲开),那就一直转。

3.角色的位置会移动,回旋镖得多次判断下一个目标点的位置。

4.回旋镖的速度,需要从最大速度开始,到接近目标点的时候逐渐减速;返回时也要从最大速度开始,到接近目标点的时候减速。

总的来说,就是类似《忍者龙剑传3》里,那个十字回旋镖武器的效果,如果跳跃躲开,就会一直在身边转。

二、问题

实际写代码实现的时候,才发现有一堆坑。
1.回旋镖的坐标,不能完全到达目标点,因为unity是按帧算的,每帧移动多少速度的话,可能会超过目标点,或者没有到达目标点,有误差。

2.如果只以接近目标点判断的话,会丢失距离,每次回旋的距离逐渐变近,最后停留原地不动了。

3.如果角色坐标移动,回旋镖计算目标点并重新移动时,经常速度变为0,卡在原地不动(不确定哪里出bug了)。

三、代码部分

//这部分代码,在武器代码cs部分
    float weaponRange = 3;
    float weaponSpeed = 15;

    //当前已飞行的距离
    float distanceTravelled = 0f;
    
    //当前武器位置到目标点的总距离
    float distanceToTarget;


    //目标点在哪边
    bool targetIsRight = false;
    [SerializeField] Transform weaponPos;
    Func<Vector2> weaponPosFunc;
     
    Vector2 targetPos;
    
    Vector2 nowSpeed;

    Animator animator;

    Rigidbody2D rb2d;
    SpriteRenderer sprite;

    CircleCollider2D cc2d;



    void Awake()
    {
        animator = GetComponent<Animator>();
        cc2d = GetComponent<CircleCollider2D>();
        rb2d = GetComponent<Rigidbody2D>();
        sprite = GetComponent<SpriteRenderer>();

    }

  //发射武器方法,其他类调用
  public void Shoot(bool isFaceingRight) {

      Vector2 newTarget;

      //其他类传来的,一开始武器起点坐标
      Vector2 v = weaponPosFunc();

      //bool类型,武器图片,如果角色面朝右边就是true,就翻转图片,否则不翻转
      sprite.flipX = isFaceingRight;

      //bool类型,武器的目标点方向,后面会用
      targetIsRight = isFaceingRight;

      //赋值,刚开始武器到目标点的距离
      distanceToTarget = Vector2.Distance(weaponPos.position, targetPos);

      //首次武器目标点,直线,看是角色面前还是背后
      //targetPos就是武器目标点
      if (!isFaceingRight) {    
          newTarget = new Vector2(v.x - weaponRange, v.y);
          targetPos = newTarget;
      }
      else
      {
          newTarget = new Vector2(v.x + weaponRange, v.y);
          targetPos = newTarget;
      }
  }

    //更新方法,系统会每帧调用
    void Update()
    {
        // 向目标位置飞行,并逐步减速,到达目标点要掉头,反复重复
        MoveTowardsTarget();
    }



    void MoveTowardsTarget()
    {
    
        //已经走过的距离/物体到目标的距离
        float dPercent = distanceTravelled / distanceToTarget;
        if(dPercent < 0){
            dPercent = -dPercent;
        }
        if(dPercent  > 1){
           dPercent = 1;
        }

        // 计算当前的速度因子,使物体从快到慢;weaponSpeed是初始武器速度
        //Lerp方法,就是dPercent为0时,返回第一个参数;为1时返回第二个参数;0-1之间时就返回第一个参数到第二个参数之间的值
        float speedFactor = Mathf.Lerp(weaponSpeed, 0f, dPercent);
        //确定一个最慢速度,否则速度因子接近0,越来越慢,很难到目标
        if(speedFactor < 2f)
        {
            speedFactor = 2f;
        }
        
        // 计算朝目标位置的方向,方向向量
        Vector2 direction = (targetPos - (Vector2)weaponPos.position).normalized;

        //如果方向向量变为0,就没办法移动了,此时要指定一个默认方向向量
        if(direction.x == 0 && direction.y == 0)
        {
            //如果目标点在右边,方向向量就是右边;否则左边
            if (targetIsRight)
            {
                direction = new Vector2(1,0);
            }
            else
            {
                direction = new Vector2(-1, 0);
            }
        }
        
        //设置物体的速度,方向向量*速度因子
        //设置了这个,武器就会开始移动
        rb2d.velocity = direction*speedFactor;
        //保存当前速度,后续可能用
        nowSpeed = direction * speedFactor;

        // 更新已飞行的距离,刚开始是0;每一帧移动速度*每一帧时间=路程,然后累加
        distanceTravelled += rb2d.velocity.magnitude * Time.deltaTime;

        //打印日志,武器到目标点的距离
        //Debug.Log("distanceToTarget"+ distanceToTarget);

        // 如果 (超过目标点 或者 接近目标点)  并且 至少走够了武器路程
        //需要分左右,才能知道是没到目标点还是超过目标点,需要超过目标点;是否超过目标点就先用x判断了;y又复杂了,还得分上下
        //防止没到目标点就卡住,又加了接近目标点
        if (targetIsRight)
        {
            if (  ((targetPos.x <= weaponPos.position.x) || distanceToTarget<=0.1) && distanceTravelled >= weaponRange)
            {
                //重置目标点
                FreshTargetPos();
            }
        }
        else
        {
            if (  ((targetPos.x >= weaponPos.position.x) || distanceToTarget<=0.1) && distanceTravelled >= weaponRange)
            {
                //重置目标点
                FreshTargetPos();
            }
        }

    }


    void FreshTargetPos() {

        //玩家当前坐标,实时获取(玩家的武器发射点的坐标)
        Vector2 p = weaponPosFunc();
        //武器当前坐标
        Vector2 w = weaponPos.position;
        //w点关于p点的对称点
        //targetPos = new Vector2( 2*p.x -w.x , 2*p.y -w.y );

        // 计算朝目标位置的方向,方向向量,扩大武器范围倍数
        Vector2 direction = (p - (Vector2)w).normalized * weaponRange;
        //算出下一个目标点
        targetPos = new Vector2(direction.x+p.x, direction.y+p.y);

        //看目标点到底在哪里,然后判断有没有超过目标点
        if (targetPos.x > weaponPos.position.x)
        {
            targetIsRight = true;
        }
        else
        {
            targetIsRight = false;
        }

        //已走过的路程归零
        distanceTravelled = 0;

        // 重新计算物体到目标的距离,weaponPos是当前武器坐标,targetPos是目标点坐标;得到的是距离
        distanceToTarget = Vector2.Distance(weaponPos.position, targetPos);

    }

四、备注

unity代码比较多,就贴重要部分了,关于坐标的计算;
目前代码就是这个版本了,大概实现了需求;
武器当前速度处理还可能有些问题,但是可以在玩家周围回旋了;
如果玩家静止或者直线移动,武器就在x轴上回旋;
如果玩家跳跃,武器就会斜方向回旋到玩家背后;
感觉情况有些复杂,可能哪里还有bug,欢迎留言指正。

五、效果图片

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如图,可以在角色周围水平回旋,斜方向也可以。


http://www.kler.cn/a/524902.html

相关文章:

  • [权限提升] 常见提权的环境介绍
  • 【张雪峰高考志愿填报】合集
  • MYSQL 商城系统设计 商品数据表的设计 商品 商品类别 商品选项卡 多表查询
  • C语言练习(29)
  • 未来无线技术的发展方向
  • XSS 漏洞全面解析:原理、危害与防范
  • 【面试】【详解】计算机网络(TCP 三次握手,四次挥手)
  • Windows 程序设计7:文件的创建、打开与关闭
  • Vue.js组件开发-实现导出PDF文件可自定义添加水印及水印样式方向
  • 10.1 DriveInfo类获取磁盘分区信息
  • 011-mac自带vnc
  • leetcode——二叉树的最大深度(java)
  • Selenium 浏览器操作与使用技巧——详细解析(Java版)
  • 8. 网络编程
  • 鸢尾花书01---基本介绍和Jupyterlab的上手
  • 杨立昆退休?中国Deepseek超Llama 4触发Meta
  • 单片机基础模块学习——超声波传感器
  • Python的那些事第五篇:数据结构的艺术与应用
  • 【redis】redis操作zset类型的key发生了什么?
  • 企业知识管理平台助力企业创新与竞争力提升的有效策略探讨
  • 网关登录校验
  • Qwen2.5-max 性能
  • JAVA实战开源项目:网上超市系统(Vue+SpringBoot) 附源码
  • 蓝桥备赛指南(5)
  • TCP 握手数据包分析
  • 「AI学习笔记」深度学习的起源与发展:从神经网络到大数据(二)