0000-00-00-u3game_V.md 25.0 KB
Newer Older
恬静的小魔龙's avatar
恬静的小魔龙 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
---
layout:   blog
istop:	  true
u3game:	  true
category: Unity3D
title:    【Unity3D开发小游戏】VVVVV游戏
date:     2020-08-21 21:09:00
background-image: https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub29idHV0cy5jb20vY29udGVudC91bml0eS8yZC12dnZ2dnYtc3R5bGUtcGxhdGZvcm1lci1nYW1lL3VuaXR5X3Z2dnZ2dl9zdHlsZV9wbGF0Zm9ybWVyLmdpZg
tags:
- Unity3D
- Unity3D开发小游戏
---

@[TOC]
## 一、前言
本教程将展示如何在特里·卡瓦纳赫的启发下,制作一款简单而令人上瘾的平台游戏-VVVVV游戏。
这个游戏规则很简单,用户必须通过水平行走或倒转重力来控制一个小个子通过某种奇怪的迷宫。

我们的游戏很容易做。它只需要一个水平图像,一个尖峰图像,一个检查点图像和一个Player图像。

像往常一样,一切都会尽可能简单地解释,这样每个人都能理解它。

**效果图:**
![在这里插入图片描述](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub29idHV0cy5jb20vY29udGVudC91bml0eS8yZC12dnZ2dnYtc3R5bGUtcGxhdGZvcm1lci1nYW1lL3VuaXR5X3Z2dnZ2dl9zdHlsZV9wbGF0Zm9ybWVyLmdpZg)
## 二、源码
UI资源和源代码请搜索QQ群:1040082875下载

## 三、正文
### 版本
==Unity5.0.0f4==
### 1.摄像机设置
我们选择主照相机,然后我们可以设置背景色黑色并调整大小如下图所示:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912163508831.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
### 2.平面
**绘制平面**
我们将创建一个大的图像,添加一些背景模式,然后剪出一些区域供玩家行走:
![在这里插入图片描述](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub29idHV0cy5jb20vY29udGVudC91bml0eS8yZC12dnZ2dnYtc3R5bGUtcGxhdGZvcm1lci1nYW1lL2xldmVsX2JsdWUucG5n?x-oss-process=image/format,png)
*注意:右击图像,选择另存为。保存到Assets/Sprites文件夹。*

**导入平面**
导入到项目中:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912163726528.png)
**导入设置:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912163800884.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
*注:Pixels Per Unit设成16这意味着16x16像素将适合在游戏世界的一个单位。我们将使用这个值作为我们所有的纹理,因为V尖峰将是16x16像素,这应该是一个单位的游戏世界。*

之后,我们可以从项目区进入在Hierarchy为了把它添加到游戏世界中:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912163901907.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
### 3.平面物理
目前,我们的形象实际上只是游戏世界中的一个形象。我们可以看到它,但它什么也做不了,玩家将无法在上面行走。为了让它成为物理世界的一部分,我们将添加一个碰撞器,这样玩家就可以在上面行走并与之碰撞。

**添加碰撞器:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912163921700.png)
我们看场景就能看到结果了。Unity只是试图将多边形围绕在我们复杂的水平形状上:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912163941761.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
问题是,多边形只是一个估计,它还不是我们想要的。现在,只有我们用红色突出显示的区域实际上是可行走的:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912164007709.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
Unity允许我们通过单击以下按钮修改多边形碰撞器:
![在这里插入图片描述](https://img-blog.csdnimg.cn/201909121641019.png)
之后,我们可以修改场景就像这样:
![在这里插入图片描述](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub29idHV0cy5jb20vY29udGVudC91bml0eS8yZC12dnZ2dnYtc3R5bGUtcGxhdGZvcm1lci1nYW1lL2xldmVsX2JsdWVfZWRpdGNvbGxpZGVyX2luc2NlbmUuZ2lm)
让我们修改整个多边形,直到它完全符合我们的要求:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912164612662.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
现在平台的碰撞器是非常好的。

### 4.V字尖峰
好吧,让我们增加我们的难度,增加红色V尖峰。我们将首先绘制一幅16x16px图像:
![在这里插入图片描述](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub29idHV0cy5jb20vY29udGVudC91bml0eS8yZC12dnZ2dnYtc3R5bGUtcGxhdGZvcm1lci1nYW1lL1YucG5n?x-oss-process=image/format,png)
*注意:右击图像,选择另存为。并将其保存在项目的Assets/Sprites文件夹。*

**导入设置:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912164734602.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
让我们把它拖入场景中,它放在(-2, -12, 0)位置
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912164751181.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
*==注意:如果我们把所有的V型尖峰定位在整数位置上是比较好的,比如(-2, -12, 0),而不是像(-2.03 -11.99, 0)==*

让我们再一次加入碰撞器,使V字尖峰 成为物理世界的一部分。这一次我们不需要Polygon Colllider 2D,因为它的形状非常简单。

我们只需要添加一个Box Collider 2D碰撞器:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912165028221.png)
现在我们可以选择V尖峰并复制粘贴:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912165047169.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
然后把它放在前一个旁边:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150334596.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
我们将继续复制和定位V尖峰,直到我们的水平区域被填满:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150351578.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
*注意:我们可以旋转Z轴,来旋转V尖峰*

