前言   为了便于测试,我们会先使用PC端的键盘和鼠标输入
来控制人物的移动,等到功能测试完成之后,再将PC端的键盘和鼠标输入
换成移动端的虚拟摇杆和按钮输入
。这里,我们首先使用PC端的键盘和鼠标输入
来实现控制角色进行移动的功能。
为角色添加Collider和Rigidbody   为了让角色具有物理属性,我们需要为角色添加Collider和Rigidbody。此外,为了避免角色出现翻滚的问题,让角色一直保持直立,因此我们需要在Rigidbody2D
的Constraints
属性里设置勾选Freeze Rotation Z
,不让角色在进行物理模拟时,绕Z轴进行旋转。
控制角色移动   接下来,我们开始编写脚本来实现让怪物在场景中移动的功能。我们在Assets\Scripts
文件夹下创建一个名为Player
的文件夹用于保存和角色相关的脚本。创建完毕后,我们在Player
文件夹下创建一个名为PlayerController
的C#脚本,然后添加以下代码:
PlayerController.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 using System.Collections;using System.Collections.Generic;using UnityEngine; [RequireComponent(typeof(Rigidbody2D)) ]public class PlayerController : MonoBehaviour { [Tooltip("角色初始朝向是否朝向右边" ) ] public bool FacingRight = true ; [Tooltip("移动时角色加速的力大小" ) ] public float MoveForce = 365f ; [Tooltip("角色移动的最大速度" ) ] public float MaxSpeed = 5f ; [Tooltip("跳跃时向上加速的力大小" ) ] public float JumpForce = 1000f ; [Tooltip("检测角色是否落地" ) ] public Transform GroundCheck; private bool m_IsReadyToJump; private bool m_IsJumping; private bool m_GroundedStatus; private Rigidbody2D m_Rigidbody2D; private void Awake () { m_Rigidbody2D = GetComponent<Rigidbody2D>(); } private void Start () { if (GroundCheck == null ) { Debug.LogError("请先设置GroundCheck" ); } m_IsReadyToJump = false ; m_IsJumping = false ; m_GroundedStatus = false ; } private void Update () { m_GroundedStatus = Physics2D.Linecast( transform.position, GroundCheck.position, LayerMask.GetMask("Obstacle" ) ); if (m_GroundedStatus && !m_IsJumping && Input.GetButtonDown("Jump" )) { m_IsReadyToJump = true ; } if (m_GroundedStatus && m_IsJumping) { m_IsJumping = false ; } } private void FixedUpdate () { float h = Input.GetAxis("Horizontal" ); if (h * m_Rigidbody2D.velocity.x < MaxSpeed) { m_Rigidbody2D.AddForce(Vector2.right * h * MoveForce); } if (Mathf.Abs(m_Rigidbody2D.velocity.x) > MaxSpeed) { m_Rigidbody2D.velocity = new Vector2( Mathf.Sign(m_Rigidbody2D.velocity.x) * MaxSpeed, m_Rigidbody2D.velocity.y ); } if (h > 0 && !FacingRight) { Flip(); }else if (h < 0 && FacingRight) { Flip(); } if (m_IsReadyToJump) { Jump(); } } private void Jump () { m_IsJumping = true ; m_Rigidbody2D.AddForce(new Vector2(0f , JumpForce)); m_IsReadyToJump = false ; } private void Flip () { FacingRight = !FacingRight; this .transform.localScale = Vector3.Scale( new Vector3(-1 , 1 , 1 ), this .transform.localScale ); } }
代码说明:
Input.GetAxis(“Horizontal”):Unity提供的默认输入项,能获取键盘上A\D
键和<-\->
方向键的输入
Input.GetButtonDown(“Jump”):Unity提供的默认输入项,能获取键盘上空格键的输入
Physics2D.Linecast:来获取场景里两个点之间属于某个Layer的所有Collider
因为和角色物理模拟有关的操作都应该在FixedUpdate里执行
,所以Jump函数需要在FixedUpdate里调用
我们为了避免出现多次按跳跃键导致Jump函数被多次调用的问题,我们使用变量m_IsJumping
来判断当前是否处于跳跃状态
添加Physics Material   添加完成后,将PlayerController.cs
添加到Hierarchy
窗口的Player
上,运行游戏,我们发现角色在运动时,会出现很明显的滑动现象。这是因为我们没有为角色和平台上的Collider设置Physics Material
,导致角色和平台之间的摩擦力为0。
添加Physics Material的步骤
在Assets
目录下新建一个名为Physics Material
的文件夹
在Physics Material
的文件夹创建两个Physics Material 2D
,并将它们分别命名为Platform
和Player
将Platform
和Player
这两个Physics Material 2D
的Friction
属性都设置为1
将Platform
和Player
这两个Physics Material 2D
分别设置到所有角色可移动的平台
和角色的Rigidbody2D
上
  因为平台数量较多,我们可以批量操作。按住shift
键选择多个GameObject,右侧Inspector
就会显示它们同时都具备的组件,我们可以同时对它们同时都具备的组件进行编辑。
  此外,角色在跳跃时,我们发现角色跳跃的高度很高,且下落的速度很慢,游戏体验较差。为了提高游戏体验,我们需要增大游戏角色的重力加速度。将Player
下Rigidbody2D
组件的Gravity Scale
设置为3.1
,表示将角色的重力加大至原有的3.1
倍。
  从上面的图片可以看到,我将Player
