好久没做过2D项目,今天做一个摇奖转盘,没多想做出来发现停下来很生硬。就模拟加速和减速,这里是距离,最后换成改变transform改变角度就好了。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//计算转盘的加速度方法
//by thinbug 2019.3
//这里为了直观看到表现,让一个盒子前进和刹车.调试好后可以把距离改成旋转
public class TBTurnAround : MonoBehaviour {
//
//加速度 a = (v - v0) / t
//瞬时速度公式 v = v0 + at;
//位移公式 x = v0t +½at²;
//平均速度 v = x / t = (v0 + v) / 2
//导出公式 v²-v0²=2as
//(单位均为国际单位,即a的单位为m/s²,x的单位为m,v的单位为m/s)
float v1 = 100f; //最快时候的速度
float s1 = 170f; //需要行进的距离,这个可以替换成转盘的角度localEulerAngles , 角度一直累加(减少),方向不同,不用考虑360的问题,Inspector面板显示的是360,实际值随着转动很大(很小).
float s2 = 30f; //开始制动停止的距离
public int stat;
void Start () {
}
Vector3 at = Vector3.zero;
float btime = 0f;
float t;
float a;
float pass;
void Update ()
{
if (stat == 1)
{
Debug.Log("加速开始");
//状态等于1的时候开始.
btime = Time.time;
stat = 2;
//根据公式v²-v0²=2as计算出启动的加速度
//a = (v²-v0²)/2s
a = Mathf.Pow(v1, 2) / (2 * s1);
}
if (stat == 2)
{
//当前运行了多久,计算当时行走的距离
t = Time.time - btime;
pass = v1 * t + 0.5f * a * t * t; //S = 1/2 at^2;
at.z = pass;
if (pass > s1)
{
at.z = s1;
transform.transform.position = at;
Debug.Log("减速开始");
stat = 3;
btime = Time.time;
//减速也是先计算刹车的加速度
a = -Mathf.Pow(v1, 2) / (2 * s2);
return;
}
transform.transform.position = at;
}
if (stat == 3)
{
t = Time.time - btime;
pass = v1 * t + 0.5f * a * t * t;
//这个计算下当时的速度,判断是否该停下来了.如果是负数.
float atv = v1 + a * t;
if (atv < 0f)
{
at.z = s1 + s2;
transform.transform.position = at;
stat = -1;
Debug.Log("到了");
return;
}
at.z = pass + s1;
transform.transform.position = at;
}
}
}
在场景中拖动一个球,拖脚本上去,把变量stat改成1就开始了。
补充:
多次使用到这个脚本,又进行了一些修改,改为一个静态函数,方便调用。下面是代码。
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
public class MathFunction : MonoBehaviour
{
//
//加速度 a = (v - v0) / t
//瞬时速度公式 v = v0 + at;
//位移公式 x = v0t +½at²;
//平均速度 v = x / t = (v0 + v) / 2
//导出公式 v²-v0²=2as
//(单位均为国际单位,即a的单位为m/s²,x的单位为m,v的单位为m/s)
//计算加速度方法
//by thinbug 2022.9.16
//这里为了直观看到表现,让一个盒子前进和刹车.
//物理加速后进行减速
/// <summary>
///
/// </summary>
/// <param name="initSpeed">初速度</param>
/// <param name="accelerateMaxSpeed">加速到最高时候的速度</param>
/// <param name="accelerateMaxDis">加速到最高时候到起点的距离</param>
/// <param name="decelerateMaxDis">减速的最长距离</param>
/// <param name="action">返回结果{是否结束,距离}</param>
/// <param name="callbackFrameRate">返回的周期单位毫秒</param>
/// <returns></returns>
public static async Task PhysicsAccelerateDecelerate(float initSpeed = 50f, float accelerateMaxSpeed = 100f, float accelerateMaxDis = 100f, float decelerateMaxDis = 100f, UnityAction<bool,float> action = null, int callbackFrameRate = 50)
{
int delaytime = (int)(1000f / callbackFrameRate); //每帧的时间
int state = 1;
float btime = Time.time;
float t;
float a;
float pass;
float nowAt = 0f;
//加速阶段
//根据公式v²-v0²=2as计算出启动的加速度
//a = (v²-v0²)/2s
a = (Mathf.Pow(accelerateMaxSpeed, 2) - Mathf.Pow(initSpeed, 2)) / (2 * accelerateMaxDis);
while (state > 0)
{
action?.Invoke(false, nowAt);
await Task.Delay(delaytime);
if (state == 1)
{
//当前运行了多久,计算当时行走的距离
t = Time.time - btime;
//瞬时速度公式 v = v0 + at;
//位移公式 x = vt +½at²;
//pass = accelerateMaxSpeed * t + 0.5f * a * t * t; //S = 1/2 at^2;
pass = initSpeed * t + 0.5f * a * t * t; //S = 1/2 at^2;
nowAt = pass;
if (pass > accelerateMaxDis)
{
nowAt = accelerateMaxDis;
state= 2;
btime = Time.time;
//减速也是先计算刹车的加速度,vt是0
a = 0-Mathf.Pow(accelerateMaxSpeed, 2) / (2 * decelerateMaxDis);
}
}
else if (state == 2)
{
t = Time.time - btime;
pass = accelerateMaxSpeed * t + 0.5f * a * t * t;
//这个计算下当时的速度,判断是否该停下来了.如果是负数.
float atv = accelerateMaxSpeed + a * t;
if (atv < 0f)
{
nowAt = accelerateMaxDis + decelerateMaxDis;
state = -1;
}
else
{
nowAt = pass + accelerateMaxDis;
}
}
}
action?.Invoke(true, nowAt);
}
//下面的是演示如何使用
private async void OnEnable()
{
await PhysicsAccelerateDecelerate(initspd, addspd, dis1, dis2, test, 50);
}
public float initspd = 10f;
public float addspd = 50f;
public float dis1 = 100f;
public float dis2 = 50f;
void test(bool finish,float dis)
{
Debug.Log("Finish:"+finish +" ,dis : "+dis);
Vector3 at = transform.position;
at.z = dis;
transform.position = at;
}
}