【unity实战】使用旧输入系统Input Manager 写一个 2D 平台游戏玩家控制器——包括移动、跳跃、滑墙、蹬墙跳

最终效果

在这里插入图片描述

素材下载

人物

https://rvros.itch.io/animated-pixel-hero
在这里插入图片描述

环境

https://bardent.itch.io/the-bardent-asset-pack
在这里插入图片描述
https://brullov.itch.io/oak-woods
在这里插入图片描述

简单绘制环境

参考:【推荐100个unity插件之14】Unity2D TileMap的探究(最简单,最全面的TileMap使用介绍)
在这里插入图片描述

角色移动跳跃

新增PlayerController

public class PlayerController : MonoBehaviour
{
    private float movementInputDirection; // 水平输入方向
    private bool isFacingRight = true; // 玩家是否面向右侧
    private Rigidbody2D rb;
    public float movementSpeed = 10.0f; // 移动速度
    public float jumpForce = 16.0f; // 跳跃力度

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        CheckInput(); // 检查输入
        CheckMovementDirection();
    }

    private void FixedUpdate()
    {
        ApplyMovement(); // 应用移动
    }

    // 检查玩家面朝的方向
    private void CheckMovementDirection()
    {
        if (isFacingRight && movementInputDirection < 0)
        {
            Flip(); // 翻转角色
        }
        else if (!isFacingRight && movementInputDirection > 0)
        {
            Flip(); // 翻转角色
        }
    }

    // 检查输入
    private void CheckInput()
    {
        movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
        if (Input.GetButtonDown("Jump"))
        {
            Jump(); // 如果按下跳跃键,则执行跳跃
        }
    }

    // 跳跃
    private void Jump()
    {
        rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 设置 y 方向的速度为跳跃力度
    }

    // 移动
    private void ApplyMovement()
    {
        rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
    }

    // 翻转角色
    private void Flip()
    {
        isFacingRight = !isFacingRight; // 改变面向方向的标志
        transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
    }
}

配置
在这里插入图片描述
效果
在这里插入图片描述

视差和摄像机跟随效果

参考:【unity小技巧】Unity实现视差效果与无限地图

新增CameraController代码

public class CameraController : MonoBehaviour
{
    public Transform target;//玩家的位置
    public Transform farBackground, middleBackground, frontBackground;//远的背景和中间背景的位置
    private Vector2 lastPos;//最后一次的相机位置
    void Start()
    {
        lastPos = transform.position;//记录相机的初始位置
    }

    void Update()
    {
        //将相机的位置设置为玩家的位置,但限制在一定的垂直范围内
        //transform.position = new Vector3(target.position.x, target.position.y + 1f, transform.position.z);
        //计算相机在上一帧和当前帧之间移动的距离
        Vector2 amountToMove = new Vector2(transform.position.x - lastPos.x, transform.position.y - lastPos.y);
        //根据相机移动的距离,移动远背景和中间背景的位置
        farBackground.position += new Vector3(amountToMove.x, amountToMove.y, 0f);
        middleBackground.position += new Vector3(amountToMove.x * 0.9f, amountToMove.y, 0f);
        frontBackground.position += new Vector3(amountToMove.x * 0.5f, amountToMove.y, 0f);
        lastPos = transform.position;//更新最后一次的相机位置
    }
}

Map代码

public class Map : MonoBehaviour
{
    [Header("无限地图")]
    private GameObject mainCamera;//主摄像机对象
    private float mapwidth;//地图宽度
    private float totalwidth;//总地图宽度
    
    public int mapNums;//地图重复的次数
    void Start()
    {
        mainCamera = GameObject.FindGameObjectWithTag("MainCamera");//查找标签为"MainCamera'"的对象并赋值
        mapwidth = GetComponent<SpriteRenderer>().sprite.bounds.size.x;//通过SpriteRenderer获得图像宽度
        totalwidth = mapwidth * mapNums;//计算总地图宽度
    }

