项目 0:滚动的小球(未完)

[\[原版教程\]](http://unity3d.com/learn/tutorials/projects/roll-a-ball)

##项目 0:滚动的小球(roll a ball)

>   通过第一次接触Unity开发,创建一个简单的滚球游戏来讲解不少物体的工作方式:GameObject,Component,Prefabs和Scripting。
    
*   章节 1
    1. 简介
    2. 设置游戏
    3. 移动角色
    4. 移动摄像机
    5. 创建拾取物体
    6. 收集 & 计数
    7. 显示文本
    8. 发布游戏


1. 简介(introdution)
----

>   一个滚球项目的介绍,展示最终游戏并讲解将在这个任务里将被涉及到的东西。

在这个基础任务(assignment)里,我们将做一个非常简单的可玩的游戏,在其中运用不少的基础概念。

在游戏里会我们会收集一些特殊的游戏物体(game object),并看到如何创建一个新的拥有组件(component)的游戏物体,设置它在场景(scene)里它们的位置(position)属性(property)值(value)。

玩家将控制一个小球在板上滚动,通过键盘输入来对小球施加物理力(physics force)并产生移动。

我们会看到小球和拾取物体之间发生联系,并通过这些事件来收集物体,同时显示(display)当前收集物体数量在屏幕(screen)上。

在这个项目里我们不需要导入(import)任何资源(asset)。不使用模型(module)、纹理(texture)、声音(sound)以及动画(animation),仅仅会用到Unity提供的原始形状(primitive shape),比如:正方体(cube),球体(sphere)和平面(plane)。


2. 设置游戏(setting up the game)
----

>   创建一个新的工程以及设置游戏的基础。

让我们开始吧。

首先,咱们创建一个新的项目(project)。菜单栏:`file(文件) - new project(新项目)`。

这将会出现一个创建新项目的向导(wizzard)。

咱们的第一件事是要给这个项目设置目标地址(destination)或路径(path),比如放在桌面(desktop)上。

创建一个新文件夹接着给它命名roll a ball,最后点击create按钮,第一步到此完成。

---

在给新项目添加任何东西之前,先保存一下场景(scene)。菜单栏:`file - save scene`,也可以用快捷键:`ctrl + s`。

我现在把它保存到默认的assets目录(directory)里的一个新文件夹(folder)`_scenes`里面,下划线`_`(underscore)是可选的,但可以让这个文件夹自动排列到项目视图(project view)窗口顶端。

最后在保存之前,咱们可以顺便命名一下场景,比如:mini-game。

---

现在,咱们给游戏创建一个游戏板(game board)或者说球场(play field),其实它就是一个Unity的平面(plane)。

可以通过两种方式来创建它,菜单栏:`game object - create other - plane`,或层次视图(hierarchy view)的create菜单。

重命名这个游戏物体(game obejct)为ground。同样可以有多种方式:`右键菜单 - rename`或`f2`——输入新名字——`enter`或`esc`完成命名。

重置(reset)transform组件,使游戏物体位置在场景的(0,0,0)点上。该组件右上角:`上下文相关齿轮菜单(context sensitive gear menu) - reset`。

        这一点被视为世界的原点(origin),而且是所有场景开始计算坐标(coordinate)的一点。

现在,当游戏物体处于被选中状态,将光标(cursor)移到场景视图(scene view)上,按下`f`或菜单栏:`edit - frame selected(选中框架)`,这会让我们整个的(entire)看见游戏物体在场景视图内。

接着,调整ground的缩放(scale)作为大小。一样有多种方式:`拖动缩放工具轴柄(axis handle)`或者`改变scale相应字段(field)的值`。

        注意,一个平面并没有体积(volume),所以缩放并不会在y轴(y axis)上生效,除非是一个负值(反转效果)。
    
这里的平面只是个单面物体,这会反转平面朝向另一边,以至于你会看不见它。
    
所以要注意平面和照相机(camera),确定你已经使用正确的y轴缩放值,通常就是1。

---

好,咱们来创建玩家物体吧。

在这个任务里,我们的玩家物体就是个球。

`hierarchy - create - sphere - rename 'player' - reset transform - select object(选中物体)- edit - frame selected(使场景视图的摄像机对焦)`。

我们发现,球体被隐藏在平面中。

这是因为这两个物体在相同的位置(0,0,0)上,因此我们需要上移球体直到它所有部分都在平面之上。

        在Unity提供的基本物体方体、球体,胶囊体等都有一个标准的大小(size)
        要么是(1,1,1),要么就是(1,2,1)个基本单位。

因此,也可以通过设置半个单位在y轴坐标(position)上来解决。

如果我们现在查看游戏视图(game view),就会发现它非常的黑暗,这是因为在场景里还没有光线(light)。

加入一个定向灯光(directional light)物体到场景内,操作同上,命名为main light,设置下x轴的旋转(rotation)为30度,y轴旋转为-30度。

现在,场景就亮敞啦。

可是,玩家在平面上的位置比较不太明确。不过,阴影(shadow)可以有助于让这更加明了。

我们设置一下主光源产生的阴影类型(shadow type):`shadow type - soft shadows`。

再设置一下分辨率(resolution):`resolution - very high resolution`。

但现在有点难以看清球体与阴影之间的分割。

我们再增加一个定向灯光,命名为fill light(补光灯),设置transform的rotation为y:-90,也就是让这个灯光从下往上照。

稍稍让颜色值(color value)有点蓝,降低强度(intensity)为0.1(zero point one),最后确认阴影关闭:`shadow type - no shadows`。

这就可以作为(act as)一个温和的补光灯(gentle fill light)来明显区分开球体与阴影。

---

让我们来看看现在层次视图里的结构吧。

呃,仅仅是五个物体就已经让整个视图有点扑朔迷离(confusing)并且凌乱了。

        结构(organization)在项目以及层次视图里都非常的重要,我们必须能一眼(at a glance)就理解整个结构。
        比如现在我们组织我们的项目通过在操作系统(operation system)创建的文件夹或者说目录。

同样的,在层次视图里也可以通过游戏物体来进行组织。

        在场景里,游戏物体可以包含其他的游戏物体,就像文件夹一样。

不需要害怕在层次视图中使用空物体(empty game object)来当作文件夹或是目录。

接下来我们这么来做,菜单栏:`game object - create empty`,或是用快捷键`ctrl + shift + n`。

命名为lighting,并重置transform(这相当重要,易于它的子物体transform的设置),最后把fill light和main light选中拖入变成lighting的子物体。

可以在场景视图中发现,变成子物体后看上去并没有使物体发生变化,它们的图标依然堆在(0,0,0)原点上。

        注意一下,定向灯光的方向取决于它的旋转属性并非位置属性。

因此,我们可提高它们的父物体lighting的y坐标,这样就方便我们在场景视图里对其他物体进行编辑啦,并且对效果没有任何影响。


3. 移动玩家(moving the player)
----

现在我们要移动玩家游戏物体。

首先,我们来想一下玩家如何行动(behave)。

我们希望球体能滚动在游戏区域里,能碰撞墙壁,能停在地面上,而且不会飞到在空白空间里。

我们希望它能与收集物体碰撞,然后在我们发生这个物理要求时把它们拾起。

为了使用物理学(physics),游戏物体需要附加一个刚体(rigid body)组件(component)。

而添加组件需要先选中要添加组件的游戏物体,在这里我们选中玩家球体,给它添加物理组件。菜单来:`component - physics - rigid body`,或者在属性面板(inspector view):`add component - physics - rigid body`。

        我们可以改变组件们的排列顺序,在每个组件的右上角齿轮菜单进行操作。
        改变顺序并不会影响游戏的性能,然而却可以有一个连贯的顺序,容易加快开发速度并保持维护(maintaining)和管理我们项目。

        不要忘记你可以折叠或展开(collapse or expand)组件信息,只要单击它的标题栏。
        不过我们也要知道,每次切换(toggle)时,场景视图中相应的线框(gizmo)也会变化。


现在,我们来让玩家物体通过我们的控制进行移动。

在这之前,还得先获得玩家在键盘(keyboard)上的输入(input),根据输入对玩家物体施力来让球体在场景中移动。

我们可以通过给玩家物体添加一个脚本(script)来做到这些。

首先,我们在项目视图(project view)里建立一个文件夹,用来存放脚本资源:`project view - create - folder - rename 'scripts'`。

接着再创建一个C#脚本,通过之前的方法或者,菜单栏:`asset - create - c# script`。

但在这里最高效的方法是:选中玩家物体,`inspector - add component - new script - rename 'PlayerController' - language 'c sharp' - create and add`。一步到位,直接就创建并添加到游戏物体上了。

        不过,这样会直接将脚本资源创建在项目视图的根(root)目录里,需要手动拖动到scripts文件夹中。

---

当我们在项目视图里选中一个脚本时,属性视图(inspector)会出现脚本预览,但这段文本还不能被编辑。

我们点击open按钮打开它。

然后出现了首选的(preferred)脚本编辑器。

首先,我们去除掉基础脚本提供的样本代码(就是Start和Update两个空函数以及注释)。

接着,想想我们想做些什么。

我们希望每一帧里,检查玩家输入,并将输入作为玩家物体的运动。

不过哪里可以检查和应用输入呢?

两个选择,Update和FixedUpdate。

*   Update:被调用在每次渲染一帧之前,基本大部分的代码都放在这里。

*   FixedUpdate:仅仅是被调用于执行物理计算时,物理代码放这里。

我们将会移动小球通过对刚体施力,这是物理学的,所以我们选择FixedUpdate函数。

我们该编写什么代码?

---

我们已经知道需要在FixedUpdate函数主体里写一个`Input`,但接着呢?

在MonoDevelop编辑器里有一个快捷键用于搜索Unity的API(应用程序编程接口)。
```
    Mac:cmd + '
    PC:ctrl + '
```
选中需要搜索的目标,这里是`Input`,按下组合快捷键,转到了Input类的页面。

我们通过这个类来读取在input manager(输入管理器)里设置的Axes(轴),以及管理在移动设备(mobile devise)上的multi-touch(多点触控)和accelerometer data(加速器数据)。

在说明(description)下方是一个列表显示类变量和类函数。

*   类变量存有信息,比如:touchCount里有当前触点总数。
    或是存有一个引用(reference),比如:gyro就是一个用于访问gyroscope(陀螺仪)的引用。

*   类函数为我们做一些事情。

在这里我们需要用到Input.GetAxis函数,点击链接,转到相应类成员信息页面。

我们来看看Input.GetAxis页面。

这个页面包含了函数签名(signature),函数描述以及示例代码片段(code snippets)。

        在Unity有3中脚本:JavaScript、C#和Boo。

这里我们选择C#。

我们对Input.GetAxis的用途与示例代码片段相似(similar)。

---

好,返回编辑器开始编辑代码。

=(equal)
```csharp
    void FixedUpdate(){
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
    }
```

这会取得玩家的键盘输入。

浮点变量(float variable)水平移动(move horizontal)和垂直移动(move vertical)会记录通过键盘按键控制的水平轴和垂直轴的输入。

---

我们的玩家物体使用刚体组件与物理引擎关联,而我们使用这些输入给刚体施力来移动物体。

关于如何对刚体(rigid body)施力(add force),我们来查询一下有关rigidbody的相应的信息吧。

在编辑器输入rigidbody并选中,按下快捷键转到rigidbody类页面。

查看一下rigidbody类提供方法,这里我们点击AddForce函数。

我们能看到内容顶部的签名(signature)显示需要给AddForce函数提供一个Vector3类型和一个ForceMode(力模式)类型的参数,后者是可选的。

在描述的下方我们能看到相应的演示代码片段,再下面是第二个方法的签名,提示需要3个float变量对应xyz轴以及可选的ForceMode参数。

我们选择简单点的第一个,只提供一个Vector3变量,忽略ForceMode。

如何把float类型的输入变量变成Vector3类型的方法参数呢?

首先我们添加一个Vector3变量叫做movement(移动),等于,new Vector3,这里我们提供moveHorizontal和moveVertical给对应的xz参数,y参数填0,因为并不需要y轴上的移动。

接着,调用rigidbody的AddForce函数,传入movement作为参数。

```csharp
    Vector3 movement = new Vector3(moveHorizontal, 0, moveVertical);
    rigidbody.AddForce(movement);
```

保存脚本,回到Unity。

---

我们检查一下错误,是否出现在Console(控制台)面板,确定没有。

很好,接下来进行测试,查看脚本效果。

点击play按钮,通过键盘输入对小球进行控制,有效果,不过好像很慢,相当没劲。

但是,方法对了不是么。

停止测试,离开play模式。

我们返回脚本编辑器,增加一个能控制小球速度的东西。

---

我们可以让movement乘以一个数值,放大它的效果。但,这个准确的数值我们还不清楚,并且当以后需要改变的时候,还要回来对这个数字反复修改。

怎么做好呢?对,就是变量。

加入一个float类型的speed(速度)变量,再在AddForce里让movement乘以它。这样,只需要在我们需要的时候,修改speed变量的值就可以方便的调整效果了。

不过,不断的反复测试切换编辑,这依然有点花费时间。

我们可以创建一个公共型的(public)speed变量,这会在Unity的属性面板里的PlayerController脚本出现一个对应的Speed属性,便于在测试游戏的时候进行调整,并在结束测试后返回脚本编辑器输入最终的合适的值。

注意,为了使游戏的运行效果不会受到不同运行环境下不同帧率的影响,我们通过每一帧的间隔时间(delta time)来更新游戏的变化,这个时间值就是Time类的deltaTime属性。

```csharp
    //在FixedUpdate函数外部添加
    public float speed = 500;

    //回到FixedUpdate内部修改AddForce这一行为
    rigidbody.AddForce(movement * speed * Time.detalTime);
```

保存脚本,回到Unity并进行测试。

看,现在好多了。

下一个任务将谈论关于摄像机的移动。


4.移动摄像机(movin the camera)
----

摄像机如果固定不变的话,会出现某些时刻玩家物体移动到视野以外,也就是消失在屏幕上。

所以,我们需要将摄像机绑定(tie)在玩家物体上。

首先,我们调整一下默认的摄像机的属性,y坐标:5,z坐标:-10,x旋转:45。

然后,我们可不可以把摄像机直接设置成玩家物体的子物体呢?

结果是,画面相当混乱。

在小球移动的同时,作为子物体摄像机的确跟随了(可以在编辑器中拖动移动手柄查看),但由于小球在运动中同时旋转,摄像机也是如此。

显然,这不是我们希望的。

一个更好的方法是通过脚本控制摄像机对对象物体进行跟随。

我们从玩家物体中分离摄像机,给摄像机添加新脚本'CameraController'(注意将脚本资源移动到scripts文件夹便于维护及管理),打开脚本,开始脚本编辑。

这一次,我们需要操作的是摄像机的位置,也就是transform组件的position属性。

想象一下,这一次的脚本内容非常简单,保持摄像机与玩家物体之间一个固定距离不变,及:摄像机位置 = 玩家物体位置 + 固定间距。

我们可以创建一个Vector3类的offset(偏移量)保存间距,以及一个public的GameObject类的变量target(目标)来获取目标当前位置,并且容易的在编辑器中给target设置为玩家物体对象。

由于这个代码是每一帧都要更新的,同样有两个选择,Update函数或者LateUpdate函数。

        LateUpdate:在Update调用完毕后调用。
        
当然,既然提到了,肯定这里要选后者嘛。

```
    void LateUpdate(){
        transform.position = target.transform.position + offset;
    }
```

保存脚本,回到Unity进行测试,嗯,效果不错。

下一个任务里,我们将完善游戏区域,并加入可被捡取的特殊游戏物体。


5.创建捡取物品(creating pick-up objects)
----

转载于:https://www.cnblogs.com/GettingStarted/p/3468585.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值