人人都是秋名山车神——Unity实现简化版卡丁车漂移


大家好。

前一阵子,Unity出了个名为Karting Microgame的教程项目,里边有一个赛车小游戏示例场景,行驶、转弯、漂移等主要功能都几乎都是通过数学计算来完成的,同时还提供了场景资源供使用者自定义修改完善游戏。这让我想起在YouTube上mix and jam大神出过一个制作马里奥赛车漂移的视频,于是也想尝试自己捣鼓一个简化版的漂移(用刚体加力什么的),实现效果如下:

一、前期准备
1 导入资源(使用其他资源的可跳过)

在Asset Store里搜索并下载Karting Microgame > 导入,写文章时该资源暂时还不支持Unity 2019.x版本。

2 组合模型(使用其他资源的可跳过)

导入资源后新建场景,这次主要用到的是包里提供卡丁车以及赛道的模型,脚本另外重新写。

(1) 组合卡丁车

Hierarchy窗口中新建空的GameObject命名为KartPlayer

②打开文件夹 Assets > Karting >Models。

③找到Kart并拖到KartPlayer下实例化。

④继续在Models文件夹中找到WartWheels和Player,并把他们拖到Kart GameObject下实例化,成为Kart的子物体,层次结构如下:

(2)分配Animator Controller(播放Idle动画,可以不做)

①打开Assets > Karting > Animations > Controllers文件夹,里边的Animator Controller已经配好Idle动画。

②把KartControllrt分配给Kart GameObject的Animator组件(拖到Kart上)。

③把PlayerControllrt分配给Player GameObject的Animator组件(拖到Player 上)。

3 添加并且配置Collider与RigidBody

①添加Capsule Collider,设置参数(根据使用模型自行调节),使Capsule Collider大小与Kart相匹配,如下图:

Center:0.6
Height:2
Direction:Z-Axis

②添加Rigidbody,配置如下:

Drag:2,增加阻力,用于模拟摩擦力。
Freeze Rotation:勾选X轴和Z轴,防止加力的时候翻车。

现在Ctrl+P试执行,车和人物都在播放动画,Player可能需要微调一下位置,调整完成后效果如下:

终于把前置准备工作完成了,接下来开始整理一下实现思路。

二、需求分析
1 这次目标只实现最基本的几个功能:

(1)方向键控制移动。

(2)按住空格漂移。

(3)漂移结束时获得加速。

(4)漂移粒子特效随着漂移时间变色。

2 实现思路

(1)根据以上的功能,在Update()实现控制操作:

①首先获取竖直和水平方向的输入。

②当按住空格并且有水平输入时,在落地的瞬间 && 不在漂移状态 &&有一定速度时开始漂移;

③松开空格键时,如果在漂移,则根据漂移等级给予加速,并且停止漂移。

代码如下:

void Update()
{
//输入相关
v_Input = Input.GetAxisRaw(“Vertical”); //竖直输入
h_Input = Input.GetAxisRaw(“Horizontal”); //水平输入

    //按下空格起跳if (Input.GetKeyDown(KeyCode.Space)){if (isGround)   //如果在地上{Jump();}}//按住空格,并且有水平输入:开始漂移if (Input.GetKey(KeyCode.Space) && h_Input != 0){//落地瞬间、不在漂移并且速度大于一定值时开始漂移if (isGround && !isGroundLastFrame && !isDrifting && kartRigidbody.velocity.sqrMagnitude > 10){StartDrift();   //开始漂移}}//放开空格:漂移结束if (Input.GetKeyUp(KeyCode.Space)){if (isDrifting){Boost(boostForce);//加速StopDrift();//停止漂移}}
}

(2)在FixedUpdate()中进行逻辑运算:

①控制车的转向。

车要与地面大致平行。
根据水平输入,计算车将会绕自己的Y轴如何旋转。
②计算添加到车上的力的大小以及方向。

③如果在漂移,计算漂移等级同时根据漂移等级改变粒子特效颜色。

④为了足够的简单粗暴,功能最终是通过旋转刚体以及给刚体加力来完成的。

代码如下:

private void FixedUpdate()
{
//车转向
CheckGroundNormal(); //检测是否在地面上,并且使车与地面保持水平
Turn(); //输入控制左右转向

    //起步时 力递增IncreaseForce();//漂移加速后/松开加油键 力递减ReduceForce();//如果在漂移if (isDrifting){CalculateDriftingLevel();   //计算漂移等级ChangeDriftColor();         //根据漂移等级改变颜色}//根据上述情况,进行最终的旋转和加力kartRigidbody.MoveRotation(rotationStream);//计算力的方向CalculateForceDir();//移动AddForceToMove();
}

