• 山寨小小军团开发笔记 之 Arrow Projectile


        好久没怎么更新博客了,今天抽空来一篇,讨论一下弓箭的轨迹生成。

    一、原理

          弓箭的轨迹本质就是一个数学问题,使用一个 bezier 曲线公式就可以插值生成。得到轨迹后,做一个lookAt就可以了。

    二、Bezier 曲线原理

         2015-5-15 :相关原理介绍,我就不重复了

      http://zh.wikipedia.org/wiki/%E8%B2%9D%E8%8C%B2%E6%9B%B2%E7%B7%9A

      http://devres.zoomquiet.io/data/20110728232822/index.html

         我这里贴一下应用代码

         

     public class Bezier
        {
            public Vector3 p0 = Vector3.zero;
            public Vector3 p1 = Vector3.zero;
            public Vector3 p2 = Vector3.zero;
    
    
            public Bezier(Vector3 v0, Vector3 v1, Vector3 v2)
            {
                this.p0 = v0;
                this.p1 = v1;
                this.p2 = v2;
            }
    
            public void UpdateTargetPos(Vector3 v2)
            {
                p2 = v2;
            }
    
            public Vector3 GetPointAtTime(float t)
            {
                float x = (1 - t) * (1 - t) * p0.x + 2 * t * (1 - t) * p1.x + t * t * p2.x;
                float y = (1 - t) * (1 - t) * p0.y + 2 * t * (1 - t) * p1.y + t * t * p2.y;
                float z = (1 - t) * (1 - t) * p0.z + 2 * t * (1 - t) * p1.z + t * t * p2.z;
                return new Vector3(x, y, z);
            }
        }
    

    三、例子分析

         搭建一个测试场景,从中心点发射弓箭诡异

      

          Center上挂载一个脚本  

          BezierTest.cs

        public GameObject arrowPrefab;
    
        public Transform left;
    
        public Transform right;
    
        void Test(bool fireRight)
        {
            Transform end = fireRight ? right : left;
    
            ///在中心点生成弓箭
            GameObject curArrow = arrowPool.Spawn(transform.position, Quaternion.Euler(Vector3.zero));
    
            ///计算LookTarget的点 与 贝塞尔曲线的第三个控制点
            Vector3[] points = Re_LookTarget_MiddlePerpendicularPoint(curArrow.transform, end);
    
            ///初始化发射
            ArrowControl arrowControl = curArrow.GetComponent<ArrowControl>();
            arrowControl.Init(
                points[0],
                points[1],
                end.position,
                3.0f,
                delegate()
                {
                    arrowPool.Unspawn(curArrow);
                });
        }
    
        Vector3[] Re_LookTarget_MiddlePerpendicularPoint(Transform self, Transform enemy)
        {
            Vector3 direction = (enemy.position - self.position).normalized;
    
            float segment = Vector2.Distance(enemy.position, self.position) / 2.0f;
            
            Vector3 lookTarget = (self.position + enemy.position) / 2.0f;
    
            Vector3 perpendicular_direction;
            if (direction.x > 0)
            {
                perpendicular_direction = new Vector3(-direction.y, direction.x, 0);
            }
            else
            {
                perpendicular_direction = new Vector3(direction.y, -direction.x, 0);
            }
    
            ///perpendicular line
            Vector3 middle_pendicular = lookTarget + perpendicular_direction * segment;
    
            return new Vector3[] { lookTarget, middle_pendicular };
        }
    
    
    
        void OnGUI()
        {
            if (GUI.Button(new Rect(10, 10, 150, 100),  "Fire Left"))  Test(false);
            if (GUI.Button(new Rect(160, 10, 150, 100), "Fire Right")) Test(true);
        }
    

     

      预设上挂载的控制脚本

      ArrowControl.cs 

        float alltimer;
        
        Util.Bezier bezier = null;
    
        float timer = 0f;
    
        Callback recycleFunction;
    
        Vector3 lookAtTarget;
    
        //bool defaultRightNeedTurn = false;;
    
        public void Init(Vector3 lookAtTarget, Vector3 middle, Vector3 end, float alltimer, Callback recycleFunction)
        {
            this.lookAtTarget = lookAtTarget;
    
            this.bezier = new Util.Bezier(transform.position, middle, end);
            
            this.alltimer = alltimer;
    
            Flip(end.x, transform.position.x);
    
            this.recycleFunction = recycleFunction;
        }
    
        void Flip(float end_X, float self_X)
        {
            Transform pic = transform.Find("pic");
    
            if (end_X < self_X)  {
                pic.localScale = new Vector3(-1.0f, 1.0f, 1.0f);
            }
            else {
                pic.localScale = new Vector3( 1.0f, 1.0f, 1.0f);
            }
        }
    
        public void Clear()
        {
            this.bezier = null;
    
            timer = 0f;
        }
    
        void Update()
        {
            if (this.bezier != null)
            {
                timer += Time.deltaTime;
    
                float t = timer / alltimer;
    
                //Debug.Log("timer" + timer + " t " + t);
    
                if (t >= 1.0f) {
    
                    Clear();
    
                    if (recycleFunction != null) recycleFunction();
    
                }
                else {
                    transform.LookAt(lookAtTarget, Vector3.forward);
                    
                    transform.position = bezier.GetPointAtTime(t);
                }
    
            }
        }
    

      

     这里注意,对于LookAt这个行为,我没有很好的数学方法,所以用了U3D的API。

     但是这个API 只能保证Z轴始终指向target, 所以图片的旋转预先是这样。

     

    四、结果

  • 相关阅读:
    Conntect Bluetooth devices in iOS.
    Why NSAttributedString import html must be on main thread?
    IOS7 SDK 几宗罪
    How to browse the entire documentation using XCode 5 Documentation and API Reference ?
    High Precision Timers in iOS / OS X
    [UWP]抄抄《CSS 故障艺术》的动画
    [Microsoft Teams]使用连接器接收Azure DevOps的通知
    [WPF 自定义控件]自定义一个“传统”的 Validation.ErrorTemplate
    [WPF 自定义控件]在MenuItem上使用RadioButton
    [WPF 自定义控件]创建包含CheckBox的ListBoxItem
  • 原文地址:https://www.cnblogs.com/chongxin/p/4340108.html
Copyright © 2020-2023  润新知