Files

181 lines
5.6 KiB
ReStructuredText

.. _doc_your_first_2d_game_creating_the_enemy:
Creating the enemy
==================
Now it's time to make the enemies our player will have to dodge. Their behavior
will not be very complex: mobs will spawn randomly at the edges of the screen,
choose a random direction, and move in a straight line.
We'll create a ``Mob`` scene, which we can then *instance* to create any number
of independent mobs in the game.
Node setup
~~~~~~~~~~
Click Scene -> New Scene and add the following nodes:
- :ref:`RigidBody2D <class_RigidBody2D>` (named ``Mob``)
- :ref:`AnimatedSprite <class_AnimatedSprite>`
- :ref:`CollisionShape2D <class_CollisionShape2D>`
- :ref:`VisibilityNotifier2D <class_VisibilityNotifier2D>`
Don't forget to set the children so they can't be selected, like you did with
the Player scene.
In the :ref:`RigidBody2D <class_RigidBody2D>` properties, set ``Gravity Scale``
to ``0``, so the mob will not fall downward. In addition, under the
:ref:`CollisionObject2D <class_CollisionObject2D>` section, click the ``Mask`` property and uncheck the first
box. This will ensure the mobs do not collide with each other.
.. image:: img/set_collision_mask.png
Set up the :ref:`AnimatedSprite <class_AnimatedSprite>` like you did for the
player. This time, we have 3 animations: ``fly``, ``swim``, and ``walk``. There
are two images for each animation in the art folder.
Adjust the "Speed (FPS)" to ``3`` for all animations.
.. image:: img/mob_animations.gif
Set the ``Playing`` property in the Inspector to "On".
We'll select one of these animations randomly so that the mobs will have some
variety.
Like the player images, these mob images need to be scaled down. Set the
``AnimatedSprite``'s ``Scale`` property to ``(0.75, 0.75)``.
As in the ``Player`` scene, add a ``CapsuleShape2D`` for the collision. To align
the shape with the image, you'll need to set the ``Rotation Degrees`` property
to ``90`` (under "Transform" in the Inspector).
Save the scene.
Enemy script
~~~~~~~~~~~~
Add a script to the ``Mob`` like this:
.. tabs::
.. code-tab:: gdscript GDScript
extends RigidBody2D
.. code-tab:: csharp
public class Mob : RigidBody2D
{
// Don't forget to rebuild the project.
}
.. code-tab:: cpp
// Copy `player.gdns` to `mob.gdns` and replace `Player` with `Mob`.
// Attach the `mob.gdns` file to the Mob node.
// Create two files `mob.cpp` and `mob.hpp` next to `entry.cpp` in `src`.
// This code goes in `mob.hpp`. We also define the methods we'll be using here.
#ifndef MOB_H
#define MOB_H
#include <AnimatedSprite.hpp>
#include <Godot.hpp>
#include <RigidBody2D.hpp>
class Mob : public godot::RigidBody2D {
GODOT_CLASS(Mob, godot::RigidBody2D)
godot::AnimatedSprite *_animated_sprite;
public:
void _init() {}
void _ready();
void _on_VisibilityNotifier2D_screen_exited();
static void _register_methods();
};
#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:
.. tabs::
.. code-tab:: gdscript GDScript
func _ready():
$AnimatedSprite.playing = true
var mob_types = $AnimatedSprite.frames.get_animation_names()
$AnimatedSprite.animation = mob_types[randi() % mob_types.size()]
.. code-tab:: csharp
public override void _Ready()
{
var animSprite = GetNode<AnimatedSprite>("AnimatedSprite");
animSprite.Playing = true;
string[] mobTypes = animSprite.Frames.GetAnimationNames();
animSprite.Animation = mobTypes[GD.Randi() % mobTypes.Length];
}
.. code-tab:: cpp
// This code goes in `mob.cpp`.
#include "mob.hpp"
#include <RandomNumberGenerator.hpp>
#include <SpriteFrames.hpp>
void Mob::_ready() {
godot::Ref<godot::RandomNumberGenerator> random = godot::RandomNumberGenerator::_new();
random->randomize();
_animated_sprite = get_node<godot::AnimatedSprite>("AnimatedSprite");
_animated_sprite->_set_playing(true);
godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names();
_animated_sprite->set_animation(mob_types[random->randi() % mob_types.size()]);
}
First, we get the list of animation names from the AnimatedSprite's ``frames``
property. This returns an Array containing all three animation names: ``["walk",
"swim", "fly"]``.
We then need to pick a random number between ``0`` and ``2`` to select one of
these names from the list (array indices start at ``0``). ``randi() % n``
selects a random integer between ``0`` and ``n-1``.
.. note:: You must use ``randomize()`` if you want your sequence of "random"
numbers to be different every time you run the scene. We're going to
use ``randomize()`` in our ``Main`` scene, so we won't need it here.
The last piece is to make the mobs delete themselves when they leave the screen.
Connect the ``screen_exited()`` signal of the ``VisibilityNotifier2D`` node and
add this code:
.. tabs::
.. code-tab:: gdscript GDScript
func _on_VisibilityNotifier2D_screen_exited():
queue_free()
.. code-tab:: csharp
public void OnVisibilityNotifier2DScreenExited()
{
QueueFree();
}
.. code-tab:: cpp
// This code goes in `mob.cpp`.
void Mob::_on_VisibilityNotifier2D_screen_exited() {
queue_free();
}
This completes the `Mob` scene.
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.