    void FixedUpdate()
    {
        Vector3 tempPosition = transform.position;//获取当前位置
        if (mainCamera.transform.position.x > transform.position.x + totalwidth / 2)
        {
            tempPosition.x += totalwidth;//将地图向右平移一个完整的地图宽度
            transform.position = tempPosition;//更新位置
        }
        else if (mainCamera.transform.position.x < transform.position.x - totalwidth / 2)
        {
            tempPosition.x -= totalwidth;//将地图向左平移一个完整的地图宽度
            transform.position = tempPosition;//更新位置
        }

    }
}

配置
在这里插入图片描述
在这里插入图片描述
效果
在这里插入图片描述

奔跑动画切换

动画配置
在这里插入图片描述

修改PlayerController

private void FixedUpdate()
{
    ApplyMovement(); // 应用移动
    UpdateStatus();
}

//判断状态 
private void UpdateStatus(){
    if(rb.velocity.x != 0){
        isRunning = true;
    }else{
        isRunning = false;
    }
}

//播放动画
private void UpdateAnimations(){
    animator.SetBool("isRunning", isRunning);
}

效果
在这里插入图片描述

跳跃动画,跳跃次数限制

配置跳跃动画
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
修改PlayerController

[Header("跳跃 地面检测")]
private float amountOfJumpsLeft;//当前可跳跃次数
private bool isGround;//是否是地面
private bool canJump;//能否跳跃
public int amountOfJumps = 1;//跳跃次数
public float groundCheckRadius;//地面检测距离
public Transform groundCheck;//地面检测点
public LayerMask whatIsGround;//地面检测图层


void Start()
{
    rb = GetComponent<Rigidbody2D>();
    animator = GetComponent<Animator>();
    amountOfJumpsLeft = amountOfJumps;
}

// 检查输入
private void CheckInput()
{
    movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
    if (Input.GetButtonDown("Jump"))
    {
        Jump(); // 如果按下跳跃键,则执行跳跃
    }
}

// 跳跃
private void Jump()
{
    if (canJump)
    {
        rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 设置 y 方向的速度为跳跃力度
        amountOfJumpsLeft--;
    }
}

//判断能否跳跃
private void CheckIfCanJump()
{
    if (isGround && rb.velocity.y < 0)
    {
        amountOfJumpsLeft = amountOfJumps;
    }
    if (amountOfJumpsLeft <= 0)
    {
        canJump = false;
    }
    else
    {
        canJump = true;
    }
}

//播放动画
private void UpdateAnimations()
{
    animator.SetBool("isRunning", isRunning);
    animator.SetBool("isGround", isGround);
    animator.SetFloat("yVelocity", rb.velocity.y);
}

//检测
private void CheckSurroundings()
{
    //地面检测
    isGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
}

//视图显示检测范围
private void OnDrawGizmos()
{
    Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
}

配置,这里配置了2段跳
在这里插入图片描述
记得配置地面图层为Ground
在这里插入图片描述
效果
在这里插入图片描述

角色添加2d物理材质,防止角色粘在墙上

在这里插入图片描述
修改摩檫力为0
在这里插入图片描述
配置
在这里插入图片描述
效果
在这里插入图片描述

如果角色移动时背景出现黑线条

方法一

添加材质
在这里插入图片描述
配置
在这里插入图片描述

方法二

我们创建了一个Sprite Atlas来将Spritesheet拖入其中
在这里插入图片描述
把你的瓦片素材拖入
在这里插入图片描述

墙壁滑行

配置滑墙动画
在这里插入图片描述

修改PlayerController

[Header("墙壁滑行")]
public float wallCheckDistance;//墙壁检测距离
public Transform wallCheck;//墙壁检测点
public float wallSlideSpeed;//墙壁滑行速度
private bool isTouchingWall;//是否接触墙壁
private bool isWallSliding;//是否正在墙壁滑行

// 移动
private void ApplyMovement()
{
    rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
    
    //应用滑墙速度    
    if (isWallSliding)
    {
        if (rb.velocity.y < -wallSlideSpeed)
        {
            rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
        }
    }
}

