这节讲讲类似马里奥,盗贼遗产,这些类似的游戏人物基本的控制方式。这里我用Prime31这个公司的插件改了一个比较小的控制脚本,博客结尾会给大家一份源码,先简单的讲一下2D人物控制器的原理,2D控制器可不像untiy 3D控制器那样,untiy官方帮我们封装了。所以这里就只能自己去写了,先贴出一张图。

图中的场景时源插件中的场景,我就直接拿来用免得自己去摆了,图中有3中不同的障碍物,方块代表玩家,1号障碍物是无法穿过的,2号可以穿过,3号是一个简单的坡,玩家可以从2好障碍物的下面直接跳上2,如果站在1号障碍物的下面是无法跳上1号障碍物。站在2号障碍物上可以按跳下键直接从2上面跳下,站在1号障碍物上无法从上面跳下。3号是一个长坡,如果坡的陡峭程度超过了玩家的最大爬坡角度的话玩家是不能爬上去的,有的坡可以像2号障碍物那样可以跳上和跳下。基本的介绍完了,接下来就是具体讲讲怎么实现了,这种控制器的核心就是射线检测。假如玩家受到重力的作用(这里的重力我们用一个常量来模拟,不用刚体自带的重力)开始往下落它遇到地面的时候会停止下落,那么首先我们要做的就是判断落地,我们在玩家盒子碰撞器的四周添加16个射线发射点,每个边有4个发射点,当我们速度y方向的值为正时表示我们在向上运动,反之在向下运动,为0就表示不动了,当y<0时,我们速度为velocity,velocity在y方向的分量为velocity.y,那么玩家下帧的偏移量我们定义为delta=velocity*Time.delatime;我们在玩家碰撞器的下边依次均匀分布在这条边上(考虑到碰撞器紧贴着其他碰撞器,所以我们的发射点在y轴上减去一点偏移量,我们把这个偏移量定义为skin,即皮肤),发射点问题解决了,接下来就是方向,距离,层的确定问题,如果速度的方向为下,方向就是-vector2.up,距离就是我们下帧的偏移量,层就是default层,1号障碍物的层为default,2号障碍物的层我们定义为OneWayPlatform,当velocity.y>0表示玩家正在向上运动,这是我们射线检测的点就是碰撞器向上的那个边了,四个发射点的位置在y轴上的分量同样依次减去skin偏移量,射线的方向为vector2.up,射线的距离为当前帧的偏移量,层我们忽略OneWayPlatform,当velocity.x>0时表示玩家往右移动,同时发射点取右边碰撞器的边,同样也需要同时减去skin,射线的方向为vector2.right,距离为当前帧的偏移量,层为default层。velocity.x<0以相反的情况推理就行了,代码如下:

