Unity 2021.3 + Sunny Land素材包:手把手教你搞定2D角色移动、跳跃与转向(附完整C#脚本)

张开发
2026/6/6 4:17:56 15 分钟阅读

分享文章

Unity 2021.3 + Sunny Land素材包:手把手教你搞定2D角色移动、跳跃与转向(附完整C#脚本)
Unity 2021.3与Sunny Land素材包打造丝滑2D角色控制的终极指南当你第一次打开Sunny Land素材包时那只呆萌的小狐狸角色是否让你眼前一亮但很快你会发现要让这个可爱的角色在2D世界中流畅移动、跳跃和转向远不是拖拽几个组件那么简单。本文将带你深入Unity 2021.3的2D物理系统核心用专业且易懂的方式实现角色控制同时规避那些新手常踩的坑。1. 环境准备与项目配置在开始编码之前正确的项目设置能避免80%的后续问题。使用Unity 2021.3 LTS版本至关重要——这个长期支持版修复了大量2D物理相关的bug特别是Rigidbody 2D在移动平台上的表现。必须安装的组件2D Sprite2D PhysicsUniversal RP用于后期特效扩展创建URP项目后从Package Manager导入Sunny Land素材包时注意勾选以下关键资源Graphics/Player文件夹中的角色精灵图Physics Materials中的预设材质Scenes/Starter_Scene初始场景提示如果素材导入后出现粉色材质需在URP Asset中勾选2D Renderer选项设置像素完美显示的关键参数// 在任意脚本的Start方法中添加 void Start() { Camera.main.orthographicSize 5f; // 适配Sunny Land的推荐值 QualitySettings.vSyncCount 1; // 防止画面撕裂 }2. 物理组件的科学配置角色的物理表现90%取决于初始组件配置。删除默认的Box Collider 2D改用更适合平台游戏的Capsule Collider 2D组件关键参数推荐值作用Rigidbody 2DGravity Scale3适合平台游戏的重力Collision DetectionContinuous防止高速穿墙Freeze RotationZ轴勾选避免角色翻滚Capsule Collider 2DSize(0.5,1.2)匹配狐狸形状Offset(0,-0.1)底部对齐脚部创建专用Physics Material 2D并命名为PlayerPhysicsPhysicsMaterial2D material new PhysicsMaterial2D(); material.friction 0; material.bounciness 0.1f; GetComponentCapsuleCollider2D().sharedMaterial material;3. 移动控制超越基础的实现方案传统的velocity直接赋值方式会导致移动生硬。我们采用力驱动速度限制的方案[Header(Movement)] [SerializeField] float moveSpeed 8f; [SerializeField] float acceleration 15f; [SerializeField] float deceleration 20f; void UpdateMovement() { float targetSpeed Input.GetAxisRaw(Horizontal) * moveSpeed; float speedDiff targetSpeed - rb.velocity.x; float accelRate Mathf.Abs(targetSpeed) 0.01f ? acceleration : deceleration; float movement speedDiff * accelRate; rb.AddForce(movement * Vector2.right); // 速度限制 if(Mathf.Abs(rb.velocity.x) moveSpeed) { rb.velocity new Vector2(Mathf.Sign(rb.velocity.x) * moveSpeed, rb.velocity.y); } }移动优化的三个关键点使用AddForce而非直接设置velocity实现加速度效果分离加速和减速参数让停止更自然最终速度限制避免惯性过大4. 跳跃系统的专业级实现基础跳跃只需几行代码但专业级的跳跃需要实现可变高度跳跃按得越久跳得越高土狼时间Coyote Time跳跃缓冲Jump Buffer[Header(Jump)] [SerializeField] float jumpForce 13f; [SerializeField] float jumpTime 0.35f; [SerializeField] float coyoteTime 0.15f; [SerializeField] float jumpBufferTime 0.2f; float jumpCounter; float coyoteCounter; float jumpBufferCounter; bool isJumping; void UpdateJump() { // 土狼时间 if(CheckGrounded()) { coyoteCounter coyoteTime; } else { coyoteCounter - Time.deltaTime; } // 跳跃缓冲 if(Input.GetButtonDown(Jump)) { jumpBufferCounter jumpBufferTime; } else { jumpBufferCounter - Time.deltaTime; } // 执行跳跃 if(jumpBufferCounter 0 coyoteCounter 0) { rb.velocity new Vector2(rb.velocity.x, jumpForce); jumpBufferCounter 0; coyoteCounter 0; isJumping true; jumpCounter jumpTime; } // 可变高度 if(Input.GetButton(Jump) isJumping) { if(jumpCounter 0) { rb.velocity new Vector2(rb.velocity.x, jumpForce); jumpCounter - Time.deltaTime; } else { isJumping false; } } if(Input.GetButtonUp(Jump)) { isJumping false; } } bool CheckGrounded() { return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer); }5. 角色转向与动画衔接转向不仅是翻转精灵图还需考虑转向时的速度平滑过渡与动画状态的同步粒子效果方向适配[Header(Visual)] [SerializeField] float turnSmoothTime 0.1f; float turnSmoothVelocity; void UpdateVisuals() { // 平滑转向 float targetDirection Mathf.Sign(rb.velocity.x); if(Mathf.Abs(rb.velocity.x) 0.1f) { float currentDirection Mathf.Lerp( transform.localScale.x, targetDirection, turnSmoothTime * Time.deltaTime * 10f); transform.localScale new Vector3( currentDirection, 1, 1); } // 动画参数 animator.SetFloat(Speed, Mathf.Abs(rb.velocity.x)); animator.SetBool(IsGrounded, CheckGrounded()); animator.SetFloat(VerticalVelocity, rb.velocity.y); }转向优化的三个技巧使用Lerp平滑过渡避免瞬间翻转只在速度达到阈值时触发转向将方向存储在localScale而非spriteRenderer.flipX方便粒子系统同步6. 高级技巧解决常见物理问题问题1角色卡在斜坡解决方案添加边缘碰撞检测[SerializeField] float slopeCheckDistance 0.5f; [SerializeField] float maxSlopeAngle 45f; RaycastHit2D slopeHit; bool OnSlope() { slopeHit Physics2D.Raycast( transform.position, Vector2.down, slopeCheckDistance, groundLayer); if(slopeHit) { float angle Vector2.Angle(slopeHit.normal, Vector2.up); return angle maxSlopeAngle angle ! 0; } return false; }问题2平台穿透解决方案使用Platform Effector 2Dvoid HandlePlatformPassthrough() { if(Input.GetAxisRaw(Vertical) -0.1f) { foreach(var platform in FindObjectsOfTypePlatformEffector2D()) { StartCoroutine(DisableCollision(platform)); } } } IEnumerator DisableCollision(PlatformEffector2D platform) { var collider platform.GetComponentCollider2D(); var playerCollider GetComponentCollider2D(); Physics2D.IgnoreCollision(playerCollider, collider, true); yield return new WaitForSeconds(0.5f); Physics2D.IgnoreCollision(playerCollider, collider, false); }7. 性能优化与调试技巧移动端优化策略将物理更新模式改为FixedUpdate降低Rigidbody 2D的sleep阈值void Start() { rb.sleepMode RigidbodySleepMode2D.NeverSleep; rb.collisionDetectionMode CollisionDetectionMode2D.Continuous; }调试可视化工具void OnDrawGizmos() { // 地面检测可视化 Gizmos.color Color.green; Gizmos.DrawWireSphere(groundCheck.position, 0.2f); // 斜坡检测可视化 Gizmos.color Color.blue; Gizmos.DrawLine(transform.position, transform.position Vector3.down * slopeCheckDistance); }性能分析关键指标Physics2D.CollisionsPerFrame控制在50Rigidbody2D.AwakeCount场景中不超过20个

更多文章