//是否正在墙壁滑行
private void CheckIfWallSliding()
{
    if (isTouchingWall && !isGround && rb.velocity.y < 0)
    {
        isWallSliding = true;
    }
    else
    {
        isWallSliding = false;
    }
}

//播放动画
private void UpdateAnimations()
{
    animator.SetBool("isRunning", isRunning);
    animator.SetBool("isGround", isGround);
    animator.SetFloat("yVelocity", rb.velocity.y);
    animator.SetBool("isWallSliding", isWallSliding);
}

//检测
private void CheckSurroundings()
{
    //地面检测
    isGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);

    //墙面检测
    isTouchingWall = Physics2D.Raycast(wallCheck.position, transform.right, wallCheckDistance, whatIsGround);
}

//视图显示检测范围
private void OnDrawGizmos()
{
    Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
    Gizmos.DrawLine(wallCheck.position, wallCheck.position + wallCheckDistance * Vector3.right);
}

配置
在这里插入图片描述
效果
在这里插入图片描述

实现角色滑墙不可以通过移动离开且不可翻转角色

// 移动
private void ApplyMovement()
{
    // 如果在地面上
    if (isGround)
    {
        rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
    }

    //应用滑墙速度    
    if (isWallSliding)
    {
        if (rb.velocity.y < -wallSlideSpeed)
        {
            rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
        }
    }
}

// 翻转角色
private void Flip()
{
    if (!isWallSliding)
    {
        isFacingRight = !isFacingRight; // 改变面向方向的标志
        transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
    }
}

效果
在这里插入图片描述

空中运动控制

空气阻力和移动速度控制

public float movementForceInAir;//空气中的运动力
public float airDragMultiplier = 0.95f;//空气阻力

// 移动
private void ApplyMovement()
{
    // 如果在地面上
    if (isGround)
    {
        rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
    }
    // 如果不在地面上且不是在墙壁滑行且有水平输入
    else if (!isGround && !isWallSliding && movementInputDirection != 0)
    {
        Vector2 forceToAdd = new Vector2(movementForceInAir * movementInputDirection, 0);// 在空中施加的力
        rb.AddForce(forceToAdd);// 添加力到刚体
        if (Mathf.Abs(rb.velocity.x) > movementSpeed)
        {
            rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y);// 限制水平速度
        }
    }
    // 如果不在地面上且不是在墙壁滑行且没有水平输入
    else if (!isGround && !isWallSliding && movementInputDirection == 0)
    {
        rb.velocity = new Vector2(rb.velocity.x * airDragMultiplier, rb.velocity.y);// 应用空气阻力
    }

    //应用滑墙速度    
    if (isWallSliding)
    {
        if (rb.velocity.y < -wallSlideSpeed)
        {
            rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
        }
    }
}

配置
在这里插入图片描述
效果
在这里插入图片描述

可变跳跃高度

长跳跃和短跳,长按和之前跳的和之前一样高

public float variableJumpHeightMultiplier = 0.5f;

 // 检查输入
private void CheckInput()
{
    movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
    if (Input.GetButtonDown("Jump"))
    {
        Jump(); // 如果按下跳跃键,则执行跳跃
    }

	// 检测是否松开Jump
    if (Input.GetButtonUp("Jump"))
    {
        rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * variableJumpHeightMultiplier);
    }
}

效果
在这里插入图片描述

蹬墙跳

实现不输入,点击跳跃就从墙上跳下来,方向按键+跳跃控制左右蹬墙跳

[Header("蹬墙跳")]
public float wallHopForce; // 离开墙时的力
public float wallJumpForce; // 蹬墙跳时施加的力
public Vector2 wallHopDirection; // 离开墙时的方向向量
public Vector2 wallJumpDirection; // 蹬墙跳时的方向向量
private int facingDirection = 1; // 角色面向的方向,1右 -1左

void Start()
{
    rb = GetComponent<Rigidbody2D>();
    animator = GetComponent<Animator>();
    amountOfJumpsLeft = amountOfJumps;
    
    //归一化向量,因为我们只要向量的方向,而不考虑长度
    wallHopDirection = wallHopDirection.normalized;
    wallJumpDirection = wallJumpDirection.normalized;
}

