游戏开发论坛

 找回密码
 立即注册
搜索
查看: 6973|回复: 0

如何用unity3d 三分钟实现简单的赛车漂移

[复制链接]

1万

主题

1万

帖子

3万

积分

论坛元老

Rank: 8Rank: 8

积分
36572
发表于 2016-8-18 11:50:53 | 显示全部楼层 |阅读模式
文/阿升哥哥

  提到赛车游戏,大家最关心的应该就是漂移吧?!

  从学unity开始,我就一直在断断续续的研究赛车。

  因为自己技术太烂、悟性太差等原因,我走了不少弯路。

  也许你会说,网上那么多资料,你不会查啊。

  是啊!网上一搜一大把unity赛车例程,还有好几篇被转发了很多次的赛车漂移实现思路。

  【wheelcollider:自己技术太烂搞不定】

  赛车例程几乎都是用wheelcollider来实现的

  我一直以为wheelcollider的参数简直就不是人能调的

  直到上个星期我才听我好朋友 阿亮 说wheelcollider的参数不是随便调的

  它是按照真实赛车来实现的,所以参数也要按照真实赛车来调

  比如赛车车轮的间距、赛车重量、悬挂高度......

  就好比小轿车使用了大货车的悬挂参数、大货车使用了小轿车的车轮参数

  好吧,我承认我自己也是听得迷迷糊糊!

  反正总而言之一句话:wheelcollider的参数的确是非常难调,但是懂的人还是能一下子就调好的(好像说的是废话,我是不懂的- -)

  【实现思路的文章:自己悟性太差搞不定】

  然后是关于赛车漂移实现思路的文章。

  有几篇被转发了很多次的文章,被转发了那么多次,那么漂移应该就是这个实现思路吧?可是大家的实现方式都不一样。

  我根本不知道漂移的实现是否有一个唯一的标准,如果有,那这个标准又是什么呢?

  看了这些文章,漂移我还是没有研究出来,还是一点思路都没有。

  后来我偶然得到了一份商业赛车游戏的代码,然后漂移就实现出来了- -

  听我说起来好像挺容易的!其实研究那份代码的过程特别煎熬!唉,不说了,都是泪啊。

  声明:下文中我要实现出来的漂移仅仅是个简单的漂移,非常简单!!!一个思路而已,并没有用到那份商业赛车游戏中的任何一句代码。

  注意:请使用Unity4.X版本,5.X版本的wheelcollider参数又变了,要哭了。

  准备工作:

  1、下载CarWaypoint插件,使用插件中的demo。

  2、easytouch插件

  导入CarWaypoint插件和easytouch插件

  打开CarWaypoints/demo/Car Physics 场景,可以看到一辆车

1.png

  Hierarchy面板除了前四项其余全部隐藏,运行起来跑一下,还是挺有漂移的感觉的,这是调wheelcollider参数调出来的。

  我们现在需要做的是用摇杆操作赛车,用另外一个思路让赛车转弯时会漂移。

