【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是为了让射线延伸到碰撞盒外面一点点(皮肤厚度)从而才能检测到其他物体

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/69664.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

premierePro 2022创建序列方式

概念 序列类似于画画的画布&#xff0c;类似ps的蒙层 一、新建序列方式 1、文件-新建-序列 2、直接将视频拖入时间轴&#xff08;没有序列时&#xff0c;如果有序列不行&#xff09; 3、右键右下角空白处 4、点击新建项按钮

九大服务构建高效 AIOps 平台,全面解决GenAI落地挑战

最近,DevOps运动的联合创始人Patrick Debois分享了他对AI平台与软件研发关系的深刻见解,让我们一起来探讨这个话题。 在AI的落地过程中,我们面临着两个主要难题: 引入AI编码工具后的开发者角色转变:随着像GitHub Copilot这样的AI工具的普及,工程师的角色正在发生深刻变革…

Golang :用Redis构建高效灵活的应用程序

在当前的应用程序开发中&#xff0c;高效的数据存储和检索的必要性已经变得至关重要。Redis是一个快速的、开源的、内存中的数据结构存储&#xff0c;为各种应用场景提供了可靠的解决方案。在这个完整的指南中&#xff0c;我们将学习什么是Redis&#xff0c;通过Docker Compose…

对顾客行为的数据分析:融入2+1链动模式、AI智能名片与S2B2C商城小程序的新视角

摘要&#xff1a;随着互联网技术的飞速发展&#xff0c;企业与顾客之间的交互方式变得日益多样化&#xff0c;移动设备、社交媒体、门店、电子商务网站等交互点应运而生。这些交互点不仅为顾客提供了便捷的服务体验&#xff0c;同时也为企业积累了大量的顾客行为数据。本文旨在…

毕业设计--具有车流量检测功能的智能交通灯设计

摘要&#xff1a; 随着21世纪机动车保有量的持续增加&#xff0c;城市交通拥堵已成为一个日益严重的问题。传统的固定绿灯时长方案导致了大量的时间浪费和交通拥堵。为解决这一问题&#xff0c;本文设计了一款智能交通灯系统&#xff0c;利用车流量检测功能和先进的算法实现了…

算法题(51):删除链表的倒数第N个节点

审题&#xff1a; 需要我们找到倒数第n个节点&#xff0c;并把他从链表中删除&#xff0c;然后把新的链表的头结点返回 思路&#xff1a; 该题的唯一难点就是如何找到单链表的倒数第n个节点 方法一&#xff1a;直接法 我们可以遍历一次单链表&#xff0c;然后把链表的总长度求出…

Ansible自动化运维实战--yaml的使用和配置(7/8)

文章目录 一、YAML 基本语法1.1. 缩进1.2. 注释1.3. 列表1.4. 字典 二、Ansible 中 YAML 的应用2.1. Ansible 剧本&#xff08;Playbooks&#xff09;2.2. 变量定义2.3. 角色&#xff08;Roles&#xff09;2.4. Inventory 文件2.5. 数据类型2.6. 引用变量 在 Ansible 里&#x…

JavaScript_02 表单

表单常用演示: 1.图片 结果失真了... 2.切换图片 切换结果 3.表单:

使用Navicat Premium管理数据库时,如何关闭事务默认自动提交功能?

使用Navicat Premium管理数据库时&#xff0c;最糟心的事情莫过于事务默认自动提交&#xff0c;也就是你写完语句运行时&#xff0c;它自动执行commit提交至数据库&#xff0c;此时你就无法进行回滚操作。 建议您尝试取消勾选“选项”中的“自动开始事务”&#xff0c;点击“工…

android获取EditText内容,TextWatcher按条件触发

android获取EditText内容&#xff0c;TextWatcher按条件触发 背景&#xff1a;解决方案&#xff1a;效果&#xff1a; 背景&#xff1a; 最近在尝试用原生安卓实现仿element-ui表单校验功能&#xff0c;其中涉及到EditText组件内容的动态校验&#xff0c;初步实现功能后&#…

星际战争模拟系统:新月的编程之道

星际战争模拟系统&#xff1a;新月的编程之道 作为一名在 25 世纪星际时代成长起来的科学家和军事战略家&#xff0c;我对编程和人工智能的热爱始于童年。我的父亲是一位著名的物理学家&#xff0c;母亲是一位杰出的生物工程师。在他们的影响下&#xff0c;我从小就对科学和技术…

网络攻防实战指北专栏讲解大纲与网络安全法

专栏 本专栏为网络攻防实战指北&#xff0c;大纲如下所示 进度&#xff1a;目前已更完准备篇、HTML基础 计划&#xff1a;所谓基础不牢&#xff0c;地动山摇。所以下一步将持续更新基础篇内容 讲解信息安全时&#xff0c;结合《中华人民共和国网络安全法》&#xff08;以下简…

[答疑]DDD伪创新哪有资格和仿制药比

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 远航 2025-1-24 10:40 最近的热门话题仿制药&#xff0c;想到您经常批评的伪创新&#xff0c;这两者是不是很像&#xff1f; UMLChina潘加宇 伪创新哪有资格和仿制药比。 仿制药的…

线性调整器——耗能型调整器

线性调整器又称线性电压调节器&#xff0c;以下是关于它的介绍&#xff1a; 基本工作原理 线性调整器的基本电路如图1.1(a)所示,晶体管Q1(工作于线性状态,或非开关状态)构成一个连接直流源V和输出端V。的可调电气电阻,直流源V由60Hz隔离变压器&#xff08;电气隔离和整流&#…

拦截器快速入门及详解

拦截器Interceptor 快速入门 什么是拦截器&#xff1f; 是一种动态拦截方法调用的机制&#xff0c;类似于过滤器。 拦截器是Spring框架中提供的&#xff0c;用来动态拦截控制器方法的执行。 拦截器的作用&#xff1a;拦截请求&#xff0c;在指定方法调用前后&#xff0c;根…

一文掌握ADB的安装及使用

文章目录 一、什么是ADB&#xff1f;二、 安装ADB2.1 下载ADB2.2 配置环境变量 三、连接Android设备四、 常用ADB命令五、ADB高级功能5.1 屏幕截图和录制5.2 模拟按键输入5.3 文件管理5.4 系统设置管理5.5 系统操作指令5.6 日志操作指令5.7 APK操作指令5.8 设备重启和恢复 六、…

【Redis】Redis入门以及什么是分布式系统{Redis引入+分布式系统介绍}

文章目录 介绍redis的引入 分布式系统单机架构应用服务和数据库服务分离【负载均衡】引入更多的应用服务器节点 单机架构 分布式是什么 数据库分离和负载均衡 理解负载均衡 数据库读写分离 引入缓存 数据库分库分表 引入微服务 介绍 The open source, in-memory data store us…

springboot使用rabbitmq

使用springboot创建rabbitMQ的链接。 整个项目结构如下&#xff1a; 1.maven依赖 <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>3.4.1</version> </dependency>application.y…

论文阅读(九):通过概率图模型建立连锁不平衡模型和进行关联研究:最新进展访问之旅

1.论文链接&#xff1a;Modeling Linkage Disequilibrium and Performing Association Studies through Probabilistic Graphical Models: a Visiting Tour of Recent Advances 摘要&#xff1a; 本章对概率图模型&#xff08;PGMs&#xff09;的最新进展进行了深入的回顾&…