@[TOC]
一、前言
我们的教程将解释如何在Unity中制作2D青蛙过河游戏。弗罗格早在1981年就发行了,但由于它的街机性质,它在今天仍然很有趣,并且很容易在Unity中开发。
这将是如此容易,我们将几个像素艺术纹理,只有几种颜色。
像往常一样,一切都会尽可能简单地解释,这样每个人都能理解它。
效果图:
二、源码
UI资源和源代码请搜索QQ群:1040082875下载
三、正文
版本
==Unity5.0.0f4==
1.摄像机设置
如果我们选择主照相机在层次性然后我们可以设置背景色黑色并调整大小而位置如下图所示:
2.背景设置
我们将选择一个简单的和未来派的艺术风格,我们的游戏,只有两种颜色。背景将由几个方块和线组成,它们都是1像素宽。我们将使用以下背景图像,但可以随意使用以下工具绘制自己的Paint.net:
==注意:右击图像,选择另存为。,导航到项目的资产文件夹并将其保存到新的Sprites文件夹。==
让我们在项目区:
然后看一看检验员在那里我们可以更改导入设置若要调整大小和压缩,请执行以下操作:
注:Pixels Per Unit设为16这意味着16x16像素将适合在游戏世界的一个单位。我们将使用这个值的所有纹理,因为青蛙将是16x16像素后(这应该是一个单位在游戏世界)。
好了,现在我们可以从项目区进入层次性为了让它成为游戏世界的一部分:
如果我们按下Play那么我们已经可以看到它的名字了:
注:底部的橙色方块是车辆行驶的街道。中间的绿地就像草,青蛙可以站在那里不用担心。上面的黑色区域将是水,这是青蛙必须跳到平台上才能通过它的地方。顶部的绿地将是必须达到的目标。
我们正在制作一个2D游戏,背景应该总是画在青蛙和汽车后面。为了实现这一点,我们可以用分类层 (用于更复杂的游戏)或者层序财产。我们来看看检验员改变背景层序财产-1:
==注意:Unity将游戏对象从最低值绘制到最高值,因此前景中的任何内容都应该具有比其他对象更高的值。==
3.创造青蛙
青蛙形象 没有青蛙就没有青蛙。我们将制作一个大的图像与所有的动画切片在其中。我们需要下列动画:
- 走上去
- 走下
- 向右走
- 向左走 ==注意:每个动画将由两个切片组成。如果我们在一个循环中播放这两个切片,那么我们将有一个动画。==
随意画任何你喜欢的青蛙。下面是包含所有动画切片的青蛙图像:
注意:右击图像,选择另存为。并将其保存在项目的资产/Sprites文件夹。
我们将使用以下方法导入设置为此:
青蛙图像切片
我们的青蛙图像实际上是由几个小片组成的动画。我们可以通过Sprite Mode选择 Multiple。之后,我们可以单击Sprite Editor按钮:.
然后我们会选择切片并将类型设置为格网使用以下设置,然后按下切片按钮:
之后,我们可以关闭切片编辑器并按下应用在未导入设置警告。
我们刚刚将青蛙图像分割成几个16×16像素的图像,我们现在可以在项目区 (他们是青蛙形象的孩子):
4.创造青蛙动画
好的,现在我们有了动画切片,我们可以用它创建我们的4幅动画。下面是我们需要的动画:
- 向上(切片0和1)
- 向下(切片2和3)
- 向右(切片4和5)
- 向左(切片6和7)
让我们先制作向上的动画,Walking up:
选择切片0和1,将它们拖到场景中:
我们将这个动画保存到FrogAnimation文件夹,命名为WalkUp.anim,如下图:
第一个文件是动画状态机,它指定诸如动画速度和混合树之类的内容。第二个是动画本身。
我们将在其余的动画中重复这个过程。(第2和3片命名为WalkDown,4和5命名为WalkRight,6和7命名为WalkLeft)
如下图:
删除不用的元素
让我们删除frog_2和frog_4和frog_6,将frog_0重命名为Frog,如下图:
然后在Project区删除另外三个状态机:
5.青蛙动画状态机
现在我们有四个动画文件,但还不知道什么时候播放哪个动画。我们需要用状态机来切换用哪个动画,它有4个状态:
- 向上WalkUp
- 向下WalkDown
- 向右WalkRight
- 向左WalkLeft
我们还要添加Transitions,来让Unity知道什么时候该从一种动画状态切换到另一种动画状态。
好的,让我们双击frog_0的动画状态机文件:
现在我们可以看到状态机的Animator:
这个WalkUp状态已经在其中,所以让我们选择WalkDown/WalkRight/WalkLeft动画文件并将其拖到此处:
注意:黄色是默认的状态
之后选择所有的状态,改变它们的speed属性到0.6:
注意:这会导致动画速度变慢。
状态机最重要的一点是,它将独立处理动画状态,完全自动完成。我们所要做的就是告诉状态机注意我们青蛙的移动方向,仅此而已。然后,状态机将在动画状态之间进行切换,而无需我们做任何事情。
让我们选择参数选项卡,添加两个int变量的X和Y:
稍后,通过编写以下内容,我们可以在脚本中设置这些参数:
GetComponent<Animator>().SetFloat("X", 0);
GetComponent<Animator>().SetFloat("Y", 0);
好的,现在我们希望Unity能在动画者状态之间自动切换,这取决于这些参数。我们的青蛙只有四个移动方向(上、下、左、右)。这意味着我们只需要以下4项Transitions在我们的动画状态机中:
- 如果Y>0然后它在移动向上 (如Y=1,Y=2,Y=3等)
- 如果Y<0然后它在移动向下 (如Y=-1,Y=-2,Y=-3等)
- 如果X>0然后它在移动右边 (如X=1,X=2,X=3等)
- 如果X<0然后它在移动左边 (如X=-1,X=-2,X=-3等)
让我们右键单击任何状态,选择Transitions并将白色箭头拖到WalkUp状态:
接着我们单击白色箭头,添加条件Y>0:
注意:我们还禁用了Can Transition to Self属性防止动画始终重新启动。
这意味着如果Y参数大于0,将切换到WalkUp状态。
好的,我们再添加剩下3个Transitions:
- 如果Y<0,从任何状态到向下
- 如果X<0,从任何状态到向左
- 如果X>0, 从任何状态到向右
注意:我们还将禁用Can Transition to Self属性
下面是我们最后的动画状态机:
6.青蛙物理
现在青蛙只是一个图像,它不是物理世界的一部分,我们需要添加碰撞器和刚体,让它成为物理世界的一部分,这意味着青蛙将于物体相撞,而不是穿过它。
添加碰撞器和刚体组件:
好吧,我们的青蛙现在是物理世界的一部分。它将与其他事物碰撞,其他事物将与之碰撞。它还将导致OnCollisionEnter2D函数将在附加到青蛙的任何脚本中调用。
我们将青蛙放到我们的底层:
7.青蛙移动脚本
添加脚本Frog.cs,双击打开,不需要Start函数和Update函数,删除。然后添加FixedUpdate函数:
using UnityEngine;
using System.Collections;
public class Frog : MonoBehaviour {
// FixedUpdate for Physics Stuff
void FixedUpdate () {
}
}
注意:FixedUpdate函数它也被一次又一次的调用,但是在一个固定的时间间隔内。单位物理是在完全相同的时间间隔内计算的,所以做物理工作的时候使用它总是一个好主意
只要玩家按下其中一个箭头键,青蛙就会移动。 但它不应该只是走一点,而是应该跳过去一个单元。
下面的图片说明了移动青蛙的两个选项。左边的那个往上走,不是我们想要的。
跳跃再往上走一个单位,才是我们想要的:
我们需要两个新变量来处理这种运动:
jump speed指定青蛙的跳跃速度
jump vector指定青蛙的跳跃位置
当青蛙还在跳跃时,玩家将不能做任何动作:
using UnityEngine;
using System.Collections;
public class Frog : MonoBehaviour {
// Jump Speed
public float speed = 0.1f;
// Current jump
Vector2 jump = Vector2.zero;
// FixedUpdate for Physics Stuff
void FixedUpdate () {
}
}
这个jump vector有一个x和y组件。 如果用户按下上箭键,然后跳转向量的y值将是1在每一个FixedUpdate调用它会稍微往上跳 然后减少y值,跳跃完成时,y值是0。 同样适用于x值的水平跳跃。
作为提醒,下面是Vector 2方向的样子:
其他脚本需要知道青蛙目前是否在跳跃,所以让我们为它提供一个isJumping函数:
// Is the Frog currently jumping?
public bool isJumping() {
return jump != Vector2.zero;
}
好的,让我们在我们的FixedUpdate中检查按键,然后设置jump vector:
// FixedUpdate for Physics Stuff
void FixedUpdate () {
if (Input.GetKey(KeyCode.UpArrow))
jump = Vector2.up;
else if (Input.GetKey(KeyCode.RightArrow))
jump = Vector2.right;
else if (Input.GetKey(KeyCode.DownArrow))
jump = -Vector2.up; // -up means down
else if (Input.GetKey(KeyCode.LeftArrow))
jump = -Vector2.right; // -right means left
}
青蛙在跳的时候,不起作用,让我们再次修改它:
void FixedUpdate () {
// Currently jumping?
if (isJumping())
{
// Do Stuff...
}
// Otherwise allow for next jump
else
{
if (Input.GetKey(KeyCode.UpArrow))
jump = Vector2.up;
else if (Input.GetKey(KeyCode.RightArrow))
jump = Vector2.right;
else if (Input.GetKey(KeyCode.DownArrow))
jump = -Vector2.up; // -up means down
else if (Input.GetKey(KeyCode.LeftArrow))
jump = -Vector2.right; // -right means left
}
}
青蛙应该跳的越远越好,同时isJumping为真。
当我们跳的越来越远,jump vector会越来越小:
// Currently jumping?
if (jump != Vector2.zero)
{
// Remember current position
Vector2 pos = transform.position;
// Jump a bit further
transform.position = Vector2.MoveTowards(pos, pos+jump, speed);
// Subtract stepsize from jumpvector
jump -= (Vector2)transform.position-pos;
}
// Otherwise allow for next jump
else
{
if (Input.GetKey(KeyCode.UpArrow))
jump = Vector2.up * jumpSize;
else if (Input.GetKey(KeyCode.RightArrow))
jump = Vector2.right * jumpSize;
else if (Input.GetKey(KeyCode.DownArrow))
jump = -Vector2.up * jumpSize; // -up means down
else if (Input.GetKey(KeyCode.LeftArrow))
jump = -Vector2.right * jumpSize; // -right means left
}
注意:跳跃代码是基本向量数学。首先,我们跳到跳跃向量的方向和设置的新位置transform.position,之后,我们减去我们往跳跃向量的方向跳的距离(新位置-旧位置)
记住我们的动画状态机X和Y参数,为了使动画工作,我们所要做的就是在FixedUpdate函数中设置这两个参数:
// Animation Parameters
GetComponent<Animator>().SetFloat("X", jump.x);
GetComponent<Animator>().SetFloat("Y", jump.y);
现在还有最后一个窍门要加在这里。我们的青蛙并不总是走路上/下/右/左。它也可以站在周围看着上/下/右/左方向。通常,这意味着我们需要4个更多的动画和4个过渡的动画状态。我们将不创建这4个动画,而是每当青蛙不动的时候动画速度设置为0,每当它四处移动将动画速度设置为1
GetComponent<Animator>().speed = Convert.ToSingle(isJumping());
注意:Convert.ToSingle函数,它简单地将布尔值转换为浮点值。简单来说就是ture转为1,false转为0
好了,这是我们的最终的FixedUpdate函数:
// FixedUpdate for Physics Stuff
void FixedUpdate () {
// Currently jumping?
if (isJumping())
{
// Remember current position
Vector2 pos = transform.position;
// Jump a bit further
transform.position = Vector2.MoveTowards(pos, pos+jump, speed);
// Subtract stepsize from jumpvector
jump -= (Vector2)transform.position-pos;
}
// Otherwise allow for next jump
else
{
if (Input.GetKey(KeyCode.UpArrow))
jump = Vector2.up;
else if (Input.GetKey(KeyCode.RightArrow))
jump = Vector2.right;
else if (Input.GetKey(KeyCode.DownArrow))
jump = -Vector2.up; // -up means down
else if (Input.GetKey(KeyCode.LeftArrow))
jump = -Vector2.right; // -right means left
}
// Animation Parameters
GetComponent<Animator>().SetFloat("X", jump.x);
GetComponent<Animator>().SetFloat("Y", jump.y);
GetComponent<Animator>().speed = Convert.ToSingle(isJumping());
}
我们还需要添加一个OnCollisionEnter2D函数。每当青蛙和车辆相撞,游戏就结束了:
void OnCollisionEnter2D(Collision2D coll) {
// Game Over
Destroy(gameObject);
}
如果我们保存脚本并按下Play然后我们可以用箭头键移动青蛙:
8.创造车辆
我们的街道需要两种类型的车辆。一辆车向左行驶,一辆车向右行驶。
我们先画一辆卡车:
注意:右键另存为,保存到项目的Assets/Sprites文件夹。
导入设置:
添加碰撞器和刚体:
车辆不应该默认出现在游戏中。相反,我们将把它保存为预制体。
我们可以在需要的时候把它加载进游戏。
让我们创建一个新的Prefabs文件夹
然后把车辆从Hierarchy拖入到该文件夹中:
接着我们就可以删除Hierarchy中的车辆了。
现在,对于第二辆赛车,我们可以重复同样的工作流程:
注意:右键另存为,保存到项目的Assets/Sprites文件夹。
下面是车辆的属性:
9.生成车辆
现在让我们生成车辆,让我们选择GameObject->Create Empty,命名为Spawner:
我们会给它一个Gizmo这样我们才能在场景中看得更清楚。Gizmo就像一个视觉提示,只显示在Unity编辑器中,而不是在最后的游戏中。我们可以通过单击左上角的图标来选择Gizmo:
我们看一看场景,我们现在可以看到一个灰色的点,其中Spawner是:
如果看不到,请尝试调整gizmo尺寸:
让我们把Spawner定位在街道上第一排网格的左边:
创建一个新脚本命名为:Spawn.cs,删除Update函数,因为我们不需要它,添加两个公共变量来指定车辆的预制体和生成间隔:
using UnityEngine;
using System.Collections;
public class Spawn : MonoBehaviour {
public GameObject prefab;
public float interval = 1;
// Use this for initialization
void Start () {
}
}
现在我们可以创建一个SpawnNext函数,该函数通过使用实例化。我们也会用InvokeRepeting调用SpawnNext函数在指定的时间间隔内执行:
// Use this for initialization
void Start () {
InvokeRepeating("SpawnNext", 0, interval);
}
void SpawnNext() {
// Instantiate
GameObject g = (GameObject)Instantiate(prefab,
transform.position,
Quaternion.identity);
}
注:实例化加载预制件就位进入现场transform.position随着旋转Quaternion.identity,这是默认的旋转。
这里还有一件事要做。现在,脚本将加载预制体到现场,但之后什么也不会发生。车辆就站在那里。让我们修改脚本,使其给实例化的Prefab以一定的速度(即运动方向乘以速度):
using UnityEngine;
using System.Collections;
public class Spawn : MonoBehaviour {
public GameObject prefab;
public float interval = 1;
public Vector2 velocity = Vector2.right;
// Use this for initialization
void Start () {
InvokeRepeating("SpawnNext", 0, interval);
}
void SpawnNext() {
// Instantiate
GameObject g =(GameObject)Instantiate(prefab,
transform.position,
Quaternion.identity);
// Set Velocity
g.GetComponent<Rigidbody2D>().velocity = velocity;
}
}
让我们保存脚本,可以看到Spawn脚本的变量:
我们将车辆的预制体拖入到Prefab卡槽,并且调整生成间隔和速度:
之后,我们可以再游戏中添加更多的生成器,这样他们就可以以不同的间隔和速度在不同的位置上生成车辆:
如果我们按下Play然后我们就可以试着用我们的技巧把青蛙移到街对面去了:
10.平台
好吧,好吧,一旦青蛙过了马路,它就必须过水。为了通过水,它必须跳到在水中移动的平台上。一旦青蛙站在一个平台上,它应该移动到站台上。
我们将使用橙色网格来代表平台:
注意:右键另存为,保存到项目Assets/Sprites文件夹中
导入设置
平台物理
让我们给平台添加碰撞器和刚体组件:
还有一件事要添加到平台上。现在,如果平台移动,青蛙在平台上行走,那么平台就会继续移动,而青蛙会一直站在同一个地方。相反,它应该像一个真正的平台,所以如果青蛙站在上面,那么青蛙应该随波逐流像这样的平台:
听起来很复杂的数学实际上在Unity很容易。我们所要做的就是把青蛙变成平台的子对象。这将产生这样的效果:每当平台移动时,青蛙也会移动,而无需我们做任何事情。换句话说:青蛙会移动相对去站台。
让我们添加新脚本Platform.cs,双击打开,删除Start函数和Update函数,添加OnTriggerStay2D和OnTriggerExit2D函数:
using UnityEngine;
using System.Collections;
public class Platform : MonoBehaviour {
void OnTriggerStay2D(Collider2D coll) {
}
void OnTriggerExit2D(Collider2D coll) {
}
}
现在我们要做的就是找出碰撞器的coll属于青蛙的情况 在这种情况下,我们将其设置为transform.parent属性,使青蛙变成平台的子对象 一旦青蛙从平台上跳下来,我们就在OnTriggerExit2D重新设置它的transform.parent属性为null:
using UnityEngine;
using System.Collections;
public class Platform : MonoBehaviour {
void OnTriggerStay2D(Collider2D coll) {
// Frog? Then make it a Child
if (coll.name == "Frog")
coll.transform.parent = transform;
}
void OnTriggerExit2D(Collider2D coll) {
coll.transform.parent = null;
}
}
好的,接着让将平台拖入到预制体文件夹中。
大型台子
对于更大的平台,我们将重复完全相同的工作流程:
注意:右键另存为,保存到Assets/Sprites文件夹中
同样给大型平台也设置以下属性:
生成平台
我们将像我们制造车辆一样,制造平台。首先,我们将添加几个生成器,然后我们将预制体拖入卡槽中,设置生成间隔,并选择一个速度。我们将在水的左右两侧放置5个生成器:
如果我们按下Play然后我们可以看到平台漂浮在水面上:
让我们用青蛙跳到平台上来测试我们的平台脚本。我们可以看到,青蛙一站在上面,它就会和平台一起移动:
11.水
我们已经增加了平台,但是现在青蛙是否使用这些平台并不重要,因为如果它想要的话,它就可以直接穿过水面。
所以,只要青蛙穿过水,我们就让它死掉。我们会选择background的对象:
添加碰撞器:
注:我们勾选了Is Trigger。因为青蛙不应该像墙一样与水相撞。相反,我们只想接收OnTrigger事件。
让我们添加新脚本Water.cs,双击打开,删除Start函数和Update函数,添加OnTriggerStay2D函数。
我们将使用OnTriggerStay2D获取青蛙是否在水中的状态。 当青蛙在水中时,它就会收到通知。 这个OnTriggerStay2D只要青蛙在水中,函数就会一次又一次地被调用。 这一点很重要,因为我们不能直接杀死接触水的青蛙。 青蛙实际上会经常跳过水面。
青蛙应该是以下状态才被杀死:
- 触水
- 不站在站台上
- 不跳
这是它的代码:
using UnityEngine;
using System.Collections;
public class Water : MonoBehaviour {
void OnTriggerStay2D(Collider2D coll) {
// Frog?
if (coll.name == "Frog")
// Not Jumping?
if (!coll.GetComponent<Frog>().isJumping())
// Not on a platform?
if (coll.transform.parent == null)
// Game Over
Destroy(coll.gameObject);
}
}
*注意:一开始,我们会检测碰撞器是否真的碰撞到青蛙。之后,我们通过使用GetComponent
保存后,我们可以通过按Play跳到平台上,然后走进水中,这会销毁青蛙:
12.边界
我们生成的物体永远不会被销毁,永远的向左或者向右移动,我们需要一点点的改进。
这个问题很容易解决。我们将首先创建一个黑色纹理:
注意:右键另存为,保存到Assets/Sprites文件夹中
导入设置
现在我们可以把它拖到场景两次,一次在左边,一次在游戏的右边:
改变他们的层序到1因此,它们总是画在平台和车辆上:
到目前一切尚好。如果我们按下玩然后,平台和车辆现在消失在边界之后:
即使我们不再看到他们,平台和车辆仍然在游戏中。一旦他们接触到边界,我们就会自动销毁他们,所以让我们先在每个边界上添加一个碰撞器,并调整其大小。
这是左边边框的碰撞器:
这是右边边框的碰撞器:
之后,添加脚本Border.cs,双击打开,输入以下代码:
using UnityEngine;
using System.Collections;
public class Border : MonoBehaviour {
void OnTriggerEnter2D(Collider2D coll) {
Destroy(coll.gameObject);
}
}
现在平台和车辆一旦在游戏之外就被摧毁了。
如果我们按下Play
然后,我们现在可以享受一轮不错的青蛙过河游戏了:
13.摘要
这是我们的青蛙过河游戏的教程,几行代码还有大量的文字,我们学会了如何创造一种独特的艺术风格,只有两种颜色,主要是为了使事情尽可能容易。 了解Unity的动画系统,并能够绘制和分割我们自己的动画,有着巨大的价值。 这些平台使我们能够更多地了解强大的transform.parent属性以及如何正确使用它。
我们创建了一个快速而坚实的代码库,和往常一样,现在由读者来让游戏变得有趣。有许多功能可以添加,比如:
- 一个输赢的屏幕
- 多个级别
- 一个更复杂的艺术风格
- 一个高分。
本文由 恬静的小魔龙 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:2020-08-21 21:09:00