游戏开发小结——Unity 2.5D 平台游戏:跳墙(新输入系统)
目标:使用 Unity 实现跳墙机制,让玩家在平台游戏中到达更高的位置。
到目前为止,在这个项目中,我已经介绍了如何创建具有双跳功能的物理基础角色控制器。我添加了动态平台、收藏品和事件驱动的电梯。
今天我将回到角色控制器脚本(Player)并添加墙跳功能。这将允许玩家检测墙壁的表面并从一堵墙跳到另一堵墙。
场景设置
我的场景有一个平台和两堵墙,创建了一条向上的路径。到达顶部的唯一方法是在两堵墙之间跳墙。
现在,我将两堵墙标记为“Wall”,作为玩家控制器检测要在哪堵特定墙上执行墙跳跃的一种方式。
游戏输入脚本
再一次,这一切都是通过 Unity 的新输入系统(Input System)完成的。我有一个单独的脚本来处理附加到玩家的游戏输入。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameInput : MonoBehaviour
{
private ActionMaps _input;
public event EventHandler OnInteract;
private void Awake()
{
_input = new ActionMaps();
}
private void OnEnable()
{
_input.Player.Enable();
_input.Player.Interact.performed += Interact_performed;
}
private void Interact_performed(UnityEngine.InputSystem.InputAction.CallbackContext obj)
{
OnInteract?.Invoke(this,EventArgs.Empty);
}
public Vector2 GetMovementVectorNormalized()
{
Vector2 _direction = _input.Player.Move.ReadValue<Vector2>();
_direction = _direction.normalized;
return _direction;
}
public bool IsJumping()
{
if (_input.Player.Jump.IsPressed())
return true;
else return false;
}
}
实施跳墙
需要一些变量、方法和 if 语句来使墙跳转到与当前角色控制器一起工作。
玩家控制器脚本
我添加了新的字段变量:
Float wallJumpForce X & Y:这决定了 X 和 Y 方向上墙跳跃的强度。
Bool _canWallJump:检查玩家是否可以跳墙
Vector3 WallJumpNormal:这将存储我们想要跳跃方向的表面法线的 Vector3。
OnControllerColliderHit 方法
接下来,我使用角色控制器的 Collider hit 方法来检测玩家碰撞的对象。我们可以通过使用 hit 变量来获取对象的变换,然后比较对象的标签以找到可跳跃的墙。当玩家与墙壁碰撞时会投射蓝色光线,代表表面法线,该法线将用作墙壁跳跃的方向矢量(vector)。
If 语句:只想当玩家在空中而不接触地面时进行墙跳,并且tag==“Wall”。
private void OnControllerColliderHit(ControllerColliderHit hit)
{
if (_controller.isGrounded == false && hit.transform.CompareTag("Wall"))
{
Debug.DrawRay(hit.point, hit.normal, Color.blue);
if (hit.normal.x == 1f || hit.normal.x == -1f)
{
_wallJumpNormal = hit.normal * _wallJumpForceX;
_canWallJump = true;
}
}
}
Non-Bug Wall Jump
下一个 if 语句检查法线表面的 x 值。我们希望 x 法线为 1f 或 -1f。意味着表面垂直于玩家(flat)。这可以防止玩家意外跳入角落并被发射到太空(如下所示)。
由于乘以跳跃力而显示
2.50 force. NormalVector * 2.50f文章来源:https://www.toymoban.com/news/detail-790044.html
然后我们采用字段变量 _wallJumpNormal 并让它等于 hit.normal * wallJumpForceX (NormalVector (1.0/-1.0,0,0) * 2.50f) 和 _canWallJump = true。
if (hit.normal.x == 1f || hit.normal.x == -1f)
{
_wallJumpNormal = hit.normal * _wallJumpForceX;
_canWallJump = true;
}
}
运动方式
之后就是调整动作方法的时候了。
每当玩家接触地面墙壁时,跳跃就会被禁用。
if (_groundPlayer == true)
{
_canWallJump = false;
_controller.Move(Vector3.zero);
_yVelocity = -_gravity;
}
当玩家在空中时,我们有两个选择:墙跳或二段跳。
Wall Jumping(跳墙):只有在满足条件的情况下,我们才会检查是否允许玩家跳墙。如果我们可以进行墙跳,请关闭双跳功能,将 xVelocity 向量设置为 _wallJumpNormal Vector,并添加 yVelocity。
if ((_canWallJump && _gameInput.IsJumping() && Time.time > _jumpDelay))
{
_doubleJump = true;
_canWallJump = false;
_xVelocity = _wallJumpNormal;
if (_yVelocity < 0)
{
_yVelocity = 0;
_yVelocity += _wallJumpForceY;
}
else
_yVelocity += _wallJumpForceY;
}
文章来源地址https://www.toymoban.com/news/detail-790044.html
然后这些值被传递到更新玩家速度和方向的移动方法中的最后一行代码。
Vector3 _movement = new Vector3(_xVelocity.x, _yMaxVelocity, 0);
_controller.Move(_movement * Time.deltaTime);
总之,这就是我在这个项目中实现跳墙功能的方法。这是通过获取玩家的墙壁表面法线并使用该矢量(vector)将玩家推向下一面墙壁来实现的。
完整的玩家脚本
using UnityEngine;
using UnityEngine.SceneManagement;
public class Player : MonoBehaviour
{
private GameInput _gameInput;
private CharacterController _controller;
[SerializeField] private float _speed = 2.0f;
[SerializeField] private float _jumpStrength = 15.0f;
[SerializeField]private float _gravity = 1.0f;
[SerializeField] private bool _groundPlayer;
public int _coinCollected { get; private set; } = 0;
private int _lives = 3;
private float _yVelocity;
private Vector3 _direction;
private Vector3 _xVelocity;
private bool _doubleJump;
private float _jumpDelay;
[SerializeField] private float _wallJumpForceX;
[SerializeField] private float _wallJumpForceY;
[SerializeField] private bool _canWallJump;
[SerializeField] private Vector3 _wallJumpNormal;
private void Awake()
{
_gameInput = GetComponent<GameInput>();
_controller = GetComponent<CharacterController>();
if (_gameInput == null) Debug.LogError("Missing Game Input");
if (_controller == null) Debug.LogError("Missing Character Controller");
}
private void Start()
{
UIManager._instance.UpdateLivesText(_lives);
UIManager._instance.UpdateCoinText(_coinCollected);
}
private void Update()
{
Movement();
ResetSpawn();
Death();
}
private void OnControllerColliderHit(ControllerColliderHit hit)
{
if (_controller.isGrounded == false && hit.transform.CompareTag("Wall"))
{
Debug.DrawRay(hit.point, hit.normal, Color.blue);
if (hit.normal.x == 1f || hit.normal.x == -1f)
{
_wallJumpNormal = hit.normal * _wallJumpForceX;
_canWallJump = true;
}
}
}
private void Movement()
{
_groundPlayer = _controller.isGrounded;
if (_groundPlayer == true)
{
_canWallJump = false;
_controller.Move(Vector3.zero);
_yVelocity = -_gravity;
}
if (_groundPlayer == true && _gameInput.IsJumping() && !_canWallJump)
{
_yVelocity += _jumpStrength;
_doubleJump = false;
_jumpDelay = Time.time + 0.3f;
}
else if (_groundPlayer == false)
{
if ((_canWallJump && _gameInput.IsJumping() && Time.time > _jumpDelay))
{
_doubleJump = true;
_canWallJump = false;
_xVelocity = _wallJumpNormal;
if (_yVelocity < 0)
{
_yVelocity = 0;
_yVelocity += _wallJumpForceY;
}
else
_yVelocity += _wallJumpForceY;
}
if ((!_doubleJump && _gameInput.IsJumping() && Time.time > _jumpDelay))
{
_doubleJump = true;
if (_yVelocity < 0)
{
_yVelocity = 0;
_yVelocity += 6f;
}
else
_yVelocity += 6f;
}
_yVelocity -= _gravity * Time.deltaTime;
}
var _yMaxVelocity = Mathf.Clamp(_yVelocity, -20, 100f);
if (_groundPlayer == true)
{
_direction = _gameInput.GetMovementVectorNormalized();
_xVelocity = _direction * _speed;
}
Vector3 _movement = new Vector3(_xVelocity.x, _yMaxVelocity, 0);
_controller.Move(_movement * Time.deltaTime);
}
private void ResetSpawn()
{
Vector3 _currentPosition = transform.position;
Vector3 _spawnLocation = new Vector3(0, 1, 0);
bool uiZeroLivesText = _lives > 0;
if (_currentPosition.y < -5)
{
transform.position = _spawnLocation;
_lives--;
if(uiZeroLivesText)
UIManager._instance.UpdateLivesText(_lives);
}
}
private void Death()
{
if(_lives < 0)
{
SceneManager.LoadScene(0);
}
}
public void AddCoins()
{
_coinCollected++;
UIManager._instance.UpdateCoinText(_coinCollected);
}
}
到了这里,关于游戏开发小结——Unity 2.5D 平台游戏:跳墙(新输入系统)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!