2.png

  创建一个虚拟摇杆取名为:MoveJoystick,InteractionType选择Event Notification

  新建一个C#脚本:RockerController,复制以下代码

  1. 1 #region HeadComments
  2.   2 /* ========================================================================
  3.   3 * Copyright (C) 2015 ArthunGame
  4.   4 *
  5.   5 * 作    者:Arthun
  6.   6 * 文件名称:RockerController
  7.   7 * 功    能:摇杆控制器
  8.   8 *
  9.   9 * =========================================================================
  10. 10 */
  11. 11 #endregion
  12. 12
  13. 13 using UnityEngine;
  14. 14
  15. 15 public class RockerController : MonoBehaviour
  16. 16 {
  17. 17     public static RockerController Instance;
  18. 18
  19. 19     /// <summary>
  20. 20     /// 摇杆移动回调
  21. 21     /// </summary>
  22. 22     /// <param name="isMove"></param>
  23. 23     /// <param name="angle"></param>
  24. 24     /// <param name="move"></param>
  25. 25     public delegate void RockerMoveCallback(bool isMove, float angle, MovingJoystick move);
  26. 26     public RockerMoveCallback rockerMoveCallback;
  27. 27
  28. 28     void Awake()
  29. 29     {
  30. 30         if (Instance != null)
  31. 31             Debug.LogError("Instance RockerController x2");
  32. 32
  33. 33         Instance = this;
  34. 34     }
  35. 35
  36. 36     /// <summary>
  37. 37     /// 当摇杆可用时注册事件(程序启动执行)
  38. 38     /// </summary>
  39. 39     void OnEnable()
  40. 40     {
  41. 41         EasyJoystick.On_JoystickMove += OnJoystickMove;
  42. 42         EasyJoystick.On_JoystickMoveEnd += OnJoystickMoveEnd;
  43. 43     }
  44. 44
  45. 45     /// <summary>
  46. 46     /// 当摇杆不可用时移除事件(程序关闭时执行)
  47. 47     /// </summary>
  48. 48     void OnDisable()
  49. 49     {
  50. 50         EasyJoystick.On_JoystickMove -= OnJoystickMove;
  51. 51         EasyJoystick.On_JoystickMoveEnd -= OnJoystickMoveEnd;
  52. 52     }
  53. 53
  54. 54     /// <summary>
  55. 55     /// 当摇杆销毁时移除事件 (程序关闭时执行)
  56. 56     /// </summary>
  57. 57     void OnDestroy()
  58. 58     {
  59. 59         EasyJoystick.On_JoystickMove -= OnJoystickMove;
  60. 60         EasyJoystick.On_JoystickMoveEnd -= OnJoystickMoveEnd;
  61. 61     }
  62. 62
  63. 63     /// <summary>
  64. 64     /// 移动摇杆结束
  65. 65     /// </summary>
  66. 66     /// <param name="move"></param>
  67. 67     void OnJoystickMoveEnd(MovingJoystick move)
  68. 68     {
  69. 69         OnRockerMoveInfo(false, -1f, move);
  70. 70     }
  71. 71
  72. 72     /// <summary>
  73. 73     /// 正在移动摇杆
  74. 74     /// </summary>
  75. 75     /// <param name="move"></param>
  76. 76     void OnJoystickMove(MovingJoystick move)
  77. 77     {
  78. 78         if (move.joystickAxis.x != 0 || move.joystickAxis.y != 0)
  79. 79         {
  80. 80             OnRockerMoveInfo(true, circularAngle(move), move);
  81. 81         }
  82. 82     }
  83. 83
  84. 84     /// <summary>
  85. 85     /// 获取摇杆全角度 0~360
  86. 86     /// </summary>
  87. 87     /// <param name="move"></param>
  88. 88     /// <returns> 左边0 上90 右180 下 270</returns>
  89. 89     float circularAngle(MovingJoystick move)
  90. 90     {
  91. 91         Vector2 pos = new Vector2
  92. 92         {
  93. 93             x = move.joystickAxis.x * 90f + 90f,
  94. 94             y = move.joystickAxis.y * 90f + 90f
  95. 95         };
  96. 96
  97. 97         if (pos.y < 90f)
  98. 98         {
  99. 99             if (pos.x < 90f)
  100. 100             {
  101. 101                 return 270f + pos.y;
  102. 102             }
  103. 103             else if (pos.x > 90f)
  104. 104             {
  105. 105                 return 180f + (90f - pos.y);
  106. 106             }
  107. 107         }
  108. 108
  109. 109         return pos.x;
  110. 110     }
  111. 111
  112. 112     /// <summary>
  113. 113     /// 摇杆移动信息
  114. 114     /// </summary>
  115. 115     /// <param name="isMove"></param>
  116. 116     /// <param name="angle">当前摇杆角度 取值范围0~360 -1为未操作</param>
  117. 117     void OnRockerMoveInfo(bool isMove, float angle, MovingJoystick move)
  118. 118     {
  119. 119         if (rockerMoveCallback != null)
  120. 120         {
  121. 121             rockerMoveCallback(isMove, angle, move);
  122. 122         }
  123. 123     }
  124. 124 }
复制代码

  RockerController挂到MoveJoystick上

  新建一个脚本:FollowCamera并复制以下代码

  1. 1 #region HeadComments
  2. 2 /* ========================================================================
  3. 3 * Copyright (C) 2016 ArthunGame
  4. 4 *
  5. 5 * 作    者:Arthun
  6. 6 * 文件名称:FollowCamera
  7. 7 * 功    能:跟随摄像机
  8. 8 * 创建时间:2016/03/11 19:38:35
  9. 9 *
  10. 10 * =========================================================================
  11. 11 */
  12. 12 #endregion
  13. 13
  14. 14 using UnityEngine;
  15. 15
  16. 16 public class FollowCamera : MonoBehaviour
  17. 17 {
  18. 18     static FollowCamera _instance;
  19. 19     public static FollowCamera GetInstance()
  20. 20     {
  21. 21         return _instance;
  22. 22     }
  23. 23
  24. 24     public Transform Target;
  25. 25     public float Distance = 10f;
  26. 26     public float Height = 20f;
  27. 27     public float HeightDamping = 2f;
  28. 28
  29. 29     void Awake()
  30. 30     {
  31. 31         if (_instance != null)
  32. 32             Debug.LogError("Instance FollowCamera x2");
  33. 33
  34. 34         _instance = this;
  35. 35     }
  36. 36
  37. 37     void LateUpdate()
  38. 38     {
  39. 39         float currentHeight = Mathf.Lerp(transform.position.y, Target.position.y + Height, HeightDamping * Time.deltaTime);
  40. 40
  41. 41         transform.position = Target.position;
  42. 42         transform.position -= Vector3.forward * Distance;
  43. 43         transform.position = new Vector3(transform.position.x, currentHeight, transform.position.z);
  44. 44
  45. 45         transform.LookAt(Target);
  46. 46     }
  47. 47
  48. 48     public void SetTarget(Transform target)
  49. 49     {
  50. 50         Target = target;
  51. 51     }
  52. 52
  53. 53     public void SetParame(float dis, float height, float heightDamp)
  54. 54     {
  55. 55         Distance = dis;
  56. 56         Height = height;
  57. 57         HeightDamping = heightDamp;
  58. 58     }
  59. 59 }
