胡闹厨房Unity小白学习篇|第二期
大概第一期算是了解了Unity开发的软件和资料导入后,下面算进入正式的设计开发阶段。原本以为C#的Unity开发比较麻烦,但却没想到其实Unity的脚本开发并不复杂,而且看上去添加插件的方式跟MVVM的底层思路是一样的,就是控制和显示分开,脚本或者材料的渲染加载交给Unity的渲染管线,开发者只关注逻辑层面的东西,简单高效。
下面是学习记录的第二期,萌萌的小黄鸡来了。
第二期| 角色控制
本文内容包含:
角色导入
素材中给出的角色材料是一只小黄鸡,不过设计的比较简单,就是上面一个黄色的脑袋,下面一个比脑袋大的黄色身体,脑袋上面有两个小眼睛。不过越简单对于新手来说越是友好,不至于控制起来脑袋和身体不同步。
素材的位置在_Assets/prefabsVisuals文件夹中
输入系统
视频中考虑新手的基础C#语言,所以采用步进式的教学方式,所以角色的移动开始使用的是最原始的WSAD控制步进量的方式。当然这种方式便于理解,但是缺点就是在角色控制的脚本文件里,程序代码显得非常冗余,而且当有其他的物品需要移动的时候需要重复编写输入控制代码,所以视频后面引入了Unity的输入系统,这样就方便多了,可以在不同类中引入控制类,简化了代码。
以下就采用直接使用Input类的新方式,来控制小黄鸡的移动。
- 第一步
:要使用新的Input System 系统,首先需要在Unity窗口-包管理器中安装InputSystem,如下图所示。安装完成后会弹出一个确认按钮,是否启用新的输入系统,这里可以点击是。
在上一步的确认按钮那里,如果选择了否,然后找到编辑-项目选项-玩家-配置-活动处理 这里选择同时启用新旧两种输入系统,视频教程里面也是这样操作的。
- 第二步
:添加新的PlayerActions,在Assets文件夹中右键点击新建-PlayerActions
- 第三步
:设置输入模式,在这里我输入了三组:WASD、上下左右箭头,还有我的手柄上下左右键。不用安装驱动等额外费力的操作,只要插上手柄后能够识别出按键,手柄的移动绑定还是比较好操作的。
- 第四步
:生成Actions的C#类,视频中没有直接使用添加input控制组件的方式,而是将刚才新建的PlayerActions生成了C#脚本类,对于程序开发来说,还是比较好理解。
- 第五步
:添加Input脚本控制,添加一个GameInput的脚本,在全局变量中声明一个PlayerInputActions类型的全局变量inputActions,并在Awake函数中初始化该变量,同时启用刚才设置的Player按键组合。
private PlayerInputActions inputActions;
private void Awake()
{
inputActions = new PlayerInputActions();
inputActions.Player.Enable();
}
- 第六步
:定义一个GetGameInputVectorNormalized的函数,用来对外部输出输入系统的值,inputActions.Player.Move是之前我们定义的变量名,通过获取一个Vector2值,然后归一化后返回外部调用。注意的是我下面函数中注释的是原来的输入调用函数,显得比较麻烦。
public Vector2 GetGameInputVectorNormalized()
{
Vector2 inputVictor = inputActions.Player.Move.ReadValue<Vector2>();
//if (Input.GetKey(KeyCode.W))
//{
// inputVictor.y = +1;
//}
//if (Input.GetKey(KeyCode.S))
//{
// inputVictor.y = -1;
//}
//if (Input.GetKey(KeyCode.D))
//{
// inputVictor.x = +1;
//}
//if (Input.GetKey(KeyCode.A))
//{
// inputVictor.x = -1;
//}
inputVictor = inputVictor.normalized;
return inputVictor;
}
- 第七步
:新建一个空的Object,然后将刚才编辑的脚本拖入Object。同时新建一个Player的脚本,拖入刚才的小鸡Object中。
- 第八步
:在Player类中声明一个开发者可以使用的GameObject变量,并编辑移动的处理函数。在开头部分还定义了一个控制移动速度的moveSpeed变量,用来控制移动速度,防止小鸡飞出去。
[SerializeField] private GameInput gameInput;
[SerializeField] private float moveSpeed = 7f;
private void HandleMoveMent()
{
Vector2 inputVictor = gameInput.GetGameInputVectorNormalized();
Vector3 moveDir = new Vector3(inputVictor.x, 0f, inputVictor.y);
float moveDistance = moveSpeed * Time.deltaTime;
float playerRadius = 0.7f;
float playerHeight = 2f;
bool canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDir, moveDistance);
if (!canMove)
{
// 只移动x方向检测是否可以移动
moveDir = new Vector3(inputVictor.x, 0f, 0f).normalized;
canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDir, moveDistance);
if (canMove)
{
transform.position += moveDistance * moveDir;
}
else // 仅仅移动x方向也无法移动 则检测z方向
{
moveDir = new Vector3(0f, 0f, inputVictor.y).normalized;
canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDir, moveDistance);
if (canMove)
{
transform.position += moveDistance * moveDir;
}
}
}
else
{
transform.position += moveDistance * moveDir;
}
}
函数中首先从gameInput获取输入的Vetctor2数据,然后因为操作都是二维xz平面,所以将y坐标的方向之灵,变为Vector3数据,同时将全局变量中定义的moveSpeed引入控制速度。
函数中定义了一个bool类型的变量canMove,是后面用来进行射线检测,前方是否存在不能穿过的物体的。
在可移动的情况下又通过分别单检测x方向或者z方向,用来解决在前方存在物体时,移动左前或者右前仍然可以往左侧或者右侧移动。