如果对不依赖物理系统来完成功能有兴趣的小伙伴可以参考Karting Microgame的教程项目。

三 功能实现
有了大概的思路之后,开始着手实现功能。

1 漂移

本次想实现的是类似与马里奥赛车那中非拟真的漂移,因此下边只为了简单实现表现效果而给刚体加的力,并非拟真的受力分析。

(1)根据触地时的水平输入,决定漂移时的车头朝向。

(2)由于漂移时合速度的方向与车头的朝向是不一致的,所以需要为最终的合力方向添加一个偏移量m_DriftOffset。以左漂移为例,合速度方向为车头朝向的右前方,如图所示(绿线为偏移后加力方向):

(3)开始漂移时播放粒子特效。

代码如下:

//开始漂移并且决定漂移朝向
public void StartDrift()
{isDrifting = true;//根据水平输入决定漂移时车的朝向,因为合速度方向与车身方向不一致,所以为加力方向添加偏移if (h_Input < 0){driftDirection = DriftDirection.Left;//左漂移时,合速度方向为车头朝向的右前方,偏移具体数值需结合实际自己调试m_DriftOffset = Quaternion.Euler(0f, 30, 0f);}else if (h_Input > 0){driftDirection = DriftDirection.Right;m_DriftOffset = Quaternion.Euler(0f, -30, 0f);}//播放漂移粒子特效PlayDriftParticle();
}

(3)停止漂移:把恢复漂移朝向,并且调整偏移。

public void StopDrift()
{
isDrifting = false;
driftDirection = DriftDirection.None;
driftPower = 0;
m_DriftOffset = Quaternion.identity;
StopDriftParticle();
}

2 转向

(1)通过射线检测地面法线,使车与地面大致水平

在靠近车头中心和车尾中心两个位置分别添加一个空的GameObject,从这两个点往车底方向打射线,射线长度比发射点到车底的距离稍长。
如果其中一条射线打中地面,则判断车在地面上,获取击中的地面的法线。
车的Up方向为两地面的法线相加的方向(主要用在上下坡的时候)。

代码如下:

public void CheckGroundNormal()
{//从车头中心附近往下打射线,长度比发射点到车底的距离长一点RaycastHit frontHit;bool hasFrontHit = Physics.Raycast(frontHitTrans.position, -transform.up, out frontHit, groundDistance, LayerMask.GetMask("Ground"));//从车尾中心附近往下打射线RaycastHit rearHit;bool hasRearHit = Physics.Raycast(rearHitTrans.position, -transform.up, out rearHit, groundDistance, LayerMask.GetMask("Ground"));isGroundLastFrame = isGround; //储存上一帧是否在地面if (hasFrontHit || hasRearHit)//更新判断现在是否在地面{isGround = true;}else{isGround = false;}//使车与地面水平Vector3 tempNormal = (frontHit.normal + rearHit.normal).normalized;Quaternion quaternion = Quaternion.FromToRotation(transform.up, tempNormal);rotationStream = quaternion * rotationStream;
}

(2)通过水平输入控制转向

马里奥赛车在漂移的时候只需按着漂移键和加油键就能按照原先朝向一直绕着某个点作圆周的运动,并且可以通过左右方向键进行微调。因此推断如下:

①漂移时需要自带一个方向的旋转。

②此时通过输入可控的旋转角速度要小于漂移自带旋转的角速度,否则会漂移到一定程度回反向飘。

③当车后退时,旋转方向与前进时相反。

大概路线如图(以左漂移为例):

根据流程对RotationStream变量进行处理,最终用kartRigidbody.MoveRotation(rotationStream)使车转向。

RotationStream处理流程:

代码如下(由于时间关系,角度直接写死了,有兴趣的同学可以自己细调):

public void Turn()
{
//只能在移动时转弯
if (kartRigidbody.velocity.sqrMagnitude <= 0.1)
{
return;
}

    //漂移时自带转向if (driftDirection == DriftDirection.Left){rotationStream = rotationStream * Quaternion.Euler(0, -40 * Time.fixedDeltaTime, 0);}else if (driftDirection == DriftDirection.Right){rotationStream = rotationStream * Quaternion.Euler(0, 40 * Time.fixedDeltaTime, 0);}//后退时左右颠倒float modifiedSteering = Vector3.Dot(kartRigidbody.velocity, transform.forward) >= 0 ? h_Input : -h_Input;//输入可控转向:如果在漂移,可控角速度为30,否则平常状态为60.turnSpeed = driftDirection != DriftDirection.None ? 30 : 60;float turnAngle = modifiedSteering * turnSpeed * Time.fixedDeltaTime;Quaternion deltaRotation = Quaternion.Euler(0, turnAngle, 0);rotationStream = rotationStream * deltaRotation;//局部坐标下旋转,这里有空换一个简单的写法
}

