在前面一章,我们添加了背景和一些道具到我们的场景里,是时候添加一些有用的元素了,比如,player!
创建一个可操控的实体player需要一些元素:一个精灵,控制它的方法和让它和世界互动的方式。
我们将一步一步的进行。
让我们从精灵开始。
我们将使用下面的图片:
(右键点击保存)
- 复制player图片到"Textures"目录
- 创建一个新的
Sprite
,命名为"Player" - 在"Sprite Renderer"组件里,将精灵放到"Sprite"里
如果你遇到问题,看看前面的章节。我们做了背景和道理完全一样的事情。
- 选择"Player"精灵层
- 将player放到"Foreground"对象里
- 改变scale为
(0.2, 0.2, 1)
我们刚才谈了"Sprite Renderer"组件,如果你还没有注意到,游戏对象由几个组件组成,可以在"Inspector"面板里看到。
默认情况下,一个空对象是这样的:
这个对象只有一个组件:"Transform",这个组件是必须且无法禁用或是移除。
你可以为一个对象添加很多你想添加的组件,脚本也是组件。大多数的组件可以启用或禁用,在对象的生命周期里。
(你可以点击checkbox去禁用它,可以右击组件重置它,移除它等等)
注:组件可以和其它组件相互作用,如果一个对象的一个组件需要另一个组件一起工作,你可以拖动整个对象到这个组件里,它会在对象里找到正确的那一个
"Sprite Renderer"是一个可以显示精灵纹理(sprite texture)的组件。
现在,我们学习了关于组件的概念,让我们给player添加一个!
在player对象里点击"Add Component"按钮,选择"Box Collider 2D"。
这表示player成为了一个hitbox。
你可以在"Scene"视图里看到collider(碰撞体),可以在"Inspector"里使用"Size"属性调整它的大小。
小巧门:这里有另外一种方法修改box collider。选择有box collider的游戏对象,并在组件里启用"Edit Collider",你可以注意到box collider(绿色矩形)现在显示了4个控制点。拖动其中一个,改变盒子的形状。
我们将设置collider大小为(10, 10)
。
它相对于实际的player来说足够大,但仍然小于精灵大小:
现在这样,足够了。
小巧门:如果你打玩做一个shmup,多花点时间来调整hitboxes,通常来说,它将是一个在player精灵里的一个完美小玩素。这里的船窗怎么样?你也可以改变collider形状,使用"Circle Collider 2D"。谢谢Unity这个行为将没有改变什么,它将仍然稍微的提升了一下游戏玩法。
保存player游戏对象为prefab(预制件),你现在有了一个基础的player实体了!
如果你想要一个更加精准,定制形状的hibox,Unity提供了一个"Polygon Collider 2D"组件,它的效率不高,但可以让你换你想要的设置形状。
这是最后一个添加到我们player上的组件:"Rigidbody 2D"。
它是告诉物理引擎,如果控制游戏对象,此外,它允许碰撞的事件可以在脚本里使用。
- 在"Hierarchy"里选择
Plyaer
游戏对象 - 添加"Rigidbody 2D"组件
现在,运行游戏,观察:
飞船掉下去了!
给自们的引力打声热乎吧。:)
由于新的场景默认拥有重力和刚体为游戏对象增加质量,所以,现在船被吸到底部了。
默认引力大小为
9.81
,地球的重力
重力可以被用到很多类型的游戏里,但这里,我们并不想要它,幸运的是,在rigidbody关掉重力非常简单,只需要设置"Gravity Scale"的值为0即可,看,船又飞起来了。
你可能还想勾上"Fixed Angles"属性,因为我们不想因为物理的关系让我们的飞机旋转。
组件设置如下:
现在来写点代码!这么久来,我们还没有写过代码,那是Unity动力。
在Unity里,创建一个新的C#脚本到你的"Scripts"目录里,命名为"PlayerScript"。
注意:你同样可以用JavaScript,就像我们前面说的那样,代码片段里是C#,但是它非常容易将代码转为另一种语言
译者注:正是前面所说,新版的Unity已经废弃了对JavaScript的支持,老老实实用C#吧
打开你最喜欢的编辑器:
Sync MonoDevelop Project:子菜单有点怪异,首先,名字是无法改变的,即使你设置为另一个编辑器。
我们建议你第一次写代码使用这个菜单,因为Unity会创建解决方案,并连接Unity库在里面(为Visual Studio, Xamarin Studio or MonoDevelop)。
如果你简单的打开代码,你的IDE编译器会抛出一堆错误,因为它们并不认识Unity。
不要担心,因为你永远不会直接编译它们,但是在Unity对象上自动编译并首次出错,是一件好事情。
如果你来自XNA,你不会搞错的。
你可以定义一些方法 (称为"Message",因为我们没有使用C#的承继),Unity会在需要的时候识别并执行。
脚本默认带有Start和Update方法,这里有一小部分常用的"Message"函数:
Awake()
当对象被创建时会被调用一次,它是经典构造函数的替代品Start()
在Awake()
后执行,不同的是Start()
方法在脚本禁用时不会被调用 (记得在"Inspector"里那个在组件里的checkbox吗)Update()
在主游戏循环里每帧都会被执行FixedUpdate()
在每个固定帧率调用。当使用物理引擎时,你需要使用这个方法代替Update()
方法Destroy()
在当对象销毁时被调用。这里你最后的机会清除或执行你的代码。
同样还有一些碰撞的函数:
OnCollisionEnter2D(CollisionInfo2D info)
当另外一个collider触碰到这个collider时被调用OnCollisionExit2D(CollisionInfo2D info)
当另一个collider不再触碰到(离开)这个collider时被调用OnTriggerEnter2D(Collider2D otherCollider)
当另一个collider做为"Trigger"触碰到这个collider时被调用OnTriggerExit2D(Collider2D otherCollider)
当另一个collider做为"Trigger"不再触碰到(离开)这个collider时被调用
咯…这个解释有点无聊,但没有办法,不好意思。
注意2D后缀:你应该注意到,多数时候我们谈论的都带有"2D"的后缀,"Box Collider 2D","Rigidbody 2D","OnCollisionEnter2D"或者"OnTriggerEnter2D"方法,等等。这些新的组件或方法出现在Unity 4.3。
通过使用它们,你将采用Unity 4.3里集成的新的物理引擎(基本于Box2D)用于游戏,而不是3D游戏(PhysX)。两个引擎分享概念和对象,但他们的工作不会完全一样。如果你使用它们其中一个(Box2D为2D游戏),就全用它,这就是为什么我们使用的所有对象和方法都是"2D"后缀。
当我们要用到它们的时候,我们再回到一些细节上。
对于player脚本,我们将添加一些简单的控制:箭头键将移动飞船。
using UnityEngine;
/// <summary>
/// Player controller and behavior
/// </summary>
public class PlayerScript : MonoBehaviour
{
/// <summary>
/// 1 - The speed of the ship
/// </summary>
public Vector2 speed = new Vector2(50, 50);
// 2 - Store the movement and the component
private Vector2 movement;
private Rigidbody2D rigidbodyComponent;
void Update()
{
// 3 - Retrieve axis information
float inputX = Input.GetAxis("Horizontal");
float inputY = Input.GetAxis("Vertical");
// 4 - Movement per direction
movement = new Vector2(
speed.x * inputX,
speed.y * inputY);
}
void FixedUpdate()
{
// 5 - Get the component and store the reference
if (rigidbodyComponent == null) rigidbodyComponent = GetComponent<Rigidbody2D>();
// 6 - Move the game object
rigidbodyComponent.velocity = movement;
}
}
(注释里的编号对应下面的解释)
关于C#约定:
spped
成就是可见的,在C#里,它是公有的,一个成员变量应该是私有的,以便将类的内部保持为私有。但使用公有变量暴露它,可以让你通过Unity的"Inspector"面板修改它的值,即使在游戏执行过程中。这是Unity非常有用的特性,让你可以不修改代码也可以调整游戏。
记得在这里编写的脚本,不是经典的C#程序,这意味着打破了一些规则和惯例。
- 我们首先定义了一下公有的变量,它将出现在Unity的"Inspector"面板里,这是飞船的速度值
- 需要的
- 我们使用默认的轴,它可以在"Edit" -> "Project Settings" -> "Input"里被重新定义,它将返回一个在
[-1, 1]
之间的值,0是空闲状态,1是右,-1是左 - 我们将方向乘以速度
- 我们需要访问刚体(rigidbody)组件,但是我们可以通过存储引用来避免每帧都这样
- 我们改变刚体(rigidbody)的速度,这将告诉物理引擎去移动游戏对象,我们在
FixedUpdate()
方法里做这样的事情,就像推荐的那样,物理相关的处理都在些方法里进行。
教程更新:如果你之前有读过本教程,你可以记得我们当时直接使用
transform.Translate
方法,这是有效的,因为平移很慢。但不建议这样做,因为它会搞乱物理引擎 (对于物理引擎来讲,平移就像传送,这里没有碰撞)谢谢你的反馈,我们更新了脚本,以便帮助大家更好的游戏游戏对象的移动
现在,附加脚本到游戏对象上。
小巧门:你可以附加一个脚本到一个游戏对象,通过拖动"Project"视图里脚本到"Hierarchy"里的游戏对象上,也可以点击"Add Component"按钮,手动找到它。
运行游戏,飞船可以移动了,你的游戏已经跑起来了!恭喜,你刚刚做了一个"Hello World"的游戏 :)
试着调整速度:点击player,修改"Inspector"中的speed值,看看效果。
小心:当游戏运行时候的修改会在游戏停止使被丢弃!这是一个非常好调整游戏的工作,但如果你要保存修改,你得记住你改的内容。
然而,这种效果也很方便:你可以完全摧毁你的游戏来测试一些新的东西,而不用担心破坏你的真实项目。
这是我们游戏生命中的第一个标志,让我们添加更多的东西!
shmup如果没有大量的敌人摧毁,它将毫无意义。
让我们使用一个无辜的章鱼,叫作"Poulpi":
(右键点击保存)
创建一个新的精灵!再次:
- 复制图片到"sprites"目录里
- 使用图片创建一个新的
Sprite
- 将sprite层设置为"Enemies"
- 改变"Scale"属性
Transform
为(0.3, 0.3, 1)
- 添加"Box Collider 2D",大小为:
(4, 4)
- 添加"Rigidbody 2D","Gravity Scale"设置为
0
,勾上"Fixed Angles"
保存为预制件(Prefab)…就是这样:
我们简编写一个简单行为:Poulpi将往一个方向移动。
创建一个新的脚本,命名为"MoveScript"。
本来可以叫它"EnemyScript",但我们计划后面在另外的上下文里复用它。
注:Unity基于组件的模块化系统,提供了一种分离具有不同功能脚本的好方法。当然,你仍然可以使用一个巨大的脚本做所有的事,那是你的选择,但我们强烈推荐像这样做。
我们将复制一些在"PlayerScript"里的代码用于移动,我们将添加新一个设计变量用于方向:
using UnityEngine;
/// <summary>
/// Simply moves the current game object
/// </summary>
public class MoveScript : MonoBehaviour
{
// 1 - Designer variables
/// <summary>
/// Object speed
/// </summary>
public Vector2 speed = new Vector2(10, 10);
/// <summary>
/// Moving direction
/// </summary>
public Vector2 direction = new Vector2(-1, 0);
private Vector2 movement;
private Rigidbody2D rigidbodyComponent;
void Update()
{
// 2 - Movement
movement = new Vector2(
speed.x * direction.x,
speed.y * direction.y);
}
void FixedUpdate()
{
if (rigidbodyComponent == null) rigidbodyComponent = GetComponent<Rigidbody2D>();
// Apply movement to the rigidbody
rigidbodyComponent.velocity = movement;
}
}
附加该脚本到Poulpi上,运行游戏,它可以向下面一样移动了。
如果你把player移动到敌人的前面,两个精娄会撞到一起,它们只会相互阻挡,因为我们还没有定义碰撞的行为。
我们已经学习了如何添加player实体,通过键盘控制它,接着,我们又创建了一个基础AI的敌人。
现在,我们要摧毁那个移动的家伙!为此,我们需要弹药!