Creating the enemy

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

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

节点设置

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

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

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

../../_images/set_collision_mask.png

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

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

../../_images/mob_animations.gif

Set the Playing property in the Inspector to “On”.

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

Like the player images, these mob images need to be scaled down. Set the AnimatedSprite2D‘s Scale property to (0.75, 0.75).

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

保存该场景.

敌人的脚本

Add a script to the Mob like this:

GDScript

C#

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 <AnimatedSprite2D.hpp>
  8. #include <Godot.hpp>
  9. #include <RigidBody2D.hpp>
  10. class Mob : public godot::RigidBody2D {
  11. GODOT_CLASS(Mob, godot::RigidBody2D)
  12. godot::AnimatedSprite2D *_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

Now let’s look at the rest of the script. In _ready() we play the animation and randomly choose one of the three animation types:

GDScript

C#

C++

  1. func _ready():
  2. $AnimatedSprite2D.playing = true
  3. var mob_types = $AnimatedSprite2D.frames.get_animation_names()
  4. $AnimatedSprite2D.animation = mob_types[randi() % mob_types.size()]
  1. public override void _Ready()
  2. {
  3. var animSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
  4. animSprite2D.Playing = true;
  5. string[] mobTypes = animSprite2D.Frames.GetAnimationNames();
  6. animSprite2D.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::AnimatedSprite2D>("AnimatedSprite2D");
  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. }

First, we get the list of animation names from the AnimatedSprite2D’s frames property. This returns an Array containing all three animation names: ["walk", "swim", "fly"].

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

注解

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

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

GDScript

C#

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 场景.

With the player and enemies ready, in the next part, we’ll bring them together in a new scene. We’ll make enemies spawn randomly around the game board and move forward, turning our project into a playable game.