「Unity2D」使用Unity创建一个2D游戏系列-3

本文由泰然教程组成员 betterdenger 翻译,原文请参阅「Create a player and its enemies」

创建玩家和敌人

在上一章节里,我们添加了一个背景以及场景小道具。现在是时候添加一些有用的游戏元素了,比如玩家之类的。

创建玩家

创建一个玩家可控实体需要一些元素:sprite, 控制这个sprite的方式,以及它和世界交互的方式。

我们会一步一步的来探索这个过程。

先从sprite开始吧。

添加sprite

同样,下面回事我们会用到的图片:

Player Sprite

(右键单击保存)

  1. 复制玩家图片到"Textures"文件夹。
  2. 创建一个新的精灵叫做命名为"Player"。
  3. 选中sprite的"Sprite"属性,显示它的"Sprite Renderer"组件。

如果有问题的话,返回仔细阅读一下上一个部分。接下来做的跟添加背景和场景道具的过程差不多。

  1. 放置玩家到"2 - Foreground"层。
  2. 改变它的大小,(0.2, 0.2, 1)应该是比较合适的。

A word about components

我们刚才提到了"Sprite Renderer"组件。不知道你是否注意到, 一个游戏对象是一组大大小小的组件构成的,可以在"Inspector"面板里看到它们。

默认的,一个空的游戏对象长这样:

Empty game object components

这个对象只有一个组件: 一个"Transform"。这个组件是强制要求的,不能从对象里移除或者禁用。

你可以给一个对象添加任意多的组件。比如一个脚本也是一个组件。大多数组件在对象的生命周期内都可以被enabled和disabled启用或者禁用。

Enable a game object component

(你可以点击如图的checkbox取消勾选来禁用。你也可以右键一个组件来对这个组件重置或者移除等等操作。)

注意:组件可能会与其他组件交互。如果一个对象里有一个组件要求另一个组件工作才能工作,你可以在组件内部拖拽整个对象,它会找到该对象里对应所需的组件。

一个"Sprite Renderer"是一个用来显示sprite纹理的组件。

现在我们已经学习到了组件的概念,让我们开始给玩家加一个组件吧!

添加一个盒子碰撞器

在player对象里点击"Add Component"按钮。选择"Box Collider 2D"。

这代表了player的碰撞盒子。

你可以在"Scene"视图看到碰撞器,可以在"Inspector"里修改"Size"属性来改变它的尺寸大小。

注意:这里还有另一个方法编辑盒子碰撞器。选中游戏对象的碰撞器,按住shift键。你可以观察到盒子碰撞器(绿色的矩形框)现在有四个小的柄在上面。拖拽其中一个可以改变盒子的形状。需要注意的是,这个蓝色的矩形框表示游戏对象的Transform组件,不是盒子碰撞器。

我们把碰撞器大小设为(10, 10).

对于一个真实的shmup来说,它显得过大了。但是对于它依旧比sprite小:

Player hitbox

暂时来说,这已经足够了。

注意:如果你想做一个shmup, 你需要花很多时间调整你的碰撞盒子。通常来说, 它应该紧贴玩家sprite内部一个小元素。我们这里用飞船的窗户?你也可以修改碰撞盒子的形状——例如使用"Circle Collider 2D"。这改变不了Unity的行为,但是会稍微提高一点游戏的可玩性。

保存player游戏对象为一个prefab。这样你就有一个基本的玩家实体了!

Adding Player Sprite

Polygon Collider 2D

如果你想精确和自定义你碰撞盒子的形状,Unity提供一个"Polygon Collider 2D"组件。 它可以允许你把碰撞盒子形状设置成你想要的,但是这样做会导致引擎处理效率变很低。

注意: 这个"Polygon Collider 2D"跟其他碰撞器一样:你可以修改在"Scene"视图里用鼠标修改形状。通过按住cmd或者ctrl, 你可以移除一个点,通过shift你可以调整一个点或者在现有碰撞盒子上加一个点。

Rigidbody的魔力

这里还有最后一个添加给player的组件——"Rigidbody 2D"。

这会告诉物理引擎怎样处理游戏对象。此外,它也允许碰撞事件被传递给脚本。

  1. 在"Hierarchy"里选中Player游戏对象。
  2. 添加一个"Rigidbody 2D"组件。

现在运行并观察:

Falling player

飞船在往下掉!

重力你好啊。:)