3 移动

处理完转向问题,剩下只需要计算力的方向并且加力,车就能动起来了。

(1)在水平方向(车的XZ平面)上,从车的前方进行一个漂移造成的偏移m_DriftOffset(在第一步漂移时已经计算好)。

(2)当竖直输入 < 0时,往反方向加力。

//计算加力方向
public void CalculateForceDir()
{
//往前加力
if (v_Input > 0)
{
verticalModified = 1;
}
else if (v_Input < 0)//往后加力
{
verticalModified = -1;
}

    forceDir_Horizontal = m_DriftOffset * transform.forward;
}//加力移动
public void AddForceToMove()
{//计算合力Vector3 tempForce = verticalModified * currentForce * forceDir_Horizontal;if (!isGround)  //如不在地上,则加重力{tempForce = tempForce + gravity * Vector3.down;}kartRigidbody.AddForce(tempForce, ForceMode.Force);
}

4 加速

放开空格键时,如果是漂移状态,则改变当前的力,并且激活拖尾,然后力再慢慢递减。

//加速
public void Boost(float boostForce)
{
//按照漂移等级加速:1 / 1.1 / 1.2
currentForce = (1 + (int)driftLevel / 10) * boostForce;
EnableTrail();
}

//力递减
public void ReduceForce()
{
float targetForce = currentForce;
if (isGround && v_Input == 0)
{
targetForce = 0;
}
else if (currentForce > normalForce) //用于加速后回到普通状态
{
targetForce = normalForce;
}

    if (currentForce <= normalForce){DisableTrail();}//每秒60递减,可调currentForce = Mathf.MoveTowards(currentForce, targetForce, 60 * Time.fixedDeltaTime);
}

5 漂移特效变色

马里奥赛车中,漂移火花的颜色会随着时间或者搓方向键改变,这次只实现随着时间变化。

(1)脚本声明Color[]数组变量driftColors,并在Inspector面板中指定颜色。

(2)声明一个枚举变量作为漂移等级,当漂移时,在FixedUpdate中计算漂移等级。

public void CalculateDriftingLevel()
{driftPower += Time.fixedDeltaTime;//0.7秒提升一个漂移等级if (driftPower < 0.7)   {driftLevel = DriftLevel.One;}else if (driftPower < 1.4){driftLevel = DriftLevel.Two;}else{driftLevel = DriftLevel.Three;}
}

(3)在场景中把粒子拖为KartPlayer的子对象,并移到车轮底下,在Start()里获取所有特效。

void Start()
{
//…
//漂移时车轮下粒子特效
wheelsParticeles = wheelsParticeleTrans.GetComponentsInChildren();
}

(4)根据漂移等级(作为颜色数组下标),改变粒子特效的颜色,这次用到的粒子特效是由多层叠加而成,所以需要遍历赋值。

public void ChangeDriftColor()
{foreach (var tempParticle in wheelsParticeles){var t = tempParticle.main;t.startColor = driftColors[(int)driftLevel];}
}

此外,如果需要比较好的摄像机跟随与特效效果可以使用Cinemachine进行调节,有兴趣的小伙伴可以参考工程里的设置。

结语:本篇中,通过旋转刚体并给刚体加力的方式,实现了卡丁车的漂移、转向、移动以及加速等功能,影响手感的参数强烈建议自己再调整,希望大家玩得开心,漂移时粒子特效的制作以后有机会再介绍~


参考资料:
Karting Microgame:Asset Store里可以下载,对不依赖物理系统来实现漂移感兴趣的小伙伴推荐参考这个工程里的脚本~

mix and jam:https://www.youtube.com/watch?v=Ki-tWT50cEQ

链接:https://pan.baidu.com/s/13RIAzicgicL3xlnz2PZfEg

提取码:cghc

