①前言
本项目的完整开发流程在Unity官方项目的教学文档中编写了详细的教程文档,但是因为官方文档距离现在已经有一段时间了,个别地方有错误或者读者在使用自己机器上的Unity 编辑器进行操作的过程中,仍然遇到了零零散散的问题,无法再和官方教程相对应,本博客是对官网提供的项目的再补充以及个人心得复盘和经验分享。
本系列博客是来源于官方教程但是最终绝对会高于官方教程,让读者综合官方教程以及我自己的升华,能够独立开发出一个比官方教程更加完整可行的游戏。
最终的开发结果,使用资源也会开源在GitHub以及Gitee。
②官方教程地址
【官方文档 —— 1、设置 Unity 编辑器】
【官方文档 —— 2、主角和第一个脚本】
【官方文档 —— 3、角色控制器和键盘输入】
③如何处理本系列博客和官方教程之间的关系
个人建议是先大致浏览本系列专栏的博客,对注意事项和要点有个初步印象,再跟着官方文档逐步操作
因为本系列博客是对官方教程的修订,补充,升华,而且本系列博客的内容篇幅不会特别冗长,完整看完大概需要5 — 7 分钟。
假如只看文章的目录,留意需要订正的地方,那么大致只是需要1分钟左右。
在官方教程中,此处在创建新场景之后并没有介绍如何将相机挂接上去,故进行一定程度的补充。
现阶段其实也可以直接使用创建2D项目之后,自带的一个SampleScene开展后续的操作。
在创建好的MainScene场景中,进行如下操作的补充:
① 创建一个新的空的游戏对象
Create Empty 在Unity 编辑器中表示创建一个空的游戏对象。
名场面——面向对象学习
② 添加摄像机组件
③ 设置添加的Camera组件上的属性
主要是将Inspector中清楚标志的Clear Flags 调整为纯色Solid Color
,以及将控制投影的Projection调整为正字形Orthographic
,其他的可以保持默认。
其他内容以及步骤可以继续跟着教程做啦
在官方提供的Unity教程中,对各个Unity中的API 阐述比较模糊,教程的目的更多是想让大家快速做出成品,至于为什么需要调用这些API并未详细说明。
因为我自己已经跟着教程初步做成完这个项目,现在处于复盘和再认知阶段,我会尽可能详细的阐介绍代码中使用这些类,函数,变量的作用,做到知其然也能知其所以然。
① 用于表示 2D 向量和点 的 Vector2
类
脚本 API —— Vector2
在2D场景中,可以使用该结构表示位置和向量。同理,当前的场景是3D的时候,就需要使Vector3。
本次项目中,主要是使用它的用于存储X分量的变量x 和 用于存储Y分量的变量y来存储在2D坐标下获得的位置信息。
② 用于描述对象的位置、旋转和缩放的Transform类
脚本 API —— Transform
在Unity 编辑器中,Inspector窗口中会显示加入场景中的游戏对象会默认添加一个Transform组件。
在Unity提供的库中,也设置了Transform类用于控制该组件,对于本案例而言,就使用了其中的position变量。
在教程上面的内容中已经写到,在Unity 编辑器中 Edit -> Project Settings窗口中的 Input Manager 页面列出所有玩家能够进行输入的控件。
同时Unity提供的API中,也设置了相应的Input类
用于操控编辑器中的这个用于检测用户输入的功能
③ 用于访问输入系统的接口,Input类
脚本API —— Input
在编辑器中,能够进行检测玩家输入的Axes目录下,在Input类中,也因此调用GetAxis类来检测玩家是否使用Horizontal以及Vertical列表
下罗列的按键进行输入操作。
按照教程文档中解释的,倘若直接把计算进行上下左右控制的功能写到一帧调用一次的Update函数中,再按照上述的计算逻辑,倘若有小伙伴计算机性能更加优秀比如一秒800帧,另一位小伙伴计算机性能较为一般,比如一秒30帧,在相同的时间中机器性能好的小伙伴已经进行800次位置计算了,而另一个,就蛮惨了。
下图是官方教程文档中的原话,可能有小伙伴读起来会比较拗口和难理解。
其大致思想是这种的
:
首先,我们检测进行按键输入的功能是放在Update函数中的,该函数每一帧调用次。
其次,不同机器的帧率是不同,比如有些1秒10帧,那么一帧花费1/10秒;有些1秒60帧,那么每帧会花费1/60秒。
那么,无论不同的机器是多少帧的,最后计算的都是1秒 * 速度而产生的距离。
因为Update按照帧调用,60帧就是调用60次,每帧计算的距离是速度 * 每帧的间隔时间(Time.deltaTime),
总的计算式就是 60 * 速度 * 1/60 = 速度 * 1秒
④ 从 Unity 获取时间信息的接口,Time类
脚本API —— Time
使用其静态变量deltaTime计算从上一帧到当前帧的时间间隔
可以解决上述问题。
那么官方文档中推荐的代码我就不放这里了,相信大家已经跟着敲了。现在我们要完善文档中,当同时摁下垂直和水平的控制按键,出现速度叠加的情况。
关于速度叠加的原因,可以参考这篇文章的部分内容:
一个开发者的自学之路(7)——Unity3d中角色的移动
为了避免出现这种速度叠加的情况,较为统一的解决方法是使用标准化
//游戏角色的移动速度,开放为public,可以在Unity编辑器中进行设置
public float speed =4.0f;
//声明一个2D的刚体变量,用于存放游戏角色对象
Rigidbody2D _rigidbody2d;
//水平输入
float _inputX;
//垂直输入
float _inputY;
// Start is called before the first frame update
void Start()
{
//返回Rigidbody2D类型的组件
//务必已经在Ruby游戏对象上挂接Rigidbody2D组件
_rigidbody2d = GetComponent();
}
// Update is called once per frame
void Update()
{
/*
* GetAxisRaw不使用平滑滤波器
* 就是说GetAxis可以返回-1到1之间的数,比如检测到按键按下的时候,由0不断增加过渡到1
* GetAxisRaw只能返回-1,0,1三个值!
*/
_inputX = Input.GetAxisRaw("Horizontal");
_inputY = Input.GetAxisRaw("Vertical");
Vector2 input = new Vector2(_inputX, _inputY).normalized;
_rigidbody2d.velocity = input * speed;
}
⑤ 用于 2D 精灵的刚体物理组件,Rigidbody2D
脚本API —— Rigidbody2D
向精灵添加 Rigidbody2D 组件后,其运动将受到物理引擎的控制。这意味着精灵将受到重力的影响,并且可以在脚本中使用力对其进行控制。通过添加适当的碰撞体组件,精灵还能响应与其他精灵的碰撞。
Rigidbody2D 类提供与 Rigidbody 类基本相同的功能,两者的区别在于:Rigidbody2D 用于 2D 环境,Rigidbody 用于 3D 环境。
⑥ 用于返回挂接Rigidbody2D 组件的游戏对象,GetComponent
_rigidbody2d = GetComponent<Rigidbody2D>();
对于此行代码,当我在游戏角色Ruby上挂着了Rigidbody2D 脚本,在Ruby运行该脚本,调用Start函数之后,就会返回Ruby游戏对象。
上述代码存在一个与C#不同的坑,看到尖括号<>的第一反应可能是泛型,但是泛型是用在定义通用类中,而此处的Getcomponent是一个公共函数
参加文献 —— C#泛型(Generic)
⑦ 表示 2D 向量和点 的 Vector2类的构造器 以及 变量
Vector2 —— Vector2构造器
构造器会使用传入的参数产生一个新的向量,normalized则是让向量的长度最大只能为1,通过这个限制,就能够解决速度叠加问题。
⑧ 表示刚体的速度矢量,即刚体位置的变化率,velocity
脚本API —— Rigidbody.velocity
① 清楚游戏角色对象移动的本质是位置的改变,因此我们应该围绕获取游戏对象的位置,修改其位置开展代码编写来实现角色移动。
②掌握按照速度*时间
的方式来产生移动,避免出现因为帧率而产生不同的运行效果。
③ 基本知晓本篇文章中总结的8个经常在角色移动中经常涉及的类、方法、变量