白帽故事 · 2025年2月3日

Unity游戏开发学习Day1/2

Visual Studio 2022 开启AI智能提示

右上角搜索,输入”intellicode“,然后在“常规”中,勾选“整行完成”即可:

image.png

【良好的编码习惯】函数封装

比如选中某一行代码,可以使用快捷键Ctrl+.,或者右键选择“快速操作与重构”:

image.png

选择“提取方法”:

image.png

然后命名(示例为Movement)即可。

image.png

碰撞检测

为了防止“无限跳越”的情况发生,因此需要设置碰撞检测。
首先,选择“Platform”组件,在其属性中的layer中,增加一个layer,起名为“Ground”:

image.png

然后在Layer中选择新建后的”Ground“图层,接着需要检测Player的位置以确定”地面“的位置,然后判断只有接触地面的时候,Player才可以跳跃,需要在C#脚本中实现。

首先创建一个地面半径检测函数public float GroundcheckRadius;,然后创建OnDrawGizmos,来绘制检测区域,使用线球体(Gizmos.DrawWireSphere),参数为中心和半径(中心为Transform的位置,半径则为GroundcheckRadius):

 private void OnDrawGizmos()
 {
     Gizmos.DrawWireSphere(transform.position, GroundcheckRadius);
 }

此时回到Unity的界面,Player的属性中就会出现GroundcheckRadius的属性,通过输入数值或拖动,可以调整检测区域(但此时的检测区域只能以Player为中心进行缩小和放大,无法对区域进行拖动):

image.png

由于需要设置检测区域为Player组件的正下方(即角色的脚底),因此首先要在Player下方创建一个Empty Object,命名为GroundCheck,然后在C#代码中修改:

public Transform GroundCheck;
 private void OnDrawGizmos()
 {
    Gizmos.DrawWireSphere(GroundCheck.position, GroundcheckRadius);
 }

此时回到Unity界面,在右侧属性中,将GroundCheck拖动至Player的GroundCheck,然后选择GroundCheck组件,就可以对检测区域进行任意拖动了:

image.png

设置好检测区域后,还需要在C#中编写代码来进行碰撞检测:

private void collisionCheck()
{
    groundDetected = Physics2D.OverlapCircle(groundCheck.position,groundCheckRadius);
}

然后要记得在Update中调用碰撞检测collisionCheck。此时回到Unity界面,播放查看:

image.png

可以看到Ground Detected被成功检测到,但是如果将Player组件挪动,离开地面时,会发现Ground Detected依然是勾选状态的。这是因为它检测自身引起的,因此还需要对C#代码进行完善,通过创建一个层变量来解决该问题:

public LayerMask whatIsGround;
  private void collisionCheck()
  {
      groundDetected = Physics2D.OverlapCircle(groundCheck.position,groundCheckRadius,whatIsGround);
      }

然后在Unity界面中,将whatIsGround设置为Ground:

image.png

再次播放,当挪动Player组件离开地面时,Ground Detected问题解决。最后就是增加一个判断,当检测到碰撞地面时才可以跳跃即可。

 private void Jump()
 {
     if(groundDetected)
         rb.velocity = new Vector2(rb.velocity.x, jumpForce);
 }

连续(浮空)跳跃问题获得解决。

【良好的编码习惯】‘分割线’

通过使用[Header("")]可将某个特性进行‘归类’,从而在属性窗口增加其易读性:

[Header("Collision Check")]
public float groundCheckRadius;
public Transform groundCheck;
public bool groundDetected;
public LayerMask whatIsGround;

image.png

切片

在导入资源后,选中要切片的资源,在Sprite Mode中选择“Multiple”,然后点击Sprite Editor,在切片类型中,选择“Grid By Cell Size”,然后输入Pixel Size为16*16,点击Apply即可:

image.png

此时将Sprite拖入场景后,发现是比较小的,而且是比较模糊的,然后再次选择素材的Sprite,在Pixels Per Unit中将值改为16:
image.png

然后在Compression中选择None,在Filter Mode中选择Point(no filter),就可以解决模糊问题:

image.png

动画制作

首先在Assets文件中新建一个文件夹,起名为Animation,然后在Animation文件中创建Animator Controller,起名为Player_AC,然后点击上方的Player组件,将Player_AC拖入右侧的属性窗口,就可以在属性窗口中随时调整Animator的属性。

image.png

接着打开Windows-Animator和Animation窗口,将导入资源中的素材2(2个图片)拖入Animation窗口,然后播放时会发现人物的眨眼动作过于快速,因此需要将采样率降低,通过点击Animation窗口右上角的按钮,将Samples显示出来:

image.png

然后在左侧Samples的值从60降低为4,再次播放就会发现眨眼动作变慢了。这一步是将人物静止时的动作做好了,接下来做人物在走动时的动画。

同样在Animation窗口左侧,新建(Create New Clip..),起名为playerMove,然后将导入资源中的走路的两张图片拖入Animation窗口,同样调整Samples采样率为6左右。
image.png

然后切换到Animator窗口,在playeridle按钮上点击右键,选择创建过渡(Make Transition),将箭头拖入playerMove,在左侧Parameters中,点击+号,新建一个Bool,起名为isMoving来判断人物是否在行走中:

image.png

同样的步骤,在playerMove上点右键,创建过渡,将箭头拖入playeridle,然后点击箭头后,在右侧属性窗口,将isMoving选择为false(同理,另一个箭头的isMoving选择为true)。
image.png

此时,回到场景(Scene),点击播放,通过右侧属性窗口,勾选isMoving,如果一切正确,就可以看到人物会在眨眼和走路动作中切换。

最后需要在C#代码中,将移动动作判定实现:

首先创建一个Animator的对象,并在Start中获取该组件:

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

然后封装一个AnimationController的方法,增加判断(判断人物的X坐标是否为0,如果为0则表示人物为静止状态):


  private void AnimationControllers()
  {
      bool isMoving = rb.velocity.x != 0;
      anim.SetBool("isMoving", isMoving);
  }

Blend Tree(混合树)

首先在Animation窗口中,创建两个(Create New Clip..),一个名为playerJump,一个名为playerFall,分别拖入导入素材中Jump和Fall图片,然后在Animator窗口中,可以将playerJump和playerFall的按钮删除,然后创建一个Blend Tree(From New Blend Tree),起名为“Jump/Fall”:

image.png

然后双击“Jump/Fall”按钮,将左侧的Blend更名为yVelocity,然后在右侧属性窗口,添加两个Motion Field:

image.png

分别为playerFall和playerJump,然后在Threshold中分别设置-1和1来表示人物的跌落和跳跃,最后在代码中增加yVelocity来控制人物的跌落和跳跃:

  private void AnimationControllers()
  {
      bool isMoving = rb.velocity.x != 0;
      anim.SetBool("isMoving", isMoving);
      anim.SetFloat("yVelocity",rb.velocity.y);
  }

回到Animator窗口,右侧创建一个bool,起名为“isGrounded”,用于判断是否接触地面,然后在playeridle按钮中创建过渡至Jump/Fall,并在右侧属性中将过渡持续时间调为0:

image.png

同样,再创建一个返回的过渡。但这样完成后有一个小问题,就是人物可以从空闲状态中切换至Jump/Fall状态,但无法从移动(Move)状态切换至Jump/Fall状态:
image.png

设置完过渡后,还要记得在C#代码中增加isGrounded的设置:

  private void AnimationControllers()
  {
      bool isMoving = rb.velocity.x != 0;
      anim.SetBool("isMoving", isMoving);
      anim.SetFloat("yVelocity",rb.velocity.y);
      anim.SetBool("isGrounded",isGrounded);
  }

接着来解决无法从移动(Move)状态切换至Jump/Fall状态的问题,一种方法是,删除人物从空闲状态到Jump/Fall状态的过渡,转为从Any State状态到Jump/Fall状态的过渡:

image.png

(推荐)另一种更加简洁的方法是,删除playeridle和playerMove的按钮,重新创建一个新的Blend Tree,起名为Idle/Move,左侧窗口创建一个float,起名为xVelocity:

image.png

然后双击Idle/Move按钮,在右侧属性窗口,首先将Parameter设置为刚刚新建的xVelocity,然后创建3个Motion Field,并分别设置为playerMove、playerIdle、playerMove,并将Threshold设置为0、-1、1:

image.png

接着回到Base Layer层,选中Idle/Move按钮,右键设置该层为默认层:

image.png

删除Any State与Jump/Fall的过渡,增加Idle/Move与Jump/Fall的过渡,同样要记得修改过渡的持续时间以及isGrounded的Bool值:

image.png

最后在C#代码中进行调整,调整后的代码则更加简洁明了:

 private void AnimationControllers()
 {
     anim.SetFloat("xVelocity", rb.velocity.x); 
     anim.SetFloat("yVelocity",rb.velocity.y);
     anim.SetBool("isGrounded",isGrounded);
 }

回到Unity界面,点击播放,如果一切正常,人物在空闲状态时,会眨眼睛,在移动时会切换走路状态,而在跳跃时,则会有跳跃和跌落状态的切换。

动画.gif