人人都是秋名山车神——Unity实现简化版卡丁车漂移相关推荐

  1. Bugku web19秋名山车神

    Bugku web18秋名山车神 前言 一.知识补充 1.正则表达试补充 2.re 函数介绍 二.web 19 解题过程 三 .秋名山车神 总结 文章目录 前言 一.知识补充 1.正则表达试补充 2. ...

  2. 从脚本学python(秋名山车神)

    最近做bugku的时候,做到秋名山车神那题,连python都不会(泪)很惭愧 https://zhuanlan.zhihu.com/p/141333473 脚本代码来自上面这个writeup impo ...

  3. 键盘敲代码out了!编程用“说”的,人人都是程序员

    原创竞科技 2021年了,人人都知道掌握计算机知识和编程技能的重要性! 可即便计算机语言已经由古早原始的机器语言.汇编语言进化到如今以C.C++.Python.PHP.Ruby等为代表"简化 ...

  4. 【总结整理】租房产品创业的三个方向和三个产品---摘自《人人都是产品经理》...

    创业产品反思录:从拆解元需求到产品定位 人人都是产品经理社区 发布于 2018-10-08 08:56:31 举报 阅读数:466 ​​10个人,10个月,100万,3款产品,我们在变幻莫测的租房领域 ...

  5. 周杰伦林俊杰李宗盛快失业了!有了AI,未来人人都能秒变作曲家

    译者 | thinkdeeper 编辑 | 鸽子 有没有想过,未来你也可以像周杰伦.林俊杰.李宗盛一样成为创作型的音乐人?你要的,只是感觉,其他的,人工智能的研究人员已经帮你做好了. 如果你以为这只是 ...

  6. python编程可视化小程序_人人都可以写的可视化Python小程序第二篇:旋转的烟花...

    兴趣是最好的老师 枯燥的编程容易让人放弃,兴趣才是最好的老师.无论孩子还是大人,只有发现这件事情真的有趣,我们才会非常执着的去做这件事,比如打游戏.如果编程能像玩游戏一样变得有趣,我相信很多人就特别愿 ...

  7. 关于学外语,这些人人都知道的“常识”,可能是错的……

    对于大多数人来说,无论上什么学校,修什么专业,外语都是一门绕不开的必修课.这门课看似不难,却让很多人头疼不已. 说它"不难",是因为关于外语学习的资源非常丰富,图书.课程.App. ...

  8. 4月书讯 | 从引爆技术圈的 K8s 到图灵奖得主作品升级,从独角兽项目到人人都要学习的写作逻辑...

    题图 Vincent van Gogh (1853 - 1890), Saint-Rémy-de-Provence, 1890 Credits: Van Gogh Museum, Amsterdam ...

  9. 人人都有好工作:IT行业求职面试必读

    身处技术行业的您还在为找到心仪的工作而绞尽脑汁吗?看过<人人都有好工作>后,你将更加专业,让招聘经理对你刮目相看!图灵教育又一人文ITl力作<人人都有好工作>将在3月中旬上市, ...

最新文章

  1. RxJava 中的map与flatMap
  2. linux 批量kill进程
  3. mysql 开源 ~ canal+otter系列(2)
  4. 四元素的真面目..........简单粗暴
  5. 客户端怎么远程上服务器失败,远程客户端连接不上,与服务器那台,跨网络,怎么解决...
  6. 【XSY2720】区间第k小 整体二分 可持久化线段树
  7. 95-40-115-java.util.concurrent-线程-AbstractExecutorService
  8. C++_类和对象_C++继承_继承中子类的同名成员处理_同名变量_同名函数---C++语言工作笔记065
  9. sop8封装尺寸图_详解MOS管封装
  10. java 拦截器 排除_java – Spring MVC Interceptor排除HTTP方法的路径
  11. 在linux下设置开机自动启动程序的方法
  12. .Net转Java自学之路—基础巩固篇二十二(XML)
  13. C++ string字符串修改和替换方法
  14. 在Windows 10 增加和使用英语语音包
  15. matlab中如何画柱状图,如何在用Matlab画柱状图
  16. 自然场景文本检测识别 - 综述
  17. 机器智能-高频问题:一阶逻辑转化合取范式
  18. Excel文件加密后忘记密码破解方法
  19. django @csrf_exempt
  20. MVC 架构解析 - 模型(Model)篇

热门文章

  1. configure配置安装详解
  2. 第五章——大数定律和中心极限定理
  3. 关于HMM(隐马尔可夫模型)
  4. 千锋教育+计算机四级网络-计算机网络学习-03
  5. Delphi @ ^
  6. 快速创建React Native App
  7. 滚动条 实现的细节代码 SCROLLINFO
  8. 2017.12.10《“剑锋OI”普及组多校联盟系列赛(14)#Sooke#Kornal 的课余时间 》分析报告
  9. linux 安装swig
  10. zabbix启用企业微信告警的脚本