using UnityEngine;
namespace Assets.LiuDoubi
{[RequireComponent(typeof(BoxCollider2D), typeof(Rigidbody2D))]public class SampleCharacterCtr2D : MonoBehaviour{public struct CharacterColState2D{public bool Right;public bool Left;public bool Top;public bool Bottom;public bool IsColision{get { return Right || Left || Top || Bottom; }}public void Reset(){Right = Left = Top = Bottom = false;}}[SerializeField]private float _skin = 0.1f;//皮肤的厚度,一般定义0.001-0.1,建议在0.00级别。[SerializeField]private int _hraycount;//在x轴方向射线个数,我们定义为4个,同时y轴方向的射线个数也是4个[SerializeField]private int _vraycount;[SerializeField]private LayerMask _obstacleMask;//1号障碍物的层标识,default的层数字为1,所在的实际层为2<<层数[SerializeField]private LayerMask _onewayPlatforms;//2号障碍物的层标识[SerializeField]private float _slopeLimit;//玩家所能爬的最陡坡度public AnimationCurve slopeSpeedMultiplier = new AnimationCurve(new Keyframe(0f, 1f), new Keyframe(90f, 0f));private BoxCollider2D _boxCollider2D;private CharacterColState2D _colState2D;//碰撞信息,分别存储了4个方向的碰撞信息,分别为top,bottom,left,rightprivate Vector3 _bottomright;//底边射点的右起始点,后面的点加上一点的偏移量即可求得,下面依次类推private Vector3 _bottomleft;private Vector3 _topright;private Vector3 _topleft;private LayerMask _curMask;//最后玩家实际需要检测的层public Vector3 Velocity;//玩家的速度public bool IgnoreOneWayPlatforms;//表示是否忽略OneWayPlatformsprivate bool _lastFramIsGround;public bool IsGround { get { return _colState2D.Bottom; } }private void Awake(){_boxCollider2D = this.transform.GetComponent<BoxCollider2D>();//先缓存一下玩家的盒子碰撞器GetRayOffset();}public void Move(Vector3 delta)//该方法为该组件对外的方法。同时也是主要方法。如果当前帧没碰到碰撞器,那下帧情况我们不是很清楚,所以我们的做法就是每帧都去检测,同样每帧的碰撞信息都应该清除{_curMask = _obstacleMask;_lastFramIsGround = IsGround;//判断上一帧玩家是不是在地上,因为没帧都在检测,所以我们需要保存上一帧的玩家碰撞信息。_colState2D.Reset();//碰撞信息重新清除RecalculateRayOrigin();//重新计算射线其实点,每帧玩家的位置信息都在发生变化,所以每帧都需重新计算。if (Mathf.Abs(delta.x) > 0.001f)//如果在x方向偏移不为0,对其进行计算,如果y轴上的偏移不为0同样也需对其进行计算。这2个方法的作用就是返回一个偏移通过delta传出去,ref 和 out表示值类型的参数按引用传递,本质都是传的地址。区别我就不说了。MoveHorizontal(ref delta);if (Mathf.Abs(delta.y) > 0.001f)MoveVertical(ref delta);delta.z = 0;this.transform.Translate(delta, Space.World);if (Time.deltaTime > 0f)Velocity = delta / Time.deltaTime;IgnoreOneWayPlatforms = false;}private void MoveHorizontal(ref Vector3 delta){bool isrgiht = delta.x > 0;//判断是不是向右Vector2 raydir = isrgiht ? Vector2.right : -Vector2.right;//如果向右的话,那么射线的方向就是向右的。Vector2 rayorigin = isrgiht ? _bottomright : _bottomleft;//如果向右的话,起始点我们从右下边,反之就是左下边取。float rayDistance = Mathf.Abs(delta.x) + _skin;射线的方向是偏移量在x轴上的分量加上皮肤的厚度。for (int i = 0; i < _hraycount; i++){var originpos = new Vector2(rayorigin.x, rayorigin.y + i * _vrayoffset);//依次求出射点的位置。RaycastHit2D hit;hit = Physics2D.Raycast(originpos, raydir, rayDistance, _obstacleMask);if (hit){if (i == 0 && HandleHorizontalSlope(ref delta, Vector2.Angle(hit.normal, Vector2.up)))//如果玩家是在爬坡的话,并且只有0号射点才能检测到障碍物的话,那么我们不用计算下面的了。{break;}delta.x = hit.point.x - originpos.x;if (isrgiht)//如果检测到了障碍物的话,并且是在右边话,表示玩家的朝向有一个障碍物{delta.x -= _skin;_colState2D.Right = true;}else{delta.x += _skin;_colState2D.Left = true;}break;}}}//该方法的作用是,假如我们离障碍物有0.1,如果我们下一帧移动偏移量为0.2的话,如果我们还是按照0.2进行移动话,那么玩家就和障碍物重叠了,如果这时我们发现重叠了,然后又把玩家移动-0.1的偏移的话,在短暂的时间内,一般玩家是可以看出来的,所以我们的做法应该在这一帧只移动0.1.。同时下面的方法都是一样的,只有上坡的时候检测可能有点区别。private void MoveVertical(ref Vector3 delta){bool isdown = delta.y < 0;Vector2 raydir = isdown ? -Vector2.up : Vector2.up;Vector2 rayorigin = isdown ? _bottomleft : _topleft;float rayDistance = Mathf.Abs(delta.y) + _skin;if (isdown && !_lastFramIsGround)_curMask |= _onewayPlatforms;if (_lastFramIsGround && IgnoreOneWayPlatforms)_curMask &= ~_onewayPlatforms;for (int i = 0; i < _vraycount; i++){var originpos = new Vector2(rayorigin.x + i * _hrayoffset, rayorigin.y);RaycastHit2D hit;hit = Physics2D.Raycast(originpos, raydir, rayDistance, _curMask);if (hit){delta.y = hit.point.y - originpos.y;if (isdown){delta.y += _skin;_colState2D.Bottom = true;}else{delta.y -= _skin;_colState2D.Top = true;}break;}}}private bool HandleHorizontalSlope(ref Vector3 delta, float angle){if (Mathf.RoundToInt(angle) == 90)return false;if (angle < _slopeLimit){if (delta.y < 0.05f){var slopeModifier = slopeSpeedMultiplier.Evaluate(angle);delta.x *= slopeModifier;delta.y = Mathf.Abs(Mathf.Tan(angle * Mathf.Deg2Rad) * delta.x);var isGoingRight = delta.x > 0;var ray = isGoingRight ? _bottomright : _bottomleft;RaycastHit2D raycastHit;//if (_lastFramIsGround)raycastHit = Physics2D.Raycast(ray, delta.normalized, delta.magnitude, _curMask);if (raycastHit){delta = (Vector3)raycastHit.point - ray;if (isGoingRight)delta.x -= _skin;elsedelta.x += _skin;}//_isGoingUpSlope = true;_colState2D.Bottom = true;}}return true;}private void RecalculateRayOrigin()//重新计算射线的各个位置的起始位置{float xoffset = _boxCollider2D.size.x * this.transform.localScale.x / 2;float yoofset = _boxCollider2D.size.y * this.transform.localScale.y / 2;_topleft = transform.position + new Vector3(-xoffset + _skin, yoofset - _skin, 0);_bottomleft = transform.position + new Vector3(-xoffset + _skin, -yoofset + _skin, 0);_topright = transform.position + new Vector3(xoffset + -_skin, yoofset - _skin, 0);_bottomright = transform.position + new Vector3(xoffset + -_skin, -yoofset + _skin, 0);}private float _hrayoffset;private float _vrayoffset;private void GetRayOffset()//计算每边射点与射点之间的距离。{var xdis = _boxCollider2D.size.x * this.transform.localScale.x - 2 * _skin;var ydis = _boxCollider2D.size.y * this.transform.localScale.y - 2 * _skin;_hrayoffset = xdis / _hraycount;_vrayoffset = ydis / _vraycount;}}
}

这样讲的好麻烦了,总之一句话,move方法中 MoveHorizontal(ref delta); MoveVertical(ref delta);就是计算玩家下一帧需要移动的偏移量。这里给出了2套控制代码,一套是Prime31,另外一套是自己写,但是大家可以先学习prime31这个插件。然后去写自己角色控制器,我写这些只是帮助大家理解角色控制器。http://pan.baidu.com/s/1hrGNgiS
如果有什么不懂的可以私聊,qq:1850761495

untiy 2D角色控制器相关推荐