因为新场景有一个默认的重力,而刚体添加了一个质量给对象。所以,飞船就被往下吸引咯。

Unity里默认的重力是9.81, 也就是地球的重力。

重力可以被运用到一些游戏,但是我们这里不想使用它。我们可以很简单的在一个刚体上禁用重力属性。只需要设置"Gravity Scale"为0就好了。搞定,现在你看我们的船飞起来了。

你可能也希望勾选"Fixed Angles"属性,因为我们不想飞船因为物理性质转来转去。

完整设置如图:

Player rigibody settings

移动玩家

现在该添加脚本了!目前为止,我们还没写代码。现在开始运用Unity真正的力量吧。

在Unity里,创建一个新的C#脚本到"Scripts"文件夹。命名为"PlayerScript"。

备注:你也可以用JavaScript。正如我们之前所说,代码片段在C#里, 但是它可以很容易被转换。

打开一个你习惯的编辑器或者使用"Sync"子菜单(在菜单栏点"Assets", 然后选"Sync MonoDevelop Project")来编辑脚本文件。

"Sync MonoDevelop Project": 这个子菜单有一点奇怪。首先,名字没有改变,即使你已经设置其他编辑器。 我们也推荐你初次使用脚本的时候使用这个菜单。因为Unity会创建不同的解决方案,并且把Unity库链接给这些解决方案(比如Visual Studio, Xamarin Studio 或者 MonoDevelop). 如果你仅仅是打开脚本文件,你的编译器可能会捕捉到一些错误,因为它没有通过链接关联Unity的库。 你几乎不会直接用它们编译,所以你可以忽略这些错误。但是最好还是在Unity里自动完成对象,一次通过编译。

如果你接触过XNA, 你就不会疑惑了。

你可以定义一些方法(称之为"Message",因为我们不会用到C#的继承系统)来让Unity识别和调用。

默认脚本带有StartUpdate两个方法。这里列举了部分使用了"Message"的函数:

  • Awake() 在对象被创建的时候调用。可以类似理解为构造函数。
  • Start()Awake()方法执行完成后开始执行。不同的是,Start()方法在在脚本被禁用后不会执行。(还记得那个在"Inspector"里的组件禁用/启用的复选框吧)。
  • Update() 在游戏循环的每一帧都会调用。
  • FixedUpdate() 在每个固定帧率时间后调用。当你处理物理相关(刚体和受力)的时候调用这个函数而不是Update()
  • Destroy() 在游戏对象摧毁的时候调用。这是执行代码或者一些清理释放资源操作的最后机会。

这里也有一些碰撞函数:

  • OnCollisionEnter2D(CollisionInfo2D info) 当其他碰撞盒子碰到了当前对象碰撞盒子时调用。
  • OnCollisionExit2D(CollisionInfo2D info) 其他碰撞盒子离开已经碰到过的当前对象碰撞盒子时调用。
  • OnTriggerEnter2D(Collider2D otherCollider) 其他碰撞盒子作为触发器碰到了当前对象碰撞盒子时调用。
  • OnTriggerExit2D(Collider2D otherCollider) 其他碰撞盒子作为触发器离开已经碰到过的当前对象碰撞盒子时调用。

这个解释有点无聊,但是不可避免需要这么说。对此感到很抱歉。

注意有关2D的后缀: 您现在应该已经注意到,几乎我们谈的是带有后缀“2D”任何东西。"Box Collider 2D", "Rigidbody 2D", "OnCollisionEnter2D" 还有 "OnTriggerEnter2D"函数等等的。这些新组件或方法已经在Unity4.3出现过了。 通过使用他们,你采用的是集成在Unity4.3的2D游戏物理引擎的(基于Box2D),而不是一个为3D游戏物理引擎(PhysX物理)。两个引擎都享有类似的概念和对象,但他们没有完全一样。如果你开始使用其中一个(Box2D更利于2D游戏),那就要一致都使用。这就是为什么我们使用的所有对象或方法有“2D”后缀。

我们使用它们的时候会再回到这里更加详尽的介绍它们。

对于playerd的脚本,我们会添加一些简单的控制: 键盘方向键控制飞船。

//
using UnityEngine;
// 
// Player controller and behavior 
//
public class PlayerScript : MonoBehaviour {
    // 1 - The speed of the ship
    public Vector2 speed = new Vector2(50, 50);
    // 2 - Store the movement 
    private Vector2 movement;
    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 - Move the game object 
        rigidbody2D.velocity = movement;
    } 
}