为了保持整齐呃,我们再Hierarchy中,将它们拖到level_blue对象下面,成为其子对象:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150504751.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
**注意:我们还把所有的V都重命名为“V”而不是“V1”, “V2”等。**

### 5.玩家
随着难度的提高,现在是时候把玩家添加到我们的游戏中了。
我们需要两个闲置的动画(面向左和右)
还有两部动画(面向左和右)
如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150616604.png)
*注意:右键另存为,保存到项目中Assets/Sprites文件夹*

**导入设置**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150658518.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
**分割图片**
我们的玩家图像实际上是由几个小片组成的动画。我们在上面的图像设置Sprite Mode:Multiple
这告诉Unity,我们的图像有多个切片。让我们点击Sprite Editor按钮,以便我们可以编辑切片:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150817301.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
之后我们会切片具有下列设置的图像:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150829138.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
Unity自动检测图像中的切片。我们现在要做的就是选择Apply并关闭Sprite Editor

然后让我们看看在项目区,所有的片段都是玩家的子部分:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319150950641.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
### 6.玩家动画
好的,现在我们有了动画切片,我们可以用它创建我们的4幅动画。作为提醒,下面是我们需要的动画:
- 闲置右(切片0和1)
- 闲置左(切片2和3)
- 向右走(切片4和5)
- 向左走(切片6和7)

让我们创建闲置右动画。我们将首先选择项目区的切片0和1:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319151123372.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
然后将它们拖到场景:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319151132922.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
我们将它保存到PlayerAnimation文件夹中,命名为idle_right.anim

然后我们可以在项目区看到:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319151242953.png)
第一个文件是动画状态机,它指定动画速度和动画状态。第二个是动画本身。

我们将在其余的动画中重复这个过程。
- 切片3-4命名为idle_left.anim
- 切片4-5命名为walk_right.anim
- 切片7-8命名为walk_left.anim


这是我们在Hierarchy看上去的样子:
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020031915152988.png)
然后删除player_2、player_4、player_6
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319151636981.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
然后在项目区删除player_2、player_4、player_6的动画状态机:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319151717522.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
### 7.玩家动画状态机
现在我们有四个动画文件,但还不知道什么时候播放哪个动画。
所以我们需要Unity的动画系统,做一个动画状态机,它有4个状态:

- 闲置右
- 闲置左
- 向右走
- 向左走
我们还将添加Transitions,这样Unity就知道什么时候该从一种动画状态切换到另一种动画状态。


好的,让我们在项目区双击player_0状态机:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319152106259.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
现在我们可以在Animator看到状态机:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319152224633.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
**创建其他状态**
这个**idele_right**状态已经在动画器中,所以让我们添加**idele_left**状态,只需从文件夹中拖动**idele_left**动画文件到Animator:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319152341480.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
对于其余两种状态,可以重复这一过程:
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020031915242468.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
**创建参数**
因为玩家只有水平移动方向。
那么参数为0代表只是站在那里,大于0说明往左,小于0说明往右

让我们点击参数选项卡的左边,点击+号,添加一个INT类型的参数DirX:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319152632237.png)
稍后,我们可以通过编写以下内容从脚本中设置参数:

```csharp
GetComponent<Animator>().SetInteger("DirX", 0);
```

**创建Transitions**
我们希望Unity能够根据这个参数自动切换到不同的动画状态。让我们选择idel_right状态,右键单击它,选择过渡并将白色箭头拖到向右拖到walk_right:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319152731247.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
然后我们点击白色箭头,设置参数大于0的时候是向左移动(小于0是向右移动):
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319152814881.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
*注意:我们还禁用了Has Exit Time因为我们不需要一个基于时间的过渡。*

现在我们可以创造另一个Transition从walk_right回到idle_right。这一次,我们将使用条件那x等于0:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319160628922.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
为了更多的转换,我们将重复这个工作流:

- idle_left到walk_left如果DirX小于0
- walk_left到idle_left如果DirX=0
- idle_left到walk_right如果DirX大于0
- walk_left到walk_right如果DirX大于0
- idle_right到walk_left如果DirX小于0
- walk_right到walk_left如果DirX小于0

以下是动画状态机与所有这些转换的外观:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319160747670.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
*注:橙色状态是默认的,或者换句话说:游戏开始后,我们处于橙色状态。*

**动画速度**
这里还有最后一次调整。让我们选择walk_left和walk_right然后把他们的速度到0.7这样动画就不会出现得太快:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319160828853.png)
我们刚刚创建了动画状态机。因此,Unity将自动在动画之间切换,并根据当前状态一遍又一遍地播放它们。剩下我们要做的就是在我们的脚本中设置动画之间参数。
那些曾经尝试用脚本手动实现所有这些状态的人会意识到动画状态机会有多方便。

### 8.玩家物理
让我们在场景中选择玩家,将玩家的位置设置在(10, -10, 0):
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319161021945.png)
**添加碰撞器:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319161034279.png)
下面是场景中的场景:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319161046129.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
**添加刚体:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319161102174.png)
*注意:我们用了一个有点大的Gravity Scale使玩家快速下降的值。我们还开启了Fixed Angle属性以确保玩家在与某物发生碰撞后不会开始旋转。*

### 9.玩家移动
添加脚本Player,双击打开脚本,删除Start函数,因为我们不需要它:

```csharp
using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {
    
    // Update is called once per frame
    void Update () {
    
    }
}
```
为了实现水平移动,我们需要运动速度变量,让我们设置一个speed变量:

```csharp
using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {
    // Movement Speed
    public float speed = 30;
    
    // Update is called once per frame
    void Update () {
    
    }
}
```
现在我们可以使用Update函数使玩家移动。
我们希望当玩家按右或左箭头键时被检测到,我们可以使用GetAxisRaw函数:

```csharp
// Update is called once per frame
void Update () {    
    // Horizontal Movement
    float h = Input.GetAxisRaw("Horizontal");
}
```
*注意:GetAxisRaw函数返回0如果不按方向,1如果用户按下指示正确的方向和-1如果他按下指示左边方向。*

现在我们可以用我们的h值设置刚体的速度属性,这就是使玩家移动的原因。
这个velocity总是运动方向乘以速度。
在我们的例子中,移动方向是Vector2.right,我们可以把它看作水平方向。
速度将是我们的速度变量乘以h变量使其变为正或负,这取决于我们需要的方向:

```csharp
// Update is called once per frame
void Update () {    
    // Horizontal Movement
    float h = Input.GetAxisRaw("Horizontal");
    GetComponent<Rigidbody2D>().velocity = Vector2.right * h * speed;
}
```

我们别忘了DirX这里的动画参数也是:

```csharp
// Update is called once per frame
void Update () {    
    // Horizontal Movement
    float h = Input.GetAxisRaw("Horizontal");
    GetComponent<Rigidbody2D>().velocity = Vector2.right * h * speed;
    
    // Animation
    GetComponent<Animator>().SetInteger("DirX", (int)h);
}
```
如果我们保存脚本并按下Play然后我们现在可以移动玩家:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319161605394.gif)
让我们来看看最有趣的机制,重力逆转。听起来有些复杂的东西,实际上在Unity中是很简单的。首先,我们要检查是否按下了空格键。
然后我们把gravityScale的值设为-1,这就逆转了它。我们还旋转玩家,使其看起来也相反:

```csharp
// Update is called once per frame
void Update () {    
    // Horizontal Movement
    float h = Input.GetAxisRaw("Horizontal");
    GetComponent<Rigidbody2D>().velocity = Vector2.right * h * speed;
    
    // Gravity Changes
    if (Input.GetKeyDown(KeyCode.Space)) {
        GetComponent<Rigidbody2D>().gravityScale *= -1;
        transform.Rotate(0, 180, 180);
    }
    
    // Animation
    GetComponent<Animator>().SetInteger("DirX", (int)h);
}
```
*注意:我们在Z轴,使其看起来倒立,180°Y轴可以水平地镜像它。这样,我们的左右动画看起来仍然是正确的与反向重力。*


我们按下Play然后我们可以通过按空格键来重力扭转:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319161805613.gif)
这里只有一点小问题。现在我们可以随时扭转地心引力,即使是在坠落的时候:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319161857962.gif)
要防止这种情况发生,必须确保重力只能在地面上倒转。出于某种原因(这有点复杂),Unity没有isGrounded函数,我们可以自己写一个IsGrounded函数:

```csharp
public bool isGrounded() {
    // Get Bounds and Cast Range (extents.y+20%)
    Bounds bounds = GetComponent<Collider2D>().bounds;
    float range = bounds.extents.y * 1.2f;

    // Calculate a position slightly 'below' the collider
    // (via transform.up so it's gravity tolerant)
    Vector2 v = bounds.center - transform.up * range;
    
    // Linecast upwards
    RaycastHit2D hit = Physics2D.Linecast(v, bounds.center);

    // Was there something in-between, or did we hit ourself?
    return (hit.collider.gameObject != gameObject);
}
```

*注意:这实际上只是一些矢量数学和一些物理的东西。如果你不明白的话,不要太担心它。这个功能会运行得很好,很可能会被Unity自己的功能所取代。*


让我们在Update函数利用我们的isGrounded函数,在按下的空格键的时候检查:

```csharp
// Gravity Changes
if (Input.GetKeyDown(KeyCode.Space) && isGrounded()) {
    GetComponent<Rigidbody2D>().gravityScale *= -1;
    transform.Rotate(0, 180, 180);
}
```
如果我们保存脚本并按下Play现在我们只能站在地面上扭转重力。


### 10.检查点
**创建检查点**
让我们增加一些检查点,让游戏变得简单一些。我们首先画一个:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319162432697.png)
*注意:右键另存为,保存到Assets/Sprites文件夹中*

**导入设置**
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020031916251087.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
设置坐标(10, -10, 0):
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319162524299.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
我们希望玩家可以显示在检查点前面,让我们选择player_0对象,然后更改Sprite Renderer的Order in Layer为1:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319162655161.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
*注意:数字越大越后被渲染,也就是说会显示在其他图像的前面*

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319162756249.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)

接着让我们给检查点添加碰撞器:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319162814278.png)
带着IsTrigger启用后,我们很快就可以使用OnTriggerEnter2D函数来确定玩家是否通过了触发器。


让我们复制几个检查点,并将其移到我们场景的不同位置:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319162841397.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E3NjQ0MjQ1Njc=,size_16,color_FFFFFF,t_70)
*注意:我们还将所有检查点重命名为“checkpoint”*

**穿过检查点**
让我们打开Player脚本,现在,每当他走过一个检查站,我们想要保存检查站的位置,以便我们可以去那里,以防死亡。让我们添加另一个变量:

```csharp
public class Player : MonoBehaviour {
    // Movement Speed
    public float speed = 30;
    
    // Last Checkpoint
    Transform check;
    
    ...
}
```
然后,我们可以在OnTriggerEnter2D收到以下信息:如果我们走过一个检查点,check的坐标就被设置为穿过的检查点的坐标:

```csharp
void OnTriggerEnter2D(Collider2D co) {
    // Checkpoint?
    if (co.name == "checkpoint")
        check = co.transform;
}
```
就这么简单。

### 11.穿过V字尖峰
如果玩家不能死,我们的检查点是无用的,所以让我们再次修改我们的脚本。这一次,我们希望检测到与V字尖峰。
V字尖峰没有Is Trigger选项,这意味着这次我们需要OnCollisionEnter2D函数:
```csharp
void OnCollisionEnter2D(Collision2D co) {
    // Collided with a V?
    if (co.collider.name == "V") {
        // Reset Rotation, Gravity, Velocity and go to last Checkpoint
        transform.rotation = Quaternion.identity;
        GetComponent<Rigidbody2D>().gravityScale = Mathf.Abs(GetComponent<Rigidbody2D>().gravityScale);
        GetComponent<Rigidbody2D>().velocity = Vector2.zero;
        transform.position = check.position;
    }
}
```
*注意:开始时,我们会通过检查名字是否为V,来检测是否碰撞到V字尖峰。然后,我们将旋转重置为默认旋转(四元数。恒等式),使重力为正(向下),并重置刚体的速度。之后,我们只需将当前位置设置为最后一个检查点的位置。*

如果我们保存脚本并按下Play之后,玩家将被重置为死后的最后一个检查点:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319163456405.gif)
就这样,现在我们可以尝试通过第一层:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200319163529247.gif)
### 12.摘要
我们刚刚在Unity制作了一个有趣的小平台游戏。像往常一样,Unity处理所有复杂的事情(isGrounded 函数除外)。现在该由读者来让游戏变得有趣了。还有各种各样的东西仍然可以实现,例如:

- 更高层
- 背景音乐
- 故事
- 菜单
- 移动平台