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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class PlayerMovement : MonoBehaviour { // 通过 “Header”, “Tooltip” and “Space” 属性来组织Inspector中的属性显示,即在Inspector窗口上有小标题分区显示 // rigidbody,collider private Rigidbody2D rb; private BoxCollider2D coll; // 使用BoxCollider可以直接获得其中的size、offset参数 [Header("移动参数")] public float speed = 8f; public float crouchSpeedDivisor = 3f; // 蹲下时的速度减缓量
[Header("跳跃参数")] public float jumpForce = 6.3f; // 跳跃力 public float jumpHoldForce = 1.9f; // 长按跳跃的力 public float jumpHoldDuration = 0.1f; // 长按跳跃加成 public float crouchJumpBoost = 2.5f; // 下蹲跳跃的加成 public float hangingJumpForce = 15f; // 悬挂时跳跃的力
[Header("状态")] public bool isCrouch; // 是否下蹲 public bool isOnGround; // 是否接触地面 public bool isHeadBlocked; // 是否头顶遮挡 public bool isJump; // 是否跳跃 public bool isHanging; // 是否悬挂
[Header("环境检测")] public LayerMask groundLayer; // 地面层 public float headClearance = 0.25f; public float groundDistance = 0.2f; public float grabDistance = 0.4f; // 抓取距离 public float reachOffset = 0.7f; // 接触偏移值
float footOffset; // 单脚的偏移值,即整体xsize的一半 float xVelocity; // 轴加速度 float jumpTime; // 跳跃时间,与duration相关 float playerHeight; // 角色高度 float eyeHeight; // 角色眼睛高度
// 按键 bool jumpPressed, jumpHeld, crouchHeld, crouchPressed;
// 碰撞体尺寸 (站立、下蹲时的大小、位置) Vector2 colliderStandSize, colliderStandOffset,colliderCrouchSize, colliderCrouchOffset;
// 初始化参数 void Start() { // rigidbody,collider rb = GetComponent<Rigidbody2D>(); coll = GetComponent<BoxCollider2D>();
footOffset = coll.size.x / 2; playerHeight = coll.size.y; eyeHeight = playerHeight - 0.4f;
// 碰撞体尺寸 colliderStandSize = coll.size; colliderStandOffset = coll.offset; colliderCrouchSize = new Vector2(coll.size.x, coll.size.y / 2f); colliderCrouchOffset = new Vector2(coll.offset.x, coll.offset.y / 2f); }
// 获取GetButton最好放在此函数中 void Update() { // GetButton参数对应Project Setting -> Input Manager的设置 jumpPressed = Input.GetButtonDown("Jump"); jumpHeld = Input.GetButton("Jump"); crouchHeld = Input.GetButton("Crouch"); crouchPressed = Input.GetButtonDown("Crouch");
playerHeight = coll.size.y; eyeHeight = playerHeight - 0.4f; }
// 固定帧速率下每帧都调用该函数 private void FixedUpdate() { // 相关物理控制 GroundMovement(); PhysicsCheck(); MidAirMovement(); CrouchMovement(); HangdingMovement(); }
// 环境检测 void PhysicsCheck() { /* // 当前角色的位置和单脚左右点的offset Vector2 pos = transform.position; Vector2 offset = new Vector2(-footOffset, 0f); // leftCheck/rightCheck即单脚左右点检测 RaycastHit2D leftCheck = Physics2D.Raycast(pos+offset,Vector2.down,groundDistance,groundLayer); // 显示射线 Debug.DrawRay(pos + offset, Vector2.down, Color.red, 0.2f); */ // 1、左右脚射线检测 是否接触地面 // 获得射线检测 RaycastHit2D leftCheck = Raycast(new Vector2(-footOffset, 0f), Vector2.down, groundDistance, groundLayer); RaycastHit2D rightCheck = Raycast(new Vector2(footOffset, 0f), Vector2.down, groundDistance, groundLayer);
// 是否接触地面层 //if (coll.IsTouchingLayers(groundLayer)) if (leftCheck || rightCheck) isOnGround = true; else isOnGround = false;
// 2、头顶射线检测 是否有阻挡 RaycastHit2D headCheck = Raycast(new Vector2(0f, playerHeight), Vector2.up, headClearance, groundLayer); if (headCheck) isHeadBlocked = true; else isHeadBlocked = false;
// 3、头前方区域射线检测 是否悬挂 float diraction = transform.localScale.x; RaycastHit2D blockedCheck = Raycast(new Vector2(footOffset * diraction, playerHeight), new Vector2(diraction,0f), grabDistance, groundLayer); RaycastHit2D wallCheck = Raycast(new Vector2(footOffset * diraction, eyeHeight), new Vector2(diraction, 0f), grabDistance, groundLayer); RaycastHit2D ledgeCheck = Raycast(new Vector2(reachOffset * diraction, playerHeight), Vector2.down, grabDistance, groundLayer);
if (!isOnGround && rb.velocity.y < 0f && ledgeCheck && wallCheck && !blockedCheck) { // 将player固定在一个位置 Vector3 pos = transform.position; pos.x += (wallCheck.distance - 0.05f) * diraction; // RaycastHit2D下的distance为起始点到接触点的距离 pos.y -= ledgeCheck.distance; transform.position = pos;
rb.bodyType = RigidbodyType2D.Static; // 让角色静止 isHanging = true; }
}
// 控制角色左右移动 void GroundMovement() { // 悬挂时不允许移动 if (isHanging) return;
// 获取键盘输入 xVelocity = Input.GetAxis("Horizontal"); // 返回值-1f ~ 1f,当不按下方向键时为0 // 控制角色速度以移动 rb.velocity = new Vector2(xVelocity * speed, rb.velocity.y); // 控制朝向 FlipDiraction(); }
// 控制角色下蹲起立 void CrouchMovement() { if (crouchHeld && !isCrouch && isOnGround) { // 按下下蹲按钮,不处于下蹲状态,接触地面 --》 角色下蹲 Crouch(); } else if (!crouchHeld && isCrouch && !isHeadBlocked) { // 未按下按钮,处于下蹲状态,头顶无遮挡 --》 角色起身 StandUp(); } else if (!isOnGround && isCrouch) { // 不接触地面但是处于下蹲状态 ——》 处于空中 --》 角色起身 StandUp(); } }
// 控制角色跳跃 void MidAirMovement() { // 跳跃检测 if(jumpPressed && isOnGround && !isJump && !isHeadBlocked) { // isOnGround && !isJump 防止蹭墙无限跳 // 按下了跳跃,接触地面,未处于跳跃状态 ——》 控制跳跃
// 下蹲状态跳跃,有额外力的加成 if (isCrouch && !isHeadBlocked) { // 控制起身 StandUp(); // 添加额外力 rb.AddForce(new Vector2(0f, crouchJumpBoost), ForceMode2D.Impulse); }
isJump = true; isOnGround = false;
// 计算跳跃时的时间,Time.time获取游戏真实持续时间(不断增长) jumpTime = Time.time + jumpHoldDuration;
// 修改rb.velocity也可以实现 rb.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse); } else if (isJump) { // 长按跳跃,获得额外的力jumpHoldForce加成 if (jumpHeld) { rb.AddForce(new Vector2(0f, jumpHoldForce), ForceMode2D.Impulse); } // 当Time.time获取的时间比jumpTime大,即之间的时间差已经大于jumpHoldDuration的值时,即可恢复isJump if (jumpTime < Time.time) { isJump = false; } } }
// 控制角色悬挂时跳跃与下蹲 void HangdingMovement() { // 悬挂时检测 if (isHanging) { if (jumpPressed) { rb.bodyType = RigidbodyType2D.Dynamic; rb.AddForce(new Vector2(0f, hangingJumpForce), ForceMode2D.Impulse); isHanging = false; } else if (crouchPressed) { rb.bodyType = RigidbodyType2D.Dynamic; isHanging = false; } } }
// 控制角色面向方向 void FlipDiraction() { if(xVelocity < 0) { transform.localScale = new Vector2(-1, 1); }else if(xVelocity > 0) { transform.localScale = new Vector2(1, 1); } }
// 控制角色下蹲 void Crouch() { isCrouch = true; // 下蹲时速度减缓 xVelocity /= crouchSpeedDivisor;
// 改变collider的尺寸 coll.size = colliderCrouchSize; coll.offset = colliderCrouchOffset; }
// 控制角色起立 void StandUp() { isCrouch = false;
// 改变collider的尺寸 coll.size = colliderStandSize; coll.offset = colliderStandOffset; }
// 增强Raycast函数 RaycastHit2D Raycast(Vector2 offset, Vector2 rayDirection, float length, LayerMask layer) { // 当前角色的位置 Vector2 pos = transform.position; // 创建射线 RaycastHit2D hit = Physics2D.Raycast(pos + offset, rayDirection, length, layer);
// 显示射线,hit返回是否碰撞布尔值 Color color = hit ? Color.red : Color.green; Debug.DrawRay(pos + offset, rayDirection, color);
return hit; }
}
|