  1. Unity 2D角色控制器(横板滚轴)

    unity自带的2D刚体在坡上会往下滑.而且也是会有可能穿墙,或碰撞到其他物体时鬼畜抖动. 设计思路 1.利用射线进行判断碰撞防止穿墙: 原理传送门 2.在地面时往地面发射两条射线,获得的两个点,两个 ...

  2. Unity 2D动画控制器详解

    http://www.tairan.com/archives/6939#16 文章目录 开始 过渡 编辑过渡 颜色变化 动画参数 复习一下 CatConga动画 CatDisappear Clip A ...

  3. 控制元素聚焦_聚焦资产商店角色控制器

    控制元素聚焦 Friendly greetings, Unity developers! 友好的问候,Unity开发人员! 我想欢迎您参加每两周一次的系列社论的第一部分,其中展示了Unity Asse ...

  4. Unity自定义角色控制器(一):碰撞检测

    我实在很喜欢用Unity.它在处理很多底层问题的同时有给了开发者很多自由.而且它还有着非常活跃的社区,起到了很大的帮助. 不幸的是,之前也说了,Unity也带着世界上最糟糕的角色控制器.在与Unity ...

  5. Unity超级角色控制器研究(四)——地形检测

    一个地形检测反例 因此我们想知道什么在角色脚底下呢?首先是距离脚下有多远.我们会想知道角色的脚是否贴着地面还是在半空中.我们还会想知道脚下地面具体位置坐标,这对于上一章强调过的钳住地面是很重要的.第三 ...

