微积分:切线

     水一篇博客,做一个基础数学搬运工。计算切线应该说算是很基础的数学计算之一,但是涉及到了微积分,而且在简单物理运算中很常用。

     比如我们要做一个自由落体运动,但是为了性能考虑不使用物理组件,而是使用水平方向匀速运动s = v*t,垂直方向自由落体运动h = g*t^2/2,得到函数图像,同时求函数图象中某个点的切线。这种做法在需要预计算的逻辑处理中很常见。

     首先我们把自由落体运动的函数求解出来,如下:

     如果我们将时间t作为变量,那么x方向就存在函数f(t) = v*t,y方向f(t) = -0.5*g*t^2,综合起来的线性映射f(v*t) = -0.5*g*t^2。

     程序中模拟一下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GravTest : MonoBehaviour
{
    public Transform ball;  
    [Range(1,20)]
    [SerializeField] public float HorConstSpeed = 5.0f;
    [SerializeField] public int MaxDisperseCount = 100;
    private const float GravAccelerat = 9.8f;

    private int disperseCount = 0;

    private float t = 0f;
    private float time = 0.0f;

    void Start()
    {
        
    }

    void Update()
    {
#if UNITY_EDITOR
        t = 0f;
        for (int i = 0; i < MaxDisperseCount; i++)
        {
            t += Time.deltaTime;
            float x = t;
            float y = -0.5f * GravAccelerat / HorConstSpeed * t * t;
            Vector3 fpos = new Vector3(x, y, 0);
            float t1 = t + Time.deltaTime;
            x = t1;
            y = -0.5f * GravAccelerat / HorConstSpeed * t1 * t1;
            Vector3 tpos = new Vector3(x, y, 0);
            Debug.DrawLine(fpos, tpos, Color.black);
        }
#endif
        time += Time.deltaTime;
        float tx = time;
        float ty = -0.5f * GravAccelerat / HorConstSpeed * time * time;
        Vector3 pos = new Vector3(tx, ty, 0);
        ball.position = pos;
        disperseCount++;
        if (disperseCount >= MaxDisperseCount)
        {
            disperseCount = 0;
            time = 0.0f;
        }
    }
}

       我们离散100个数据量,去做模拟,效果如下:

  

       同时我们可以看到一个问题,那就是小球的朝向不会改变,并不会“朝着运动方向”,我猜很多同学的做法肯定很简单粗暴,那就是根据下个position-上个position得到的向量去修改小球的朝向向量。但是严格来讲, 这并不是切线方向,虽然很近似切线方向。

      如果要计算严格切线方向,我们用微积分的角度来理解:

      切线是什么?如果我们拿一把直尺和圆柱体水杯,那么直尺贴近水杯,相交的点为切点,直尺所在的直线为切线。

      函数切线怎么算?以上面的函数为例,我们两个离散的坐标的朝向方向就十分近似切线方向,为了便于形象理解,我们先改造一下函数代码,如下: 

//假设我们已1000个deltatime为x总长度
    private float getDisperseDelta(int count)
    {
        return 1000 * Time.deltaTime / (float)count;
    }

    void Update()
    {
#if UNITY_EDITOR
        t = 0f;
        for (int i = 0; i < MaxDisperseCount; i++)
        {
            float x = t;
            float y = -0.5f * GravAccelerat / HorConstSpeed * t * t;
            Vector3 fpos = new Vector3(x, y, 0);
            
            float t1 = t + getDisperseDelta(MaxDisperseCount);
            x = t1;
            y = -0.5f * GravAccelerat / HorConstSpeed * t1 * t1;
            Vector3 tpos = new Vector3(x, y, 0);

            Color col = i % 2 == 0 ? Color.black : Color.red;
            Debug.DrawLine(fpos, tpos, col);

            t += getDisperseDelta(MaxDisperseCount);
        }
#endif
        time += getDisperseDelta(MaxDisperseCount);
        float tx = time;
        float ty = -0.5f * GravAccelerat / HorConstSpeed * time * time;
        Vector3 pos = new Vector3(tx, ty, 0);
        //ball.position = pos;
        disperseCount++;
        if (disperseCount >= MaxDisperseCount)
        {
            disperseCount = 0;
            time = 0.0f;
        }
    }

        我们把离散的数量参数进行改变,会看到以下现象,如图:

   

       看得出来,如果我们将离散数量增大,那么两点之间的朝向方向就越近似于起点的切线方向。

       那么假设我们离散到两个点的t变化量无限小,也就是Δt无限小(英文delta),就可以算出起点的切线向量,当然我们可以反过来说如果我们离散的数量无限大,MaxDisperseCount设置为int.MaxValue,我们也就可以通过两点的坐标算出切线,如果你的CPU是量子CPU的话。

       这里我们还是用微分运算来进行计算,我们假设t存在Δt的变化(也就是x轴变化),同时得到f(t)到f(t+Δt)的变化(也就是y轴的变化),那么可以通过起点二维向量(t,f(t))和Δ变化的终点二维向量(t+Δt,f(t+Δt))得到切线的斜率,同时根据起点就能算出切线的向量了,如下:

       

       上面根据微分lim Δt->0(即=0)计算出斜率,然后我们再根据起点二维坐标计算出切线向量,代码如下:

Vector3 pos = new Vector3(tx, ty, 0);
float slope = -GravAccelerat / HorConstSpeed * time;
Vector3 tan = pos + new Vector3(1, slope, 0) * 5.0f;
#if UNITY_EDITOR
        Debug.DrawLine(pos, tan, Color.white);
#endif

       下面看下效果,如图:

      明显看得出来白色的切线向量吧。

      所以我们也可以说得到一个结论:瞬间的弦的斜率=切线的斜率

      好,我继续潜水学习,偶尔出来写一篇博客冒泡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值