【Unity3D】实现横版2D游戏角色二段跳、蹬墙跳、扶墙下滑
目录
一、二段跳、蹬墙跳
二、扶墙下滑
一、二段跳、蹬墙跳

GitHub - prime31/CharacterController2D
下载工程后直接打开demo场景:DemoScene(Unity 2019.4.0f1项目环境)
Player物体上的CharacterController2D,Mask添加Wall层(自定义墙体层)


将场景里其中一个障碍物设置为Wall层 Wall标签 并拉伸为墙体高度


Player物体上的Demo Scene脚本控制玩家移动 二段跳 蹬墙跳

蹬墙跳要调整好Jump On Wall H Force 横向力 和 Jump On Wall V Force 纵向力 数值才能表现正常,其中 V Force 是在 1的基础上的增量值,这里的力并非物理力实际是速度增量倍率。
跳跃对Y轴速度影响是用公式:根号2gh
代码则是:Mathf.Sqrt(2f * jumpHeight * -gravity),加速度是重力反方向,跳跃高度固定,则计算出了速度增量,之后用它乘以(1+V Force)得出的一个对Y轴速度影响的增量。
上例子中速度增量根号2gh是8.48,因此每次蹬墙跳Y速度增量是8.48*1.335=11.32
代码默认有重力对Y轴速度影响:_velocity.y += gravity * Time.deltaTime; 即每秒Y轴速度会减去重力加速度(墙上为-24,地面为-25)若帧数是30,则每帧会减少0.8。具体可以将_velocity参数公开查看变化,实际蹬墙跳会离开墙体,重力加速度为-25,可自行调整这些参数来达到理想效果
修改部分代码:
private float rawGravity;private int jumpLevel;//跳跃阶段 1段跳 2段跳private int dir; //朝向 -1左 1右public LayerMask jumpOnWallMask = 0;//墙体Layer层遮罩private bool isHoldWall; //是否在墙上public float jumpOnWallHForce = 1; //墙上跳跃横向力度public float jumpOnWallVForce = 2; //墙上跳跃纵向力度public float gravityOnWall = -24f;void Awake(){//... ...rawGravity = gravity;}void Update(){if (_controller.isGrounded){gravity = rawGravity;_velocity.y = 0;jumpLevel = 0;}//朝着dir方向发射长度为(碰撞体宽度+自身皮肤厚度)的射线RaycastHit2D hit = Physics2D.Linecast(playerBottomTrans.position, playerBottomTrans.position + new Vector3(dir * (Mathf.Abs(transform.localScale.x) * _controller.boxCollider.size.x / 2f + _controller.skinWidth), 0, 0), jumpOnWallMask);if (hit && hit.collider.tag == "Wall"){isHoldWall = true;gravity = gravityOnWall; //可调整由rawGravity随着时间降低到gravityOnWall}else{isHoldWall = false;gravity = rawGravity;}if ( Input.GetKey( KeyCode.RightArrow ) ){//... ...dir = 1;}else if( Input.GetKey( KeyCode.LeftArrow ) ){//... ...dir = -1;}else { //... ...}//原点击UpArrow代码删除,改为如下//点击向上if (Input.GetKeyDown(KeyCode.UpArrow)){//未在墙上if (!isHoldWall){//在地面起跳 (1级跳)if (_controller.isGrounded){jumpLevel = 1;_velocity.y = Mathf.Sqrt(2f * jumpHeight * -gravity);_animator.Play(Animator.StringToHash("Jump"));}else{//1级跳途中,再次起跳(2级跳)if(jumpLevel == 1){jumpLevel = 2;_velocity.y = Mathf.Sqrt(2f * jumpHeight * -gravity);_animator.Play("Jump");}}}else{//墙上可连续起跳,若想限制只能2段跳,则要类似上面代码写法//在墙上_velocity.x += -dir * jumpOnWallHForce;//仅在墙上会受到重力因此想再次起跳上升 必须比重力还要大的力 1+jumpOnWallForce//若在墙体上且在地面上,则不要加这个jumpOnWallVForce力,否则贴墙就起跳会让你飞起来!_velocity.y += Mathf.Sqrt(2f * jumpHeight * -gravity) * (1 + (_controller.isGrounded ? 0 : jumpOnWallVForce));_animator.Play("Jump");}}}
完整代码:
using UnityEngine;
using System.Collections;
using Prime31;public class DemoScene : MonoBehaviour
{// movement configprivate float rawGravity;public float gravity = -25f;public float runSpeed = 8f;public float groundDamping = 20f; // how fast do we change direction? higher means fasterpublic float inAirDamping = 5f;public float jumpHeight = 3f;[HideInInspector]private float normalizedHorizontalSpeed = 0;private CharacterController2D _controller;private Animator _animator;private RaycastHit2D _lastControllerColliderHit;private Vector3 _velocity;private int jumpLevel;//跳跃阶段 1段跳 2段跳private int dir; //朝向 -1左 1右public LayerMask jumpOnWallMask = 0;//墙体Layer层遮罩private bool isHoldWall; //是否在墙上public float jumpOnWallHForce = 1; //墙上跳跃横向力度public float jumpOnWallVForce = 2; //墙上跳跃纵向力度public float gravityOnWall = -24f;void Awake(){_animator = GetComponent<Animator>();_controller = GetComponent<CharacterController2D>();// listen to some events for illustration purposes_controller.onControllerCollidedEvent += onControllerCollider;_controller.onTriggerEnterEvent += onTriggerEnterEvent;_controller.onTriggerExitEvent += onTriggerExitEvent;rawGravity = gravity;}#region Event Listenersvoid onControllerCollider( RaycastHit2D hit ){// bail out on plain old ground hits cause they arent very interestingif( hit.normal.y == 1f )return;// logs any collider hits if uncommented. it gets noisy so it is commented out for the demo//Debug.Log( "flags: " + _controller.collisionState + ", hit.normal: " + hit.normal );}void onTriggerEnterEvent( Collider2D col ){Debug.Log( "onTriggerEnterEvent: " + col.gameObject.name );}void onTriggerExitEvent( Collider2D col ){Debug.Log( "onTriggerExitEvent: " + col.gameObject.name );}#endregion// the Update loop contains a very simple example of moving the character around and controlling the animationvoid Update(){if (_controller.isGrounded){gravity = rawGravity;_velocity.y = 0;jumpLevel = 0;}//朝着dir方向发射长度为(碰撞体一半宽度+自身皮肤厚度)的射线RaycastHit2D hit = Physics2D.Linecast(playerBottomTrans.position, playerBottomTrans.position + new Vector3(dir * (Mathf.Abs(transform.localScale.x) * _controller.boxCollider.size.x / 2f + _controller.skinWidth), 0, 0), jumpOnWallMask);if (hit && hit.collider.tag == "Wall"){isHoldWall = true;gravity = gravityOnWall; //可调整由rawGravity随着时间降低到gravityOnWall}else{isHoldWall = false;gravity = rawGravity;}if ( Input.GetKey( KeyCode.RightArrow ) ){normalizedHorizontalSpeed = 1;dir = 1;if( transform.localScale.x < 0f )transform.localScale = new Vector3( -transform.localScale.x, transform.localScale.y, transform.localScale.z );if( _controller.isGrounded )_animator.Play( Animator.StringToHash( "Run" ) );}else if( Input.GetKey( KeyCode.LeftArrow ) ){normalizedHorizontalSpeed = -1;dir = -1;if( transform.localScale.x > 0f )transform.localScale = new Vector3( -transform.localScale.x, transform.localScale.y, transform.localScale.z );if( _controller.isGrounded )_animator.Play( Animator.StringToHash( "Run" ) );}else{normalizedHorizontalSpeed = 0;if( _controller.isGrounded )_animator.Play( Animator.StringToHash( "Idle" ) );}//点击向上if (Input.GetKeyDown(KeyCode.UpArrow)){//未在墙上if (!isHoldWall){//在地面起跳 (1级跳)if (_controller.isGrounded){jumpLevel = 1;_velocity.y = Mathf.Sqrt(2f * jumpHeight * -gravity);_animator.Play(Animator.StringToHash("Jump"));}else{//1级跳途中,再次起跳(2级跳)if(jumpLevel == 1){jumpLevel = 2;_velocity.y = Mathf.Sqrt(2f * jumpHeight * -gravity);_animator.Play("Jump");}}}else{//墙上可连续起跳,若想限制只能2段跳,则要类似上面代码写法//在墙上_velocity.x += -dir * jumpOnWallHForce;//仅在墙上会受到重力因此想再次起跳上升 必须比重力还要大的力 1+jumpOnWallForce//若在墙体上且在地面上,则不要加这个jumpOnWallVForce力,否则贴墙就起跳会让你飞起来!_velocity.y += Mathf.Sqrt(2f * jumpHeight * -gravity) * (1 + (_controller.isGrounded ? 0 : jumpOnWallVForce));_animator.Play("Jump");}}// apply horizontal speed smoothing it. dont really do this with Lerp. Use SmoothDamp or something that provides more controlvar smoothedMovementFactor = _controller.isGrounded ? groundDamping : inAirDamping; // how fast do we change direction?_velocity.x = Mathf.Lerp( _velocity.x, normalizedHorizontalSpeed * runSpeed, Time.deltaTime * smoothedMovementFactor );// apply gravity before moving_velocity.y += gravity * Time.deltaTime;//在地面上,按住下键不松开会蓄力将起跳速度*3倍// if holding down bump up our movement amount and turn off one way platform detection for a frame.// this lets us jump down through one way platformsif( _controller.isGrounded && Input.GetKey( KeyCode.DownArrow ) ){_velocity.y *= 3f;_controller.ignoreOneWayPlatformsThisFrame = true;}_controller.move( _velocity * Time.deltaTime );// grab our current _velocity to use as a base for all calculations_velocity = _controller.velocity;}}
蹬墙跳问题:


因此你要将重力、X Force 、Y Force、JumpHeight都要调整好才能呈现出正常的蹬墙跳,目前来看仅靠简单调整Y Force是不行的,要么力度太大 要么力度太小。
二、扶墙下滑

Asset Store使用免费资源:Hero Knight - Pixel Art

if(!_controller.isGrounded){if (isHoldWall){//必须是坠落时 if (_velocity.y < 0){//人物顶点发起射线检测到墙体 才算是完整在墙体上 播放扶墙动画RaycastHit2D hit2 = Physics2D.Linecast(playerTopTrans.position, playerTopTrans.position +new Vector3(dir * (Mathf.Abs(transform.localScale.x) * _controller.boxCollider.size.x / 2f + _controller.skinWidth), 0, 0), jumpOnWallMask);if (hit2 && hit2.collider.tag == "Wall"){_animator.Play(Animator.StringToHash("WallSlide"));}}}else{//避免影响1级跳(离地后)以及2级跳时立即切到Fall动画,代码里没有主动将jumpLevel在1级跳或2级跳结束后将jumpLevel改为0的操作,仅在蹬墙跳重置为0if (jumpLevel != 2 && jumpLevel != 1){_animator.Play(Animator.StringToHash("Fall"));}}}
蹬墙跳时进行重置jumpLevel为0状态

![]()
Animator如上所示,Roll和Jump是无条件直接结束时回到Fall,仅适用于本案例不会在平地滚动。


可做辅助射线查看是否正常射线检测到墙体
//朝着dir方向发射长度为(碰撞体宽度+自身皮肤厚度)的射线
Debug.DrawRay(playerTopTrans.position, new Vector3(dir * (Mathf.Abs(transform.localScale.x) * _controller.boxCollider.size.x / 2 + _controller.skinWidth), 0, 0), Color.red);
Debug.DrawRay(playerBottomTrans.position, new Vector3(dir * (Mathf.Abs(transform.localScale.x) *_controller.boxCollider.size.x / 2 + _controller.skinWidth), 0, 0), Color.red);

skinWidth是为了让射线延伸到碰撞盒外面一点点(皮肤厚度)从而才能检测到其他物体
相关文章:
【Unity3D】实现横版2D游戏角色二段跳、蹬墙跳、扶墙下滑
目录 一、二段跳、蹬墙跳 二、扶墙下滑 一、二段跳、蹬墙跳 GitHub - prime31/CharacterController2D 下载工程后直接打开demo场景:DemoScene(Unity 2019.4.0f1项目环境) Player物体上的CharacterController2D,Mask添加Wall层…...
mybatis(134/134)完结
一级缓存(默认情况下开启)同一个sqlsession中执行相同的查询语句走一级缓存 二级缓存 :同一个sqlsessionfactory,sqlsession关闭了才会将一级缓存提交到二级缓存中 外部编写的缓存 PageHelper插件:方便进行分页&#x…...
PaddleSeg 从配置文件和模型 URL 自动化运行预测任务
git clone https://github.com/PaddlePaddle/PaddleSeg.git# 在ipynb里面运行 cd PaddleSegimport sys sys.path.append(/home/aistudio/work/PaddleSeg)import os# 配置文件夹路径 folder_path "/home/aistudio/work/PaddleSeg/configs"# 遍历文件夹,寻…...
人工智能丨视觉识别在自动化测试中的应用
视觉识别:自动化测试的新纪元 在当今快速发展的科技时代,软件测试正面对着日益复杂的挑战。作为其中一个关键领域,自动化测试不断寻求创新的方法,以提高测试效率和准确性。在这一背景下,视觉识别技术的引入为自动化测…...
lambda 表达式:Python中的极简艺术
lambda 表达式:Python中的极简艺术 — 让你的代码更简洁、更高效! 引言 在 Python 中,lambda 表达式是一种简洁的定义匿名函数的方式。它通常用于需要函数对象的场景,但又不需要显式定义一个完整函数的场合。本文将详细介绍 la…...
BLE透传方案,IoT短距无线通信的“中坚力量”
在物联网(IoT)短距无线通信生态系统中,低功耗蓝牙(BLE)数据透传是一种无需任何网络或基础设施即可完成双向通信的技术。其主要通过简单操作串口的方式进行无线数据传输,最高能满足2Mbps的数据传输速率&…...
无用知识研究:对std::common_type以及问号表达式类型的理解
先说结论:如果问号表达式能编译通过,那么std::common_type就能通过。因为common_type的底层依赖的就是?: common_type的实现里,利用了问号表达式:ternary conditional operator (?:) https://stackoverflow.com/questions/1432…...
苍穹外卖—订单模块
该模块分为地址表的增删改查、用户下单、订单支付三个部分。 第一部分地址表的增删改查无非就是对于单表的增删改查,较基础,因此直接导入代码。 地址表 一个用户可以有多个地址,同时有一个地址为默认地址。用户还可为地址添加例如&q…...
「 机器人 」扑翼飞行器的数据驱动建模核心方法
前言 数据驱动建模可充分利用扑翼飞行器的已有运行数据,改进动力学模型与控制策略,并对未建模动态做出更精确的预测。在复杂的非线性飞行环境中,该方法能有效弥补传统解析建模的不足,具有较高的研究与应用价值。以下针对主要研究方向和实现步骤进行整理与阐述。 1. 数据驱动…...
openeuler 22.03 lts sp4 使用 cri-o 和 静态 pod 的方式部署 k8s-v1.32.0 高可用集群
前情提要 整篇文章会非常的长…可以选择性阅读,另外,这篇文章是自己学习使用的,用于生产,还请三思和斟酌 静态 pod 的部署方式和二进制部署的方式是差不多的,区别在于 master 组件的管理方式是 kubectl 还是 systemctl有 kubeadm 工具,为什么还要用静态 pod 的方式部署?…...
Helm Chart 实战指南
Helm 是 Kubernetes 的包管理工具,而 Helm Chart 是 Helm 的核心概念,用于定义、安装和升级 Kubernetes 应用。本文将带你从零开始,通过实战演练,掌握 Helm Chart 的创建、配置和部署,帮助你高效管理 Kubernetes 应用。 1. 环境准备 在开始之前,确保你已经具备以下环境:…...
【数据结构】_顺序表经典算法OJ(力扣版)
目录 1. 移除元素 1.1 题目描述及链接 1.2 解题思路 1.3 程序 2. 合并两个有序数组 1.1 原题链接及题目描述 1.2 解题思路 1.3 程序 1. 移除元素 1.1 题目描述及链接 原题链接:27. 移除元素 - 力扣(LeetCode) 题目描述:…...
目前市场主流的AI PC对于大模型本地部署的支持情况分析-Deepseek
以下是目前市场主流AI PC对**大模型本地部署支持情况**的综合分析,结合硬件能力、软件生态及厂商动态进行总结: --- ### **一、硬件配置与算力支持** 1. **核心处理器架构** - **异构计算方案(CPUGPUNPU)**:主流…...
Vue3 v-bind 和 v-model 对比
1. 基本概念 1.1 v-bind 单向数据绑定从父组件向子组件传递数据简写形式为 : 1.2 v-model 双向数据绑定父子组件数据同步本质是 v-bind 和 v-on 的语法糖 2. 基础用法对比 2.1 表单元素绑定 <!-- v-bind 示例 --> <template><input :value"text&quo…...
MySQL分表自动化创建的实现方案(存储过程、事件调度器)
《MySQL 新年度自动分表创建项目方案》 一、项目目的 在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低。分表是一种有效的优化策略,它将数据分散存储在多…...
接口技术-第6次作业
目录 作业内容 解答 1.假设在一个系统中,8255A的端口地址为184H-187H,A口工作于方式1输出,B口工作于方式1输入,禁止中断,C口剩余的两根线PC5,PC4位输入,如下图所示,试编写初始化…...
计算机网络之计算机网络体系结构
一、定义与概述 计算机网络体系结构是计算机网络及其部件所应该完成功能的精确定义,这些功能由何种硬件或软件完成是遵循这种体系结构的。体系结构是抽象的,实现是具体的,是运行在计算机软件和硬件之上的。 二、主流模型 目前,…...
(1)Linux高级命令简介
Linux高级命令简介 在安装好linux环境以后第一件事情就是去学习一些linux的基本指令,我在这里用的是CentOS7作演示。 首先在VirtualBox上装好Linux以后,启动我们的linux,输入账号密码以后学习第一个指令 简介 Linux高级命令简介ip addrtou…...
网络直播时代的营销新策略:基于受众分析与开源AI智能名片2+1链动模式S2B2C商城小程序源码的探索
摘要:随着互联网技术的飞速发展,网络直播作为一种新兴的、极具影响力的媒体形式,正逐渐改变着人们的娱乐方式、消费习惯乃至社交模式。据中国互联网络信息中心数据显示,网络直播用户规模已达到3.25亿,占网民总数的45.8…...
CSS(快速入门)
欢迎大家来到我的博客~欢迎大家对我的博客提出指导,有错误的地方会改进的哦~点击这里了解更多内容 目录 一、什么是CSS?二、基本语法规范三、CSS选择器3.1 标签选择器3.2 id选择器3.3 class选择器3.4 通配符选择器3.5 复合选择器 四、常用CSS样式4.1 color4.2 font…...
waitpid使用
waitpid 是 Unix/Linux 系统中用于等待子进程状态变化的系统调用。它允许父进程挂起执行,直到指定的子进程终止或者发生了其他指定的状态变化。 waitpid 的语法 pid_t waitpid(pid_t pid, int *status, int options); pid: 要等待的子进程的进程 ID,特殊…...
对顾客行为的数据分析:融入2+1链动模式、AI智能名片与S2B2C商城小程序的新视角
摘要:随着互联网技术的飞速发展,企业与顾客之间的交互方式变得日益多样化,移动设备、社交媒体、门店、电子商务网站等交互点应运而生。这些交互点不仅为顾客提供了便捷的服务体验,同时也为企业积累了大量的顾客行为数据。本文旨在…...
MySQL查询优化(三):深度解读 MySQL客户端和服务端协议
如果需要从 MySQL 服务端获得很高的性能,最佳的方式就是花时间研究 MySQL 优化和执行查询的机制。一旦理解了这些,大部分的查询优化是有据可循的,从而使得整个查询优化的过程更有逻辑性。下图展示了 MySQL 执行查询的过程: 客户端…...
pytorch线性回归模型预测房价例子
import torch import torch.nn as nn import torch.optim as optim import numpy as np# 1. 创建线性回归模型类 class LinearRegressionModel(nn.Module):def __init__(self):super(LinearRegressionModel, self).__init__()self.linear nn.Linear(1, 1) # 1个输入特征&…...
UE AController
定义和功能 AController是一种特定于游戏的控制器,在UE框架中用于定义玩家和AI的控制逻辑。AController负责处理玩家输入,并根据这些输入驱动游戏中的角色或其他实体的行为。设计理念 AController设计用于分离控制逻辑与游戏角色,增强游戏设计…...
选择的阶段性质疑
条条大路通罗马,每个人选择的道路,方向并不一样,但不妨碍都可以到达终点,而往往大家会更推崇自己走过的路径。 自己靠什么走向成功,自己用了什么方法,奉行什么原则或者理念,也会尽可能传播这种&…...
Git进阶之旅:Git 配置信息 Config
Git 配置级别: 仓库级别:local [ 优先级最高 ]用户级别:global [ 优先级次之 ]系统级别:system [ 优先级最低 ] 配置文件位置: git 仓库级别对应的配置文件是当前仓库下的 .git/configgit 用户级别对应的配置文件时用…...
51单片机开发:定时器中断
目标:利用定时器中断,每隔1s开启/熄灭LED1灯。 外部中断结构图如下图所示,要使用定时器中断T0,须开启TE0、ET0。: 系统中断号如下图所示:定时器0的中断号为1。 定时器0的工作方式1原理图如下图所示&#x…...
ultralytics 是什么?
ultralytics 是一个用于计算机视觉任务的 Python 库,专注于提供高效、易用的目标检测、实例分割和图像分类工具。它最著名的功能是实现 YOLO(You Only Look Once) 系列模型,特别是最新的 YOLOv8。 1. YOLO 是什么? YO…...
Qt调用FFmpeg库实时播放UDP组播视频流
基于以下参考链接,通过改进实现实时播放UDP组播视频流 https://blog.csdn.net/u012532263/article/details/102736700 源码在windows(qt-opensource-windows-x86-5.12.9.exe)、ubuntu20.04.6(x64)(qt-opensource-linux-x64-5.12.12.run)、以…...
