阶段性复习-C#角色控制及其状态动画

复习内容

复习的主要内容是角色控制器中的角色移动代码部分和切换角色状态动画的代码部分。复习流程:

  1. 在自己的博客上通过看图文内容的方式复习教程并总结知识点;
    • C#语言基础-从第一篇看到第七篇,巩固C#语言的最基本的知识。
    • 角色控制器-一些获取用户按键信息的方法,2维变量的使用以及2维变量和3维变量对应的方式。
    • 角色状态动画的制作和切换-在unity编辑器中实现两种状态(待机状态和移动状态)的动画,并实现不同状态下的动画切换。
    • 尝试在不看教程和文章的情况下独立实现角色控制和动画的制作切换。这里我是重新创建一个review1.0的unity项目,导入资产包。

学习进度1:学完第19课《for循环补充示例》

老师ID:丑萌气质狗(老师的博客

学习进度2:code monkey 的《unity2023初级到中级教程》B站搬运版

有条件的同学可以在youtube上找到院课程,还能在博主的简介中找到code monkey的网站。

为什么会有这篇复习性质的文章

加深对教程内容的理解

跟着视频教程学习的过程中还有很多不理解的地方,为了继续往后看就先跟着教程把代码打出来了,让自己独立做的话肯定做不出来。

尽可能低让自己独立开发的时候不看教程也能做出来

知识点总结

C#基础知识

C#基础知识大部分内容比较简单,但如何传递参数,返回和接收值等比较绕,可以看这篇:

在这个教程之前,我们所有的代码都是写在一个函数括号内的。为了让代码更好地查看和管理。我们会把不同的方法单独拿出来,每个方法只处理一件事。

方法的返回值

创建方法的如果没有返回值则使用void,如果返回值是一个字符串则使用string,若是整数则使用int,以此类推。

要返回的内容使用 “return 返回的内容”。

static int ChangeData(){
      //要执行的代码
      return a;//要返回的内容
}

接收参数

新的方法如果要使用到其它方法的变量(参数),在方法的小括号内编写。

static int ChangeData(int a):要接受的参数一个整数类型的变量a。

static int ChangeData(int a){
      //要执行的代码
      return a;//要返回的内容
}

原方法也要对返回值进行接收。(如果要用到返回值的话)


游戏实操

控制角色移动的方法

监测到用户输入后改变角色位置的值

创建一个监测用户键盘输入并改变角色坐标值的方法。

private void Update(){
}

当用户输入WASD时,改变角色的坐标。因为本游戏只有2维移动,所以声明的是2维向量(vector2)类型的变量,变量的初始值为(0,0)。[vector(x,y)]

vector2 inputVector = new Vector2(0, 0);

用户输入w时,重新给inputVector复制,让其在原来的基础上+1。

if(input.GetKey(KeyCode.W)){//如果用户输入了W
    inputVector.x = + 1; 
};

改变角色的位置坐标(position),让角色位置坐标的值=inputVector的值(随着用户的输入,inputVector也在发生变化。

同样的方式将WASD四个键对应的值的变化给到inputVector。这个时候我们仅仅完成了inputVector的值根据用户按键进行变化。还需要将角色palyer的位置坐标跟inputVector的值关联起来,即用户输入W,inputVector的值变化,然后吧把这个值给到角色的位置属性。

transform.position = inputVector;

从逻辑上来讲,做到这一步我们的角色移动就完成了。完整的代码如下:


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerContriller : MonoBehaviour
{
    //创建一个方法
    private void Update(){
    //声明一个二维变量,让用户输入w的时候变量的x值+1
        Vector2 inputVector = new Vector2(0, 0);
    
        //监控用户按键输入
        if(Input.GetKey(KeyCode.W)){
            inputVector.x = inputVector.x + 1; 
//实际上的x y对应应该是+ 还是-可以根据实际情况调,我这里没有很严谨。
        };

         if(Input.GetKey(KeyCode.S)){
            inputVector.x = inputVector.x - 1; 
        };

        if(Input.GetKey(KeyCode.A)){
            inputVector.y = - 1;
        }
        if(Input.GetKey(KeyCode.D)){
            inputVector.y = + 1;
        }
    
//上面的步骤完成了用户输入的监测,并根据用户输入的情况对变量inputVector进行了重新赋值。

        //还需要让角色的位置坐标跟inputVector新的值关联起来,才能看到角色的移动。

        transform.position = inputVector;

    }
    
}

在引擎中跑一下试一试,会有两个问题:

  1. 角色不是在地面上移动;
  2. 角色只能移动一步且移动后又回到了原点,而没有一直朝着想要的方向运动。

转换坐标,让角色在想要的平面上移动

但是有一个问题是:unity的世界场景是三维的,而我们只希望角色在二维空间中移动。所以需要将角色移动所在的平面XY值与三维空间对应的XYZ对应起来。

Vector3 moveDir = new Vector3(inputVector.x, 0 , inputVector.y)//声明一个三维变量moveDir,它的值是一个三维变量值(x, y, z),其中x=inputVector中x的值,y失踪为0,z为inputVector中y的值。

//实际在操作的时候如果不知道xyz的对应关系其实可以每个都试一试,在引擎中跑一下。

然后让角色的坐标值为新变量moveDir的值。此时角色就能在我们想要的平面 上移动了。这里两步可以多在编辑器中运行感受一下。

此时可以测试你的xy对应的运算是否争取。比如我这里的x 和 y就搞反了。在输入W和S的时候应该改变Y的值。当然你也可以子啊二维坐标转换三维坐标的时候,将X Y的值赋予给moveDir的时候进行调换。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerContriller : MonoBehaviour
{
    //创建一个方法
    private void Update(){
    //声明一个二维变量,让用户输入w的时候变量的x值+1
        Vector2 inputVector = new Vector2(0, 0);
    
        //监控用户按键输入
        if(Input.GetKey(KeyCode.W)){
            inputVector.y = inputVector.y - 1; 
        };

         if(Input.GetKey(KeyCode.S)){
            inputVector.y = inputVector.y + 1; 
        };

        if(Input.GetKey(KeyCode.A)){
            inputVector.x = + 1;
        }
        if(Input.GetKey(KeyCode.D)){
            inputVector.x = - 1;
        }
    
    //上面的步骤完成了用户输入的监测,并根据用户输入的情况对变量inputVector进行了重新赋值。

    //现在来解决让角色在指定平面上移动的问题。我们创建的是一个3D项目,角色的位置信息是有x,y,z三个值的,即是一个三维向量。
    Vector3 moveDir = new Vector3(inputVector.x, 0,inputVector.y);//实际在操作的时候如果不知道xyz的对应关系其实可以每个都试一试,在引擎中跑一下。

 
    //还需要让角色的位置坐标跟inputVector新的值关联起来,才能看到角色的移动。
    //重新声明来moveDir后,角色的位置信息应该跟moveDir关联起来。

        transform.position = transform.position + moveDir ;

    }
    
}

让角色移动后保持现有位置而不再回到原点

上面的代码在执行完成最后一个语句后,仅仅让角色的位置改变了,但是没有让计算机记录当前位置。

transform.position = transform.position + moveDir;

可以简写成

transform.position += moveDir;

此时我们面临一个新的问题:

  1. 角色移动速度过快一下就跑出屏幕外了。

避免角色移动的速度受屏幕刷新率的影响

转换角色位置(position)的值,使其=moveDir的值*渲染一帧需要的时间(Time.deltaTime)。

transform.position += moveDir * Time.deltaTime;

然后我们会发现角色的移动又过于慢了。

开放一个系数,使得编辑器中可以微调角色移动的速度

此时角色已经可以根据用户的输入进行移动,并且不受屏幕刷新率的影响。但是有可能角色移动的速度还是过慢或过快。可以声明一个系数,让角色的移动乘以这个系数来调整角色移动的速度。使用public可以在编辑器中显示和调整这个系数,更方便调整和观察。

public float moveSpeed = 7f;//声明一个浮点数
transform.position += moveDir * Time.deltaTime * moveSpeed;//在原来的position上加入moveSpeed系数。

使角色斜着走的时候保持同样的速度

当角色朝着正X轴方向走的一下,用户的位置坐标为(1, 0, 0)[三维变量中的y使用为0,x等于inputVector的x,z等于inputVector的y],此时角色移动的距离为1。在y轴也类似。

但是当用户同时按下两个方向的键,让角色朝着斜着的方向走的时候,坐标可能改为(1, 0, 1),此时角色实际移动的距离是根号2。也就是说角色移动的速度比在正方向移动的速度变快了。所以引入了向量归一的概念

新手如果不理解原理,只需要记住使用normalized进行向量归一让角色在斜方向上的运动速度保持与整方向运动速度一致即可。

inputVector = inputVector.normalized;

ok 记得停下来关闭文章和视频练习一下。

优化角色移动(移动时转向)

前面我们制作了一个可以控制角色移动的脚本,但是在移动过程中,角色的始终是朝着一个方向的,这不符合常识,我们需要角色始终朝向它运动的方向。

transform.forward = moveDir;

此时影响forward的只有moveDir这一个因素。转向会很生硬。

transform.forward = Vector3.Slerp(transform.forward,moveDir,Time.deltaTime);//将影响转向的因素改为当前朝向、移动方向的坐标值和渲染帧所需的时间。

同样引入一个速度因子,来控制转向的速度

float rotatSpeed = 10f;
Vector3.Slerp(transform.forward,moveDir,Time.deltaTime * rotatSpeed);

状态动画的制作

在引擎中操作创建动画的过程请查看:

创建一个方法,子啊游戏唤醒时访问角色的Animator组件。

动画逻辑部分,我们需要做的是在游戏唤醒(开始)后访问角色的动画组件,播放角色待机动画(Idle),当角色运动时切换行走动画(Walk),停下行走重新播放待机动画。

public class NewBehaviourScript : MonoBehaviour
{
    private Animator animator;//这里是申明一个私有的animator变量,类似int a,Animator是unity提供给我们的一种类,动画类。

    
    //创建一个方法,当游戏唤醒时访问Animator组件。
    private void Awake(){
       animator =  GetComponent<Animator>();//访问conmpontent中的Animator组件。       
    }

}

这段代码声明了一个私有的Animator变量,并在Awake方法中通过获取与脚本所在游戏对象相连接的Animator组件来进行初始化。

接着我们需要创建一个方法来判断角色静止时播放Idle动画。

private void Update(){
    animator 中IsWalking为ture时播放行走动画。
}

判断角色是否处于运动状态

如何判断角色是否处于运动状态呢:如果角色中在它移动的方向上的数值不为0,则角色处于行走状态。在前面控制角色移动的脚本中,我们有一个变量moveDir来表示角色运动方向上的值。所以,当moveDir的xyz都不为0时,则表示角色处于运动状态。

moveDir != Vector3.zero;//moveDir != Vector3(0, 0, 0)

moveDir是角色控制器脚本中的变量,需要在同一个脚本中编写。同时我们需要一个变量来存储这个表达式的值为true还是false。所以需要在该脚本中申明一个布尔类型的变量isWalking,当角色移动时isWalking的值为true,否则为false。

private bool isWalking;
isWalking = moveDir != Vector3.zero;

此时,这些代码和判断结果都仍然还在我们的角色控制器脚本中。想要在动画脚本中调用,还需要将判断结果返回(传递)出来。所以需要在判断完成后声明一个新的方法IsWalking()来返回上一步变量isWalking的值(true或false)。

public public IsWalking(){
 return isWalking;
}

在动画脚本中调用角色状态

animator.SetBool("IsWalking", is walking());

只要游戏唤醒后,角色就一直保持待机动画状态,直到行走开始。

private void Update(){
    animator.SetBool("IsWalking", is walking());
}

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注