123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- .. _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.
|