(注释中的数字请参考以下解释)

注意关于C#的通用约定: speed数字的访问可见性是public。在C#中,一个数值变量应该被设为private来表示变量属于类内部私有。但是暴露它作为一个公共变量,可以让你通过Unity的"Inspector"面板来修改,即使是在游戏运行的时候。这是Unity一个很棒的功能,让你无需编码就可以调整游戏的游戏性。记住,我们在这里是写脚本,不是传统意义上的C#编程。所以不需要遵守一些传统的规则和约定。

解释

  1. 我们首先顶一个公共变量让我们可以在"Inspector"视图里面修改它。这个变量代表了飞船的速度。
  2. 我们存下飞船每一帧的运动。
  3. 我们使用默认的轴,可以在"Edit" -> "Project Settings" -> "Input"里修改。这里会返回一个在[-1, 1]区间的值, 0代表禁止状态,1代表向右,-1代表向向左。
  4. 我们把方向和速度相乘。
  5. 我们修改刚体速度。这会告诉物理引擎移动游戏对象。我们会在FixedUpdate()做这些操作,因为推荐的操作就是在这里做物理相关的操作。

现在关联游戏对象的和脚本。

注意: 您可以通过从"Hierarchy"的"Project"的游戏对象上拖动脚本到游戏对象使之关联。您也可以点击"Add Component",并手动找到它。

点击编辑器的"Play"的按钮。飞船开始动了,你的游戏也成功运行了。恭喜你,你现在做出了一个"Hello, World!"级别的游戏了。:)

The player moves!

试着调整速度: 点击player,修改"Inspector"里speed的值并观察结果。

The inspector for a script

小心: 当执行游戏时做出的修改修改,在停止后都将丢失!对于调整游戏性这是一个很好的工具,但是一定要记住在停止调试的时候确定的数值。 然而,这种效果也很方便:您可以在执行过程中随便测试新的东西,不需要担心损害真实的项目。

这是我们游戏开始的起点,让我们加更多的东西!

第一个敌人

一个shmup如果没有成千上万敌人轰炸就什么也不是。

让我们使用一个只无辜的章鱼,名叫"Poulpi"吧:

Poulpi Sprite

(右键单击保存)

Sprite

再次创建新的sprite:

  1. 复制图片到"Textures"文件夹。
  2. 用这张图创建一个新的Sprite
  3. 修改Transform的"Scale"属性为(0.4, 0.4, 1)
  4. 添加一个尺寸为(4, 4)的"Box Collider 2D"。
  5. 添加一个"Rigidbody 2D"组件,"Gravity Scale"设置为0,同时勾选"Fixed Angles"。

保存为prefab...然后完毕!

Enemy Sprite in Unity

脚本

我们通过脚本实现一个简单的行为:Poulpi会在一个方向移动。

创建新脚本"MoveScript"。

我们可以称之为“EnemyScript”,但我们计划以后在另一种情况下重用它。

注意:通过Unity的基于组件的系统提供的模块化提供了一个很好的方式来根据不同特征分离脚本。当然,你仍然可以有一个巨大的脚本通过很多参数来搞定一切。这是你的选择,但我们强烈建议你别这样做。

我们将复制已经写入“PlayerScript”的某些部分来实现运动。我们要设计另外的变量表示方向(一个可以在"Inspector"修改的public成员变量):

//
using UnityEngine;
//
// Simply moves the current game object 
//
public class MoveScript : MonoBehaviour { 
    // 1 - Designer variables
    // Object speed 
    public Vector2 speed = new Vector2(10, 10);
    // Moving direction 
    public Vector2 direction = new Vector2(-1, 0);
    private Vector2 movement;
    void Update() { 
        // 2 - Movement 
        movement = new Vector2( speed.x * direction.x, speed.y * direction.y); 
    }
    void FixedUpdate() { 
        // Apply movement to the rigidbody 
        rigidbody2D.velocity = movement; 
    } 
}

把这个脚本和Poulpi关联。点击"Play": 它会像下面这样运动

Enemy is now moving

如果移动玩家到敌人面前,两个精灵会发生碰撞。他们目前只是阻止对方,因为我们没有定义碰撞行为呢。

下一步

你已经学会了如何添加一个玩家实体,通过键盘控制玩家。接着,我们创建了一个敌人和一个不成熟的AI。

现在我们想要摧毁那个移动的东西!因此,我们需要弹药!

标签: none

?>