从创建项目开始
创建一个项目
打开unity,在Projects中可以查看当前的本地项目或者云端项目,点击New project或者右上角的New都可以新建项目。
然后在1处填写创建项目的名称,2处选择创建地址,3处选择Template(模板),可以选中3D或者2D。
稍等片刻即可进入unity的主页面。
保存Scene并管理Assets
进入unity之后,可以看到已经预设的SampleScene和其中的Main Camera(主摄像机)和Directional Light(平行光)组件,如果你不喜欢这些预设的名字,直接点击修改并使用Ctrl+S保存你的修改即可。
为了使我们的Assets文件夹管理地更加井井有条,推荐对不同的assets进行分文件夹管理,这时一些文件夹的命名可以自己定义,但通常可以遵循一定的不成文的规定,比如通常将场景存放在Scenes目录、将脚本存放在Scripts目录等等,要根据自己来管理好Assets目录,以后对于大量的assets可以很方便快捷。
创建游戏场景
创建Plane
可以使用unity的内置GameObject的Plane(平面)类型来作为游戏场景的“地面”。
可以在Hierarchy视窗下右键选择3D Object下找到Plane创建,也可以在顶部菜单栏的GameObject下的3D Object下找到Plane创建。当然也可以看到unity为我们创建了很多预置的Game Object,比如3DO bject、2D Object、Effects、Light、Audio、 Video、UI、Camera等等,当然每一个分类下还有更详细的分类,这些都可以直接拿来使用,非常方便,一些重要的Game Object以后还会慢慢使用。
这里只需要创建一个Plane并将其命名为Ground来作为我们游戏的“地面”,可以看到Plane出现在了MainScene中,同时我们可以注意到Hierarchy视窗中的MainScene右上角出现了一个*号,这就表示该Scene处于待保存的状态,可以通过菜单栏的File->Save或者快捷键Ctrl+S保存Scene。
选中刚刚创建的Ground,Inspector视窗中就会出现其所有的Components,这些都是预先被untiy设置给Plane的,点击Transform这一Component的右侧的齿轮状图标,可以选择Reset(重置)选项,这样,刚刚创建的Plane的Transform就会被重置为初始值,它的Position会被设置为(0,0,0),这是整个游戏世界的原点坐标,游戏中的所有GameObject的坐标都是基于此原点进行计算的。
选中任何一个GameObject,比如选中Ground,然后按F键,或者在菜单栏中点击Edit->Frame Select可以快速地调整Scene的角度,让我们有一个非常合适的角度来观察Ground的全貌。
改变Transform
改变Transform的三组值的方法有很多。
直接赋值
可以在Inspector面板中对Transform的九个值直接输入数值来设置
拖动输入框调节
当我们把鼠标指向每一个值的输入框的左侧边界时,就会发现鼠标成为了一个左右双箭头的形状,此时按下鼠标左键所有拖动,就会发现该输入框变成了蓝色,并可以随着拖动改变它的值。
在Scene窗口中改变
在左上方有六个按钮,分别表示对Scene中GameObject的操作。
这里提一句:不管选中六个按钮中的哪一个,只要按住Alt键在Scene中拖动鼠标就可以转动视角,只要滚动鼠标滚轮即可放大/缩小视角。
这六个按钮从左到右依次为:
- 第一个:拖动Scene的视角
-
第二个:选中后,再选中Scene中的任何一个GameObject,就可以通过拖动它的三个方向箭头(x、y、z)以及三个平面(xy平面、xz平面、yz平面)来改变Position的值。
-
第三个:选中后可以在Scene中对选中的GameObject改变其Rotation的值
-
第四个:选中后在Scene中可以对选中的GameObject进行三个方向的Scale的调整
-
第五个:选中后可以在Scene中对选中的GameObject进行顶点的位置调整从而改变Scale的值
-
第六个:选中后可以同时改变Position、Rotation、Scale的值,是第二三四个的结合。
创建游戏对象
创建Sphere
接下来创建小球,同样地,在MainScene下右键->3D Object->Sphere来创建一个unity预置的Sphere(球体),命名为Player,并通过reset其Transform来使其位置重置到原点。
这样我们看到小球的中心已经被定位到了(0,0,0)处,为了让小球能在平面上滚动,我们需要将小球放到平面上。
观察小球的Transform我们可以得到,它的Scale的值为(1,1,1),也就是说它的三个方向的大小都为1单位,为了让小球放到平面上边,显然我们需要将其向上移动半个球的距离,即将Position的Y值设置为0.5,小球就刚好在平面上了。
关于光源
其实我们可以看到小球是有影子的,这是最开始unity为我们准备的Directional Light作用的结果,我们可以看到Scene中的一个小太阳的标志,这个就是我们的光源,使用Directional Light来模拟太阳的平行光。它的Transform则显示了光源的位置、角度(也就是平行光的照射方向),如果我们将这个GameObject去掉的话就没有了光的效果。
当然通过改变Rotation的值就可以调节光源的方向,比如为了效果我将Rotation的Y值改为60。
创建Material
为了使GameObject美观,我们通常会对其表面进行一系列装饰,而其表面的表现是通过为这个GameObject添加Material(材料)来实现的。
接下来为我们的Ground和Player添加最简单材料:纯颜色。
在Assets下新建Materials目录用于管理各种材料,然后右键该目录选择Create->Material新建一个材料命名为Background。
选中Background,就可以看到它的Inspector面板了,我们在Albedo(反射率)一栏中可以选择一种颜色,在下方的预览中就可以看到效果了,这里我们选择RGB色(0,32,64)作为我们的Background的颜色。
想要将创建的Material运用在某个GameObject上,很简单,只需要拖动该Material到Scene中的目标GameObject上或者拖动到Hierarchy的该GameObject上即可。
可以看到我们的Ground已经变成了蓝色
让小球滚动起来
让小球拥有成为刚体
为了让小球有滚动的效果,我们需要小球拥有一系列的物理属性,物理属性已经由unity内置,我们只需为需要增加物理属性的GameObject添加一个Rigidbody的Component即可。如上一节中所示,选中Player,在Inspector面板中通过Add Component中找到Physics下的Rigidbody即可。
为小球添加控制脚本
有了刚体属性的小球需要在我们的控制下滚动,比如我们规定使用W,S,A,D四个按键来控制小球的方向,那么对于一个有物理属性的刚体来说,为了能够动起来,当然需要力(Force)作用在物体上,这些有关于如何控制GameObject的方法需要我们使用脚本(Scripts)来完成,假如你已经拥有了一定的C#编程基础。
同样我们在Assets下创建Scripts目录来管理脚本,在该目录处右键->Create->C# Script创建一个脚本,这里我们命名为PlayerController。
为了让我们创建的脚本与Player联系起来,可以在Player的Inspector面板下选择Add Component,在其中搜索我们的脚本名字就可以找到该脚本,根据unity的命名规范,喜欢将脚本各个单词使用“驼峰法”并且首字母同样大写的方式,有趣的是,unity对于这些脚本通常都会在每个大写字母处将这些单词分开,我也不知道为什么。简单点儿的话可以直接将Assets中的脚本拖到Inspector面板下,就可以添加成功。
打开脚本
编辑脚本需要编辑器,Visual Studio是较好的选择,它和unity之间有很好的合作关系,使用起来也很方便。双击脚本文件或者在Inspector面板中点击脚本的右上角的齿轮图标选择Edit Scrpit都可以打开编辑器对其进行编辑。
unity已经为我们预置好了脚本的最基本结构,最基本的,我们可以看到所有的unity脚本都继承自MonoBehaviour类,然后有两个预设函数,Start函数是在第一帧开始渲染前调用,Update函数在每一帧刷新前调用,都是非常常用的函数。
接下来思考我们要做的事情,我们需要检测用户的输入,并且通过输入的按键来控制小球的滚动方向,检测用户的输入同时也需要识别输入的是哪一个按键,除此之外我们还需要一些物理学有关的逻辑,比如我们需要添加一个力来控制小球的移动,这就是物理学逻辑,这些逻辑当然是每一帧都要进行一次,所以我们需要将这些逻辑写在每一帧更新都要执行的函数中。
显然我们可以写到Update函数下,因为Update函数是每一帧刷新前都会调用的,同时我们还有另外的选择,即使用FixedUpdate函数,它在每一次进行物理学运算的时候调用,每次检测到用户输入都需要进行物理学运算,所以我们可以将逻辑写到FixedUpdate函数下。
开始编写脚本
首先我们需要创建一个对于这个Player小球的引用,这样才能知道我们控制的是哪个小球,这里小球是刚体,所以我们创建一个刚体(Rigidbody)的引用,并且需要在第一帧开始渲染之前通过GetComponent方法来找到小球创建刚体的引用,这一逻辑自然就需要写到Start函数中了。
private Rigidbody rigidbodyPlayer;
void Start()
{
rigidbodyPlayer = GetComponent<Rigidbody>();
}
接下来对用户输入的读取就需要写到FiexedUpdate函数中了,我们使用Input类的GetAxis方法来获取水平或者垂直的运动轴,这个方法会返回一个float值作为该轴的移动距离。
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
通过以上两行代码我们就可以将用户的W,S,A,D输入转化为水平轴和垂直轴的移动距离,分别存储在moveHorizontal和moveVertical两个float类型的变量中。
接下来通过Rigidbody类的AddForce方法可以为刚体添加作用力,AddForce方法接受一个三维向量(Vector3)参数,这个三维向量就可以表示力,显然我们的三维向量可以用刚才的moveHorizontal和moveVertical两个变量作为X值和Z值,同时我们是不需要小球在Y方向上移动的,也就是将Y方向的力作用设置为0.0f即可,经过调试我们会发现小球的移动速度过慢,为了方便调节小球的速度,只需要在表示力的三维向量前乘以一个倍数即可,为了方便调整,我们设置一个public的float类型的变量speed来调节这个乘积。
这里必须说明的是,凡在unity的脚本中被声明为public类型的变量,在unity的Inspector界面中的该脚本的Component下都会出现一个可以设置的值的方框。
public float speed;
void FixedUpdate()
{
Vector3 movement = new Vector3(moveHorizontal,0.0f,moveVertical);
rigidbodyPlayer.AddForce(movement*speed);
}
此处我们将speed的值设为10比较合理,运行游戏就会发现通过W,S,A,D的控制,小球动了起来。
完整的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Rigidbody rigidbodyPlayer;
public float speed;
void Start()
{
rigidbodyPlayer = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal,0.0f,moveVertical);
rigidbodyPlayer.AddForce(movement*speed);
}
}
当然你也会发现,小球超出了Plane的边界居然掉了下去,其实这是合理的,因为小球为刚体,也就拥有物理引擎,当然受到重力的影响,在没有Plane的向上的作用力的情况下自然会下落。
设置摄像机
可以发现,我们的Camera的角度和位置都比较刁钻,这导致我们的游戏看到的画面并不完整,接下来我们对Camera进行设置,使其能够跟随我们的小球滚动来同时移动。
首先调节Main Camera的Position和Rotation使得画面和角度比较合适,比如这里将Position的Y值设为6,Z值设为-6,将Rotation的X值设为45得到了一个较为合适的位置。
接下来通过脚本控制Main Camera跟随小球Player一同移动,即在Position上保持相对静止。
可能你会想到,只需要将Main Camera拖动给Player使其成为Player的子物体不就可以保持两者相对位置不变化了吗?但是问题在于球体Player是滚动的,如果两者的位置完全相对静止,就会导致球滚动时Main Cmaera也会跟着球滚动,有一种天旋地转的感觉。感兴趣的话可以尝试一下。
新建脚本CameraController并添加给Main Camera做一个Component。为了使Main Camera的Transform的Position和Player的保持相对静止,Rotation并不和其保持一致,可以想到一个办法:设置一个偏移量,这个值初始化为游戏开始时Main Camera和Player之间的Position的差值,然后在球滚动时,每一次滚动都改变Main Cmaera的Position,使其新的Position等于现在球的Position的值加上刚才的偏移量,这样就会在每次球的位置改变时Main Camera都会跟上它的步骤。
显然,偏移量的设置需要在Start函数中完成,每一次球的位置发生变化时的逻辑可以在Update函数中完成,但还有一个更好的选择,就是LateUpdate函数,该函数在每次有GameObject发生变动时才会调用。
同时,我们的脚本使加在Main Camera上的,所以Main Camera的Transform可以直接调用,但是球的Transform则需要单独获取,这里我们设置一个public的GameObject量,然后在unity中将球Player拖动到这个量处作为参数即可。
private Vector3 offset;
public GameObject player;
void Start()
{
offset = transform.position - player.transform.position;
}
void LateUpdate()
{
transform.position = player.transform.position + offset;
}
这时候运行游戏就可以发现Main Camera的位置随着球的改变而发生了改变。
完整的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
private Vector3 offset;
public GameObject player;
void Start()
{
offset = transform.position - player.transform.position;
}
void LateUpdate()
{
transform.position = player.transform.position + offset;
}
}
建立围墙
为了不让小球总是滚落到Plane之外,可以在其四周围建立起一圈儿围墙,很简单,使用预设的Cube就可以了。
为了更好的管理四个围墙,我们可以先创建一个空的GameObject,将其命名为Walls,Reset使其重置位置,然后在其上右键新建一个Cube,并命名为WestWall,这样这个Cube就成为了Walls的子物体。
接下来如何调整这面墙的大小、位置就很简单了,可以在Inspector中直接输入具体数值,也可以直接在Scene中拖动和缩放,最好我们可以得到这个墙体结构。
为了简单方便,只要选中WestWall,在菜单栏的Edit中选择Duplicate(复制)(快捷键:Ctrl+D)即可,然后将复制好的墙拖动到适合的地点,在复制,最后可以得到四面墙。
接下来测试游戏,墙起作用了!
下一篇文章:https://www.jianshu.com/p/7e0e85092e5a
参考资料:https://unity3d.com/learn/tutorials/s/roll-ball-tutorial
本节内容的完整官方教程视频分享(英文无字幕):https://pan.baidu.com/s/1TNMMFjs7pZqLPhGQZ1tPqQ 提取码:1u70
转载请注明出处,本文永久更新链接:https://blogs.littlegenius.xin/2019/02/10/从滚球游戏初步体验Unity3D/