Parcourir la source

箭的飞行算法和规则

lvjincheng il y a 4 ans
Parent
commit
baff0955bd
1 fichiers modifiés avec 46 ajouts et 9 suppressions
  1. 46 9
      Assets/BowArrow/Scripts/Game/Arrow.cs

+ 46 - 9
Assets/BowArrow/Scripts/Game/Arrow.cs

@@ -57,49 +57,64 @@ public class Arrow : MonoBehaviour
         }
     }
 
+    /**在箭飞行前初始化&如果瞄准射线的落点在靶子上时,才调用该函数 */
     void SetUpBeforFly()
     {
+        //看能否通过调整发射角让箭落在靶子的瞄准点上
         CalculateParabolaAngle(absoluteRay.point); 
 
-        if (!hasParabolaAngle) {
+        if (!hasParabolaAngle) { //发射角无解,就提示用户
             if (AimLoadChecker.ins) AimLoadChecker.ins.ShowOutTip();
             return;
         }
+        //来到这里,证明发射角有解,该解姑且叫它绝对角
 
+        //因为Unity引擎的规则,向上时359~270度,向下是0~90度
+        //为了方便数学运算,换算成 向上时0~90度,向下是0~-90度
         float baseAngleX = this.transform.eulerAngles.x;
         if (baseAngleX > 180) baseAngleX = baseAngleX - 360;
         baseAngleX *= -1;
 
+        //绝对角
         float absoluteAngleX = parabolaAngleInRadian / Mathf.PI * 180;
 
+        //客户要求绝对角跟原本角度相差不能超过 maxDeltaAngleX
         float deltaAngleX = absoluteAngleX - baseAngleX;
         float maxDeltaAngleX = 5;
 
+        //最终的角还要加上偏移角度,偏移角也是客户要求加,单纯是为了增加误差,简单模式偏移角度为0
         float finalAngleX = this.offsetAngle;
+        //如果绝对角跟原本角度相差超过 maxDeltaAngleX,就不能用绝对角了,直接在原本角度上加上最大差角
         if (Mathf.Abs(deltaAngleX) > maxDeltaAngleX) {
             finalAngleX += baseAngleX;
             finalAngleX += deltaAngleX > 0 ? maxDeltaAngleX : -maxDeltaAngleX;
             if (AimLoadChecker.ins) AimLoadChecker.ins.ShowOutTip(); 
-        } else {
+        } else {//不超过就最好,直接用绝对角
             finalAngleX += absoluteAngleX;
         }
 
+        //拆分两轴的速度
         float vy = Mathf.Sin(finalAngleX / 180 * Mathf.PI) * this.mySpeed;
         float vx = Mathf.Cos(finalAngleX / 180 * Mathf.PI) * this.mySpeed;
+        //x轴距离
         float deltaX = Mathf.Sqrt(
             Mathf.Pow(absoluteRay.point.x - this.shootOutPosition.x, 2) + 
             Mathf.Pow(absoluteRay.point.z - this.shootOutPosition.z, 2)
         );
+        //x方向的耗时
         float tx = deltaX / vx;
+        //最终y轴的位移
         float dy = vy * tx + 0.5f * Physics.gravity.y * tx * tx;
 
-        if (Mathf.Abs(dy) < 0.62f) { //在靶子范围内,则用update运算物理
+        float tbRadius = 0.62f;//靶子半径
+        //之所以这样比较,是因为发射位置跟靶子中心是等高的,靶子半径是已知的
+        if (Mathf.Abs(dy) < tbRadius) { //在靶子范围内,则用update运算物理
             finalPoint = absoluteRay.point;
             finalPoint.y = absoluteRay.transform.parent.position.y + dy;
             useUpdatePhysics = true;
             parabolaAngleInRadian = finalAngleX / 180 * Mathf.PI;
             RandomUseSideCamera();
-        } else {
+        } else { //否则,调下角度就好,到时候用unity物理运算
             Vector3 eulerAngle = this.transform.eulerAngles;
             eulerAngle.x = -finalAngleX;
             this.transform.eulerAngles = eulerAngle;
@@ -114,8 +129,34 @@ public class Arrow : MonoBehaviour
         }
     }
 
+    /**发射角有无解 */
     bool hasParabolaAngle = false;
+    /**发射角弧度解 */
     float parabolaAngleInRadian = 0;
+    /**
+        求弓箭发射到指定坐标所需要的角度。
+        已知初速度大小V,重力g,起始坐标(a1,a2),目标坐标(b1,b2)。
+        解:
+        1、列出关系式
+        Δx = b1 - a1;
+        Δy = b2 - a2;
+        Vx = V * cos(angle)
+        Vy = V * sin(angle)
+        Vy * t + 1/2 * g * t^2 = Δy
+        Vx * t = Δx
+        t = Δx / Vx
+        2、推导过程
+        (V * sin(angle)) * Δx / (V * cos(angle)) + 1/2 * g * Δx^2 / (V^2*cos(angle)^2) = Δy
+        tan(angle) * Δx + 1/2 * g * Δx^2 / (V^2*cos(angle)^2) = Δy
+        tan(angle) * Δx + 1/2 * g * (Δx^2 / V^2) * (1 + tan(angle)^2) = Δy
+        3、根据求根公式得出结论
+        a = 1/2 * g * Δx^2 / V^2
+        b = Δx
+        c = a -  Δy
+        d = tan(angle) = (-b ± (b^2 - 4*a*c)^0.5) / (2*a)
+        angle = atan(d) 
+        有无根的判别式,有根则b^2-4*a*c>=0
+    */
     void CalculateParabolaAngle(Vector3 destination)
     {
         float deltaX = Vector2.Distance(
@@ -142,9 +183,6 @@ public class Arrow : MonoBehaviour
         }
     }
 
-    Vector3 position;
-    Quaternion rotation;
-
     void FixedUpdate()
     {
         if (newRigidbody) {
@@ -152,8 +190,6 @@ public class Arrow : MonoBehaviour
         }
         if (!isHit && flyTime >= 0) {
             flyTime += Time.deltaTime;
-            this.position = this.transform.position;
-            this.rotation = this.transform.rotation;
             if (flyTime > 14) {
                 Destroy(gameObject);
                 GameMgr.ins.gameMode.HitTarget(0);
@@ -172,6 +208,7 @@ public class Arrow : MonoBehaviour
     bool useUpdatePhysics = false;
     float flyTimeWithoutPhysics = 0;
     Vector3 finalPoint = default;
+    /**更新箭的飞行轨迹(非Unity物理运算)*/
     void UpdateFlyWithoutPhysics() {
         if (!useUpdatePhysics || isHit) return;
         flyTimeWithoutPhysics += Time.deltaTime;