// 跳跃
private void Jump()
{
    // 如果可以跳跃并且不是在墙壁滑行状态下
    if (canJump && !isWallSliding)
    {
        rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 设置 y 方向的速度为跳跃力度
        amountOfJumpsLeft--;
    }
    // 如果正在墙壁滑行且没有输入水平移动方向,并且可以跳跃
    else if(isWallSliding && movementInputDirection == 0 && canJump){
        isWallSliding = false;
        amountOfJumpsLeft--;
        // 计算添加的力量,使角色从墙壁上弹开
        Vector2 forceToAdd = new Vector2(wallHopForce * wallHopDirection.x * -facingDirection, wallHopForce * wallHopDirection.y);
        rb.AddForce(forceToAdd, ForceMode2D.Impulse);
    }
    // 如果正在墙壁滑行或者正在接触墙壁,并且有水平移动输入,并且可以跳跃
    else if((isWallSliding || isTouchingWall) && movementInputDirection != 0 && canJump){
        isWallSliding = false;
        amountOfJumpsLeft --;
        // 计算添加的力量,使角色从墙壁上跳跃
        Vector2 forceToAdd = new Vector2(wallHopForce * wallHopDirection.x * movementInputDirection, wallJumpForce * wallJumpDirection.y);
        rb.AddForce(forceToAdd, ForceMode2D.Impulse);
    }
}

 //判断能否跳跃
private void CheckIfCanJump()
{
    if ((isGround && rb.velocity.y < 0) || isWallSliding)
    {
        amountOfJumpsLeft = amountOfJumps;
    }

    if (amountOfJumpsLeft <= 0)
    {
        canJump = false;
    }
    else
    {
        canJump = true;
    }
}

// 翻转角色
private void Flip()
{
    if (!isWallSliding)
    {
        facingDirection *= -1;
        isFacingRight = !isFacingRight; // 改变面向方向的标志
        transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
    }
}

配置
在这里插入图片描述
运行效果
在这里插入图片描述

完整代码