这一Physics Material 2D
设置到角色的Rigidbody2D
上,这表示角色的所有没设置Physics Material 2D
的Collider都会使用Player
这一Physics Material 2D
。
  设置完成之后,运行游戏,可以看到之前的滑动和下落缓慢的问题都消失了,但是出现了一个新的问题,那就是当我们按住方向键不动的时候,角色会贴在平台边缘。之所以会出现这个问题,是因为角色和平台之间有摩擦力,为了避免这一情况,我们需要创建一个名为Wall
的Physics Material 2D
,将其Friction
属性都设置为0
,然后在各个平台的末端处创建一个使用Wall
这一Physics Material 2D
的Collider。
Foreground添加Collider的情况如下:
env_TowerFull和env_TowerFull (1):
不添加新的Collider
设置原有Collider的Physics Material 2D
为Wall
env_PlatformBridge和env_PlatformBridge (1):
新添加一个BoxCollider2D
Material
:Wall
Offset
:(0.8, 0.8)
Size
: (15.5, 0.6)
env_PlatformTop和env_PlatformTop (1):
新添加一个BoxCollider2D
Material
:Wall
Offset
:(4.7, 0.12)
Size
: (0.5, 2.6)
env_PlatformUfo:
新添加两个CircleCollider2D
Material
:都为Wall
Offset
:(-15.3, -0.65)和(15.3, -0.65)
Radius
: 都为0.5
  添加完毕之后,再次运行游戏,可以看到不再出现角色贴在平台边缘的问题。
添加音效   接着,我们需要为角色添加音效。首先,在PlayerController.cs
内加入以下代码,其中...
表示原有的代码:
PlayerController.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ... [RequireComponent(typeof(AudioSource)) ]public class PlayerController : MonoBehaviour { ... [Tooltip("跳跃音效" ) ] public AudioClip[] JumpClips; ... private AudioSource m_AudioSource; ... private void Awake () { ... m_AudioSource = GetComponent<AudioSource>(); } private void Jump () { ... if (JumpClips.Length > 0 ) { int i = Random.Range(0 , JumpClips.Length); AudioSource.PlayClipAtPoint(JumpClips[i], transform.position); } } }
  添加完毕之后,我们给Player添加AudioSource
组件,并将Assets\Audio\Player\Jumps
添加到JumpClips
属性上。接着运行游戏,可以听到角色在跳跃的时候已经有了音效。最后,将我们在Player
上做的修改Apply
到Prefab上,并保存场景产生的修改即可。
PlayerController.cs完整代码   此时,PlayerController.cs
完整代码如下:
PlayerController.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 using System.Collections;using System.Collections.Generic;using UnityEngine; [RequireComponent(typeof(Rigidbody2D)) ] [RequireComponent(typeof(AudioSource)) ]public class PlayerController : MonoBehaviour { [Tooltip("角色初始朝向是否朝向右边" ) ] public bool FacingRight = true ; [Tooltip("移动时角色加速的力大小" ) ] public float MoveForce = 365f ; [Tooltip("角色移动的最大速度" ) ] public float MaxSpeed = 5f ; [Tooltip("跳跃时向上加速的力大小" ) ] public float JumpForce = 1000f ; [Tooltip("检测角色是否落地" ) ] public Transform GroundCheck; [Tooltip("跳跃音效" ) ] public AudioClip[] JumpClips; private bool m_IsReadyToJump; private bool m_IsJumping; private bool m_GroundedStatus; private Rigidbody2D m_Rigidbody2D; private AudioSource m_AudioSource; private void Awake () { m_Rigidbody2D = GetComponent<Rigidbody2D>(); m_AudioSource = GetComponent<AudioSource>(); } private void Start () { if (GroundCheck == null ) { Debug.LogError("请先设置GroundCheck" ); } m_IsReadyToJump = false ; m_IsJumping = false ; m_GroundedStatus = false ; } private void Update () { m_GroundedStatus = Physics2D.Linecast( transform.position, GroundCheck.position, LayerMask.GetMask("Obstacle" ) ); if (m_GroundedStatus && !m_IsJumping && Input.GetButtonDown("Jump" )) { m_IsReadyToJump = true ; } if (m_GroundedStatus && m_IsJumping) { m_IsJumping = false ; } } private void FixedUpdate () { float h = Input.GetAxis("Horizontal" ); if (h * m_Rigidbody2D.velocity.x < MaxSpeed) { m_Rigidbody2D.AddForce(Vector2.right * h * MoveForce); } if (Mathf.Abs(m_Rigidbody2D.velocity.x) > MaxSpeed) { m_Rigidbody2D.velocity = new Vector2( Mathf.Sign(m_Rigidbody2D.velocity.x) * MaxSpeed, m_Rigidbody2D.velocity.y ); } if (h > 0 && !FacingRight) { Flip(); }else if (h < 0 && FacingRight) { Flip(); } if (m_IsReadyToJump) { Jump(); } } private void Jump () { m_IsJumping = true ; m_Rigidbody2D.AddForce(new Vector2(0f , JumpForce)); m_IsReadyToJump = false ; if (JumpClips.Length > 0 ) { int i = Random.Range(0 , JumpClips.Length); AudioSource.PlayClipAtPoint(JumpClips[i], transform.position); } } private void Flip () { FacingRight = !FacingRight; this .transform.localScale = Vector3.Scale( new Vector3(-1 , 1 , 1 ), this .transform.localScale ); } }
后言   可以看到,目前角色在运动的时候还没有动画。因为给角色加上动画设计到动画状态机的制作,较为复杂,在下一篇文章将会介绍如何给角色添加动画。最后,本篇文章所做的修改,可以在PotatoGloryTutorial 这个仓库的essay5
分支下看到,读者可以clone这个仓库到本地进行查看。
参考链接
Gravity Scale
Conventional Game Input
Physics Material 2D