复制代码

  将MainCamera上的SmoothFollowA脚本删除,替换为刚才新建的FollowCamera脚本。

  打开CarController脚本,添加几个变量。

  1. 1     public float EngineTorque = 1500f;//引擎扭矩
  2. 2     public WheelCollider WheelBL;//后左轮
  3. 3     public WheelCollider WheelBR;//后右轮
  4. 4     public Vector3 CenterOfMass = Vector3.zero;//质量中心
  5. 5
  6. 6     bool mRockerIsMove = false;//摇杆是否移动
  7. 7     Vector3 mTargetBodyAngle = Vector3.zero;//目标车身角度
  8. 8     float mBodyRotateSpeed = 3f;//旋转速度
复制代码

  在Start中设置赛车质量中心(越低越不容易翻车)和摇杆回调。

  1. 1     void Start()
  2. 2     {
  3. 3         rigidbody.centerOfMass = CenterOfMass;
  4. 4         RockerController.Instance.rockerMoveCallback += rockerMoveCallback;
  5. 5
  6. 6         //获取路标点数据
  7. 7         _WaypointsXML.GetXmlData(WaypointsModelAll, null, waypointsData.text);
  8. 8
  9. 9         Debug.Log("赛道总长度:" + CalcTotalDis().ToString());//计算赛道长度
  10. 10     }
  11. 11
  12. 12     /// <summary>
  13. 13     /// 摇杆移动回调
  14. 14     /// </summary>
  15. 15     /// <param name="isMove"></param>
  16. 16     /// <param name="angle"></param>
  17. 17     /// <param name="move"></param>
  18. 18     void rockerMoveCallback(bool isMove, float angle, MovingJoystick move)
  19. 19     {
  20. 20         if (move.joystickName != "MoveJoystick")
  21. 21             return;
  22. 22
  23. 23         mRockerIsMove = isMove;
  24. 24         mTargetBodyAngle.y = move.Axis2Angle(true);
  25. 25     }
复制代码

  最后,在FixedUpdate中加入三句代码。

  1. 1         if (mRockerIsMove)
  2. 2         {
  3. 3             transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(mTargetBodyAngle), Time.fixedDeltaTime * mBodyRotateSpeed);
  4. 4         }
  5. 5
  6. 6         WheelBL.motorTorque = WheelBR.motorTorque = -EngineTorque * (mRockerIsMove ? 0.7f : 0f) * 5000f * Time.fixedDeltaTime;
复制代码

  看到这里,是不是吓了一跳,这漂移也太简单了吧!一句代码搞定漂移、一句代码搞定动力。

  原理是根据摇杆旋转的角度车身也相对应的旋转,加上动力有惯性,就产生圆弧形的漂移了。

  思路就是这么简单,也许你认为这实在是太简单得离谱了,可我却花费了一年多的时间才明白。

  现在,你只需要花几分钟看看这篇没什么高深技术的文章,就能知道漂移实现的思路。

  运行跑一下,有漂移效果,但是似乎不怎么明显,不够顺是不是?

  解决方法很简单,在后面两个车轮处加一个向前推的力,在加一个目标方向侧推的力,就能每次都画出完美的漂移弧度。

  文章标题叫《三分钟实现简单的赛车漂移》,写的过程中才发现三分钟更本做不完。

  因为我自己在做的时候,很多代码都是之前写好了的,直接实现漂移部分的代码就可以了,所以感觉三分钟应该够了,就几句代码。

  好吧,我就当一次标题党吧!哈哈哈哈

  相关阅读:使用Unity3D遇到过的所有问题及解决方法(续)

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2024-5-20 06:51

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表