using Unity.VisualScripting;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private float movementInputDirection; // 水平输入方向
    private bool isFacingRight = true; // 玩家是否面向右侧
    private Rigidbody2D rb;
    public float movementSpeed = 10.0f; // 移动速度
    public float jumpForce = 16.0f; // 跳跃力度

    private Animator animator;

    [Header("状态")]
    public bool isRunning;

    [Header("跳跃 地面检测")]

    public int amountOfJumps = 1;//跳跃次数
    public float groundCheckRadius;//地面检测距离
    public Transform groundCheck;//地面检测点
    public LayerMask whatIsGround;//地面检测图层
    private float amountOfJumpsLeft;//当前可跳跃次数
    private bool isGround;//是否是地面
    private bool canJump;//能否跳跃

    [Header("墙壁滑行")]
    public float wallCheckDistance;//墙壁检测距离
    public Transform wallCheck;//墙壁检测点
    public float wallSlideSpeed;//墙壁滑行速度
    public float movementForceInAir;//空气中的运动力
    public float airDragMultiplier = 0.95f;//空气阻力
    private bool isTouchingWall;//是否接触墙壁
    private bool isWallSliding;//是否正在墙壁滑行

    [Header("可变高度")]
    public float variableJumpHeightMultiplier = 0.5f;

    [Header("蹬墙跳")]
    public float wallHopForce; // 离开墙时的力
    public float wallJumpForce; // 蹬墙跳时施加的力
    public Vector2 wallHopDirection; // 离开墙时的方向向量
    public Vector2 wallJumpDirection; // 蹬墙跳时的方向向量
    private int facingDirection = 1; // 角色面向的方向,1右 -1左

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        amountOfJumpsLeft = amountOfJumps;
        //归一化向量,因为我们只要向量的方向,而不考虑长度
        wallHopDirection = wallHopDirection.normalized;
        wallJumpDirection = wallJumpDirection.normalized;
    }

    void Update()
    {
        CheckInput(); // 检查输入
        CheckMovementDirection();
        UpdateAnimations();
        CheckIfCanJump();
        CheckIfWallSliding();
        CheckSurroundings();
    }

    private void FixedUpdate()
    {
        ApplyMovement(); // 应用移动
        UpdateStatus();
    }

    // 检查玩家面朝的方向
    private void CheckMovementDirection()
    {
        if (isFacingRight && movementInputDirection < 0)
        {
            Flip(); // 翻转角色
        }
        else if (!isFacingRight && movementInputDirection > 0)
        {
            Flip(); // 翻转角色
        }
    }

    // 检查输入
    private void CheckInput()
    {
        movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
        if (Input.GetButtonDown("Jump"))
        {
            Jump(); // 如果按下跳跃键,则执行跳跃
        }
        
        if (Input.GetButtonUp("Jump"))
        {
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * variableJumpHeightMultiplier);
        }
    }

    // 移动
    private void ApplyMovement()
    {
        // 如果在地面上
        if (isGround)
        {
            rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
        }
        // 如果不在地面上且不是在墙壁滑行且有水平输入
        else if (!isGround && !isWallSliding && movementInputDirection != 0)
        {
            Vector2 forceToAdd = new Vector2(movementForceInAir * movementInputDirection, 0);// 在空中施加的力
            rb.AddForce(forceToAdd);// 添加力到刚体
            if (Mathf.Abs(rb.velocity.x) > movementSpeed)
            {
                rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y);// 限制水平速度
            }
        }
        // 如果不在地面上且不是在墙壁滑行且没有水平输入
        else if (!isGround && !isWallSliding && movementInputDirection == 0)
        {
            rb.velocity = new Vector2(rb.velocity.x * airDragMultiplier, rb.velocity.y);// 应用空气阻力
        }

        // //应用滑墙速度    
        if (isWallSliding)
        {
            if (rb.velocity.y < -wallSlideSpeed)
            {
                rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
            }
        }
    }

    // 移动
    private void ApplyMovement()
    {
        if(!isGround && !isWallSliding && movementInputDirection == 0){
            rb.velocity = new Vector2(rb.velocity.x * airDragMultiplier, rb.velocity.y);// 应用空气阻力
        }else{
            rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
        }

        //应用滑墙速度    
        if (isWallSliding)
        {
            if (rb.velocity.y < -wallSlideSpeed)
            {
                rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
            }
        }
    }

    //判断跑步状态 
    private void UpdateStatus(){
        if(rb.velocity.x != 0){
            isRunning = true;
        }else{
            isRunning = false;
        }
    }

    // 翻转角色
    private void Flip()
    {
        if (!isWallSliding)
        {
            facingDirection *= -1;
            isFacingRight = !isFacingRight; // 改变面向方向的标志
            transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
        }
    }

    //播放动画
    private void UpdateAnimations()
    {
        animator.SetBool("isRunning", isRunning);
        animator.SetBool("isGround", isGround);
        animator.SetFloat("yVelocity", rb.velocity.y);
        animator.SetBool("isWallSliding", isWallSliding);
    }

    //判断能否跳跃
    private void CheckIfCanJump()
    {
        if ((isGround && rb.velocity.y < 0) || isWallSliding)
        {
            amountOfJumpsLeft = amountOfJumps;
        }

        if (amountOfJumpsLeft <= 0)
        {
            canJump = false;
        }
        else
        {
            canJump = true;
        }
    }

    //检测
    private void CheckSurroundings()
    {
        //地面检测
        isGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);

        //墙面检测
        isTouchingWall = Physics2D.Raycast(wallCheck.position, transform.right, wallCheckDistance, whatIsGround);
    }

    //是否墙壁滑行
    private void CheckIfWallSliding()
    {
        if (isTouchingWall && !isGround && rb.velocity.y < 0)
        {
            isWallSliding = true;
        }
        else
        {
            isWallSliding = false;
        }
    }

    //视图显示检测范围
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
        Gizmos.DrawLine(wallCheck.position, wallCheck.position + wallCheckDistance * Vector3.right);
    }
}

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向宇it

创作不易,感谢你的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值