创建敌人

是时候去做一些玩家必须躲避的敌人了. 它们的行为很简单: 怪物将随机生成在屏幕的边缘, 沿着随机的方向直线移动.

我们将创建一个 Mob 的怪物场景,以便在游戏中独立实例化出任意数量的怪物。

节点设置

点击“场景 -> 新建场景”,然后添加以下节点:

别忘了设置子项,使其无法被选中,就像你对 Player 场景所做的那样。

RigidBody2D 属性中,将 Gravity Scale 设置为 0,这样怪物就不会下坠。此外,在 CollisionObject2D 部分下,点击 Mask 属性并去除第一个复选框的勾选。这会确保怪物就不会彼此碰撞。

../../_images/set_collision_mask.png

像设置玩家一样设置 AnimatedSprite。这一次,我们有3个动画:flyswimwalk,每个动画在 art 文件夹中都有 2 张图片。

对于所有动画,将“速度(FPS)”调整为 3

../../_images/mob_animations.gif

将“检查器”中的 Playing 属性设置为“启用”。

我们将随机选择其中一个动画,以便小怪有一些变化。

像玩家的图像一样,这些小怪的图像也要缩小。请将 AnimatedSpriteScale 属性设为 (0.75, 0.75)

像在 Player 场景中一样,为碰撞添加一个 CapsuleShape2D。为了使形状与图像对齐,您需要将 Rotation Degrees 属性设为 90(在“检查器”的“Transorm”下)。

保存该场景。

敌人的脚本

像这样将脚本添加到 Mob 上:

GDScriptC#C++

  1. extends RigidBody2D
  1. public class Mob : RigidBody2D
  2. {
  3. // Don't forget to rebuild the project.
  4. }
  1. // Copy `player.gdns` to `mob.gdns` and replace `Player` with `Mob`.
  2. // Attach the `mob.gdns` file to the Mob node.
  3. // Create two files `mob.cpp` and `mob.hpp` next to `entry.cpp` in `src`.
  4. // This code goes in `mob.hpp`. We also define the methods we'll be using here.
  5. #ifndef MOB_H
  6. #define MOB_H
  7. #include <AnimatedSprite.hpp>
  8. #include <Godot.hpp>
  9. #include <RigidBody2D.hpp>
  10. class Mob : public godot::RigidBody2D {
  11. GODOT_CLASS(Mob, godot::RigidBody2D)
  12. godot::AnimatedSprite *_animated_sprite;
  13. public:
  14. void _init() {}
  15. void _ready();
  16. void _on_VisibilityNotifier2D_screen_exited();
  17. static void _register_methods();
  18. };
  19. #endif // MOB_H

现在让我们看一下脚本的其余部分。在 _ready() 中,我们从三个动画类型中随机选择一个播放:

GDScriptC#C++

  1. func _ready():
  2. $AnimatedSprite.playing = true
  3. var mob_types = $AnimatedSprite.frames.get_animation_names()
  4. $AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
  1. public override void _Ready()
  2. {
  3. var animSprite = GetNode<AnimatedSprite>("AnimatedSprite");
  4. animSprite.Playing = true;
  5. string[] mobTypes = animSprite.Frames.GetAnimationNames();
  6. animSprite.Animation = mobTypes[GD.Randi() % mobTypes.Length];
  7. }
  1. // This code goes in `mob.cpp`.
  2. #include "mob.hpp"
  3. #include <RandomNumberGenerator.hpp>
  4. #include <SpriteFrames.hpp>
  5. void Mob::_ready() {
  6. godot::Ref<godot::RandomNumberGenerator> random = godot::RandomNumberGenerator::_new();
  7. random->randomize();
  8. _animated_sprite = get_node<godot::AnimatedSprite>("AnimatedSprite");
  9. _animated_sprite->_set_playing(true);
  10. godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names();
  11. _animated_sprite->set_animation(mob_types[random->randi() % mob_types.size()]);
  12. }

首先,我们从 AnimatedSprite 的 frames 属性中获取动画名称的列表。返回的是一个数组,该数组包含三个动画名称:["walk", "swim", "fly"]

然后我们需要在 02 之间选取一个随机的数字, 以在列表中选择一个名称(数组索引以 0 起始). randi() % n 会在 0 and n-1 之中选择一个随机整数.

备注

如果希望每次运行场景时生成的“随机数”都不同,则必须使用 randomize()。我们将在 Main 场景中使用 randomize(),因此在这里不需要添加。

最后一步是让怪物在超出屏幕时删除自己. 连接 VisibilityNotifier2D 节点的 screen_exited() 信号并添加以下代码:

GDScriptC#C++

  1. func _on_VisibilityNotifier2D_screen_exited():
  2. queue_free()
  1. public void OnVisibilityNotifier2DScreenExited()
  2. {
  3. QueueFree();
  4. }
  1. // This code goes in `mob.cpp`.
  2. void Mob::_on_VisibilityNotifier2D_screen_exited() {
  3. queue_free();
  4. }

这样就完成了 Mob 场景。

玩家和敌人已经准备就绪,接下来,我们将在一个新的场景中把他们放到一起。我们将使敌人在游戏板上随机生成并前进,我们的项目将变成一个能玩的游戏。