  6. Unity手游之路lt;七gt;角色控制器

    我们要控制角色的移动,能够所有细节都由自己来实现.控制角色模型的移动,同一时候移动摄影机,改变视角.当然Unity也提供了一些组件,能够让我们做更少的工作,实现我们所期望的功能.今天我们就一起系统来学 ...

  7. unity3d人物跳_Unity3D研究院之角色控制器组件研究(二十二)

    Unity3D封装了一个非常好用的组件来实现第一人称视角与第三人称视角游戏开发,我们称他为角色控制器组件,几乎不用写一行代码就可以完成一切的操作,神奇吧.使用它的方法如下,首先打开Unity游戏引擎编 ...

  8. Unity3D基础38:角色控制器组件

    前文:https://blog.csdn.net/Jaihk662/article/details/87906156(Input控制面板) 一.CharacterController角色控制器 为什么 ...

  9. unity 2d角色移动卡住的原因

    unity 2d角色移动时卡住原地踏步的原因 可能是碰撞盒子是正方形 被地图的碰撞体卡住的原因 应该把脚底做成圆形的碰撞盒 防止和地面的碰撞盒子卡住

最新文章

  1. android在线切图工具,9Cut切图工具
  2. H3C设备之RIP v2认证
  3. 全球及中国生物质能利用产业十四五发展目标及前景容量预测报告2021-2027年
  4. 【知识图谱系列】人工智能经典图谱有哪些?
  5. Upsync:微博开源基于Nginx容器动态流量管理方案
  6. 点击跳转到QQ聊天界面
  7. 补习系列(15)-springboot 分布式会话原理
  8. TensorFlow实现深度学习算法的教程汇集:代码+笔记
  9. java stack 实现_Swift Stack实现
  10. 音创点歌机_音创点歌系统_音创KTV点歌系统下载- 下载之家
  11. 联想计算机型号吧,lenovo全系列联想笔记本电脑型号对照表
  12. QCustomplot 实现鼠标追踪定位线以及坐标
  13. 迪普融合之路 构建价值网络
  14. 88年的世界杯历史,用Python带你回顾!
  15. 禅道怎么启动mysql_禅道启动mysql报错connectByPDO
  16. 《清华园日记》读后感
  17. 开题报告、文献综述、外文翻译、论文反抄袭软件、论文目录,就差论文正文了,其他都全了!!
  18. Java之ByteBuffer详解
  19. ats2851 / cm591 ugreen bluetooth 5.3 for Linux
  20. 数学速算法_三年级数学时分秒换算口诀+精选思维奥数题整理汇总

热门文章

  1. 唧唧歪歪之OpenGL坐标系
  2. 每日一学-- 主动学习(active learning)
  3. android textview图片,Android如何在TextView中显示图片
  4. usaco The Tamworth Two
  5. 技工学校 计算机专业 课程,浅谈技工学校非计算机专业《计算机应用基础》课程教学...
  6. 记忆力很差怎么学计算机,年轻人增强记忆力五大的技巧
  7. 粉笔网iPhone端使用的第三方开源库
  8. java 格式化时间 小时_java格式化时间示例
  9. python给图片加滤镜的方程_纯Python综合图像处理小工具(4)自定义像素级处理(剪纸滤镜)...
  10. 程序员如何赚钱(转)