VR乒乓球项目Unity3D 开发经验整理,3AI

其实理论上这个AI应该不难,

  1. 我们有击球点,假设一个落点,中间是抛物线,
  2. 再加入一个第三点就能解出抛物线方程。因为一定要经过球网,所以假设球网上方有一个点。
  3. 有了轨迹方程,就可以计算球的出发速度,
  4. 这个速度是碰撞后的矢量,碰撞前的速度矢量我们也有,
  5. 那就是如何控制球拍发生碰撞才能让球按照计划移动。
  6. 根据(可能是)高中物理,完全弹性碰撞的速度计算,m1*v1+m2*v2=m1*v1'+m2*v2',可以计算出碰撞前的球拍速度。
  7. 参考https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E5%BC%B9%E6%80%A7%E7%A2%B0%E6%92%9E/1306748?fr=aladdin
  8. 有了碰撞前的球拍速度,下面分析碰撞前的球拍角度。球和平面的碰撞应该和光的反射类似,我们有入射方向和出射方向,可以求出法线方向,然后得到平面方向。
  9. 最后按照计算出的速度和角度给球拍赋值,前面计算出的速度是标量,方向就沿着法线方向移动吧。(ps:大学物理挂科,大学物理实验挂科),补充:又查了一些资料,据说速度按矢量计算就可以得到矢量速度,这样就应该按照速度方向调整球拍方向。
  10. 编码测试效果两边都用AI,结果发现全部白费,计划是每次接球落点完全一致,实际神仙打架,虽然大多数球被接到了,但是落点完全没有规律。
  11. 要么继续检查上述方法中的错误,要么改变策略,用机器学习训练个AI吧。

下面是数学和物理学的公式。考虑侧视图,二维视角,z轴水平,y轴竖直,球桌中点是(0,0)点

抛物线方程,y=a*z*z+b*z+c

球网上方一点(0,0.25),c=0.25

落地点(z1,0),击球点(z2,y2)

 float b = ((y2 - c) * z1 * z1 - (y1 - c) * z2 * z2) / (z1 * z2 * (z1 - z2));
 float a = ((y1 - c) * z2 - (y2 - c) * z1) / (z1 * z2 * (z1 - z2));

以上,得到抛物线方程,下面计算发球速度

先找到抛物线最高点,可以算出最高点到击球点高度h1,时间t1,最高点到落地点高度h2,时间t2

 float z3 = -b / (2 * a);
 // float y3 = (4*a*c-b*b)/(4*a);
 float y3 = a * z3 * z3 + b * z3 + c;
 float h1 = y3 - y2;
 float h2 = y3 - 0;
 float g = 9.8f;
 float t1 = Mathf.Sqrt(2 * h1 / g);
 float t2 = Mathf.Sqrt(2 * h2 / g);
 float vz = (z1 - z2) / (t2 + t1);
 float vy = g * t1;

 float vx = (x1 - x2) / (t2 + t1);
x轴只是匀速直线运动,至此三轴发球速度得到。ps:unity有模拟空气阻力,影响不大,本文忽略掉

    private Vector3 calcLine(float x1,float y1,float z1,float x2,float y2,float z2,float c)
    {
        //c=0.25,x1=-1.37,y1=0,x2,y2
        //b=((y2-c)*x1*x1-(y1-c)*x2*x2)/(x1*x2*(x1-x2))
        //a=((y1-c)*x2-(y2-c)*x1)/(x1*x2*(x1-x2)
        Vector3 ret;
        float b = ((y2 - c) * z1 * z1 - (y1 - c) * z2 * z2) / (z1 * z2 * (z1 - z2));
        float a = ((y1 - c) * z2 - (y2 - c) * z1) / (z1 * z2 * (z1 - z2));

        float k = a * z2;
        if (k < 0)//先上升再下降
        {
            float z3 = -b / (2 * a);
            // float y3 = (4*a*c-b*b)/(4*a);
            float y3 = a * z3 * z3 + b * z3 + c;
            float h1 = y3 - y2;
            float h2 = y3 - 0;
            float g = 9.8f;
            float t1 = Mathf.Sqrt(2 * h1 / g);
            float t2 = Mathf.Sqrt(2 * h2 / g);
            float vx = (x1 - x2) / (t2 + t1);
            float vz = (z1 - z2) / (t2 + t1);
            float vy = g * t1;
            ret = new Vector3(vx, vy, vz);
            top.transform.position = new Vector3(0, y3, z3);
        }
        else//直接下降
        {
            float h2 = y2;
            float g = 9.8f;
            float t1 = 0;
            float t2 = Mathf.Sqrt(2 * h2 / g);
            float vx = (x1 - x2) / (t2 + t1);
            float vz = (z1 - z2) / (t2 + t1);
            float vy = 0;
            ret = new Vector3(vx, vy, vz);
        }

        for (int i = 0; i < 100; i++)
        {
            float z = i * (z1 - z2) / 100 + z2;
            float y = a * z * z + b * z + c;
            float x = i * (x1 - x2) / 100 + x2;
            line.SetPosition(i, new Vector3(x, y, z));
        }
        return ret;
    }

下面是球拍速度计算方法

能量守恒方程:0.5*m1*v1*v1+0.5*m2*v2*v2=0.5*m1*v1'+0.5*m2*v2'*v2'

动量守恒方程:m1*v1+m2*v2=m1*v1'+m2*v2'

求解之后:v2'=(2*m1*v1+(m2-m1)*v2)/(m1+m2)

v2'是碰撞后球的速度,v1'是碰撞后球拍的速度,v1是碰撞前球拍的速度,v2是碰撞前球的速度。

现在已知v2,v2',求v1,对上面公式转换就好

v1=((m1+m2)*v2'-(m2-m1)*v2)/(2*m1)

把所有的速度当作矢量计算就可以得到矢量的v1,然后用这个v1控制AI球拍就可以了。

下面是球拍方向

myracket.GetComponent<Rigidbody>().MoveRotation(Quaternion.LookRotation(v1, Vector3.up));
myracket.GetComponent<Rigidbody>().velocity = v1;

最后,封装成函数,方便两边调用

AI3(racket, g1, aim2.transform.position, zero.transform.position,1);
AI3(racket2, g1, aim3.transform.position, zero2.transform.position, -1);
 public void AI3(GameObject myracket, GameObject ball, Vector3 aim, Vector3 zero,int direction)
    {
        Vector3 v = ball.transform.position;
        if (v.z * direction > zero.z* direction - 0.5 )
        {
            if (v.z * direction < zero.z * direction - 0.2)
                myracket.GetComponent<Rigidbody>().MovePosition(new Vector3(v.x, v.y, zero.z));

            //Vector3 inV = racket.transform.position - g1.transform.position;
            //Vector3 outV = racket.transform.position - aim2.transform.position;
            //Vector3 faxian = Vector3.Lerp(inV, outV, 0.5f);
            //Debug.Log(faxian);
            //racket.GetComponent<Rigidbody>().MoveRotation(Quaternion.LookRotation(new Vector3(faxian.x, 0, faxian.z), Vector3.up));

            float x2 = myracket.transform.position.x;
            float y2 = myracket.transform.position.y;
            float z2 = myracket.transform.position.z;
            float x1 = aim.x;
            float y1 = aim.y;
            float z1 = aim.z;
            //float c = UnityEngine.Random.Range(2.0f,5.0f);
            float c = height;
            Vector3 v22 = calcLine(x1, y1, z1, x2, y2, z2, c);
            Vector3 v2 = g1.GetComponent<Rigidbody>().velocity;
            Vector3 v1 = calcSpeed(0.2f, 0.0025f, v2, v22);
            //Vector3 faxian = (-v2+ v22)*0.5f;
            //Debug.Log(""+ v2+v22+faxian);
            myracket.GetComponent<Rigidbody>().MoveRotation(Quaternion.LookRotation(v1, Vector3.up));
            myracket.GetComponent<Rigidbody>().velocity = v1;
        }
        else
        {
            myracket.GetComponent<Rigidbody>().MovePosition(new Vector3(v.x, v.y, zero.z));
            myracket.GetComponent<Rigidbody>().MoveRotation(Quaternion.LookRotation(Vector3.back, Vector3.up));
            myracket.GetComponent<Rigidbody>().velocity =Vector3.zero;
        }
    }

至少可以达到神仙打架的效果,数学方面以我的能力是够呛了,后面用机器学习试试吧

猜你喜欢

转载自blog.csdn.net/u010752777/article/details/108284561
今日推荐