04.creating_the_enemy.rst 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. .. _doc_your_first_2d_game_creating_the_enemy:
  2. Creating the enemy
  3. ==================
  4. Now it's time to make the enemies our player will have to dodge. Their behavior
  5. will not be very complex: mobs will spawn randomly at the edges of the screen,
  6. choose a random direction, and move in a straight line.
  7. We'll create a ``Mob`` scene, which we can then *instance* to create any number
  8. of independent mobs in the game.
  9. Node setup
  10. ~~~~~~~~~~
  11. Click Scene -> New Scene from the top menu and add the following nodes:
  12. - :ref:`RigidBody2D <class_RigidBody2D>` (named ``Mob``)
  13. - :ref:`AnimatedSprite2D <class_AnimatedSprite2D>`
  14. - :ref:`CollisionShape2D <class_CollisionShape2D>`
  15. - :ref:`VisibleOnScreenNotifier2D <class_VisibleOnScreenNotifier2D>`
  16. Don't forget to set the children so they can't be selected, like you did with
  17. the Player scene.
  18. In the :ref:`RigidBody2D <class_RigidBody2D>` properties, set ``Gravity Scale``
  19. to ``0``, so the mob will not fall downward. In addition, under the
  20. :ref:`CollisionObject2D <class_CollisionObject2D>` section, uncheck the ``1`` inside the ``Mask`` property.
  21. This will ensure the mobs do not collide with each other.
  22. .. image:: img/set_collision_mask.webp
  23. Set up the :ref:`AnimatedSprite2D <class_AnimatedSprite2D>` like you did for the
  24. player. This time, we have 3 animations: ``fly``, ``swim``, and ``walk``. There
  25. are two images for each animation in the art folder.
  26. The ``Animation Speed`` property has to be set for each individual animation. Adjust it to ``3`` for all 3 animations.
  27. .. image:: img/mob_animations.webp
  28. You can use the "Play Animation" buttons on the right of the ``Animation Speed`` input field to preview your animations.
  29. We'll select one of these animations randomly so that the mobs will have some
  30. variety.
  31. Like the player images, these mob images need to be scaled down. Set the
  32. ``AnimatedSprite2D``'s ``Scale`` property to ``(0.75, 0.75)``.
  33. As in the ``Player`` scene, add a ``CapsuleShape2D`` for the collision. To align
  34. the shape with the image, you'll need to set the ``Rotation Degrees`` property
  35. to ``90`` (under "Transform" in the Inspector).
  36. Save the scene.
  37. Enemy script
  38. ~~~~~~~~~~~~
  39. Add a script to the ``Mob`` like this:
  40. .. tabs::
  41. .. code-tab:: gdscript GDScript
  42. extends RigidBody2D
  43. .. code-tab:: csharp
  44. using Godot;
  45. public partial class Mob : RigidBody2D
  46. {
  47. // Don't forget to rebuild the project.
  48. }
  49. .. code-tab:: cpp
  50. // Copy `player.gdns` to `mob.gdns` and replace `Player` with `Mob`.
  51. // Attach the `mob.gdns` file to the Mob node.
  52. // Create two files `mob.cpp` and `mob.hpp` next to `entry.cpp` in `src`.
  53. // This code goes in `mob.hpp`. We also define the methods we'll be using here.
  54. #ifndef MOB_H
  55. #define MOB_H
  56. #include <AnimatedSprite2D.hpp>
  57. #include <Godot.hpp>
  58. #include <RigidBody2D.hpp>
  59. class Mob : public godot::RigidBody2D {
  60. GODOT_CLASS(Mob, godot::RigidBody2D)
  61. godot::AnimatedSprite2D *_animated_sprite;
  62. public:
  63. void _init() {}
  64. void _ready();
  65. void _on_visible_on_screen_notifier_2d_screen_exited();
  66. static void _register_methods();
  67. };
  68. #endif // MOB_H
  69. Now let's look at the rest of the script. In ``_ready()`` we play the animation
  70. and randomly choose one of the three animation types:
  71. .. tabs::
  72. .. code-tab:: gdscript GDScript
  73. func _ready():
  74. var mob_types = $AnimatedSprite2D.sprite_frames.get_animation_names()
  75. $AnimatedSprite2D.play(mob_types[randi() % mob_types.size()])
  76. .. code-tab:: csharp
  77. public override void _Ready()
  78. {
  79. var animatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
  80. string[] mobTypes = animatedSprite2D.SpriteFrames.GetAnimationNames();
  81. animatedSprite2D.Play(mobTypes[GD.Randi() % mobTypes.Length]);
  82. }
  83. .. code-tab:: cpp
  84. // This code goes in `mob.cpp`.
  85. #include "mob.hpp"
  86. #include <RandomNumberGenerator.hpp>
  87. #include <SpriteFrames.hpp>
  88. void Mob::_ready() {
  89. godot::Ref<godot::RandomNumberGenerator> random = godot::RandomNumberGenerator::_new();
  90. _animated_sprite = get_node<godot::AnimatedSprite2D>("AnimatedSprite2D");
  91. _animated_sprite->set_playing(true);
  92. godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names();
  93. _animated_sprite->set_animation(mob_types[random->randi() % mob_types.size()]);
  94. }
  95. First, we get the list of animation names from the AnimatedSprite2D's ``frames``
  96. property. This returns an Array containing all three animation names: ``["walk",
  97. "swim", "fly"]``.
  98. We then need to pick a random number between ``0`` and ``2`` to select one of
  99. these names from the list (array indices start at ``0``). ``randi() % n``
  100. selects a random integer between ``0`` and ``n-1``.
  101. The last piece is to make the mobs delete themselves when they leave the screen.
  102. Connect the ``screen_exited()`` signal of the ``VisibleOnScreenNotifier2D`` node
  103. to the ``Mob`` and add this code:
  104. .. tabs::
  105. .. code-tab:: gdscript GDScript
  106. func _on_visible_on_screen_notifier_2d_screen_exited():
  107. queue_free()
  108. .. code-tab:: csharp
  109. private void OnVisibleOnScreenNotifier2DScreenExited()
  110. {
  111. QueueFree();
  112. }
  113. .. code-tab:: cpp
  114. // This code goes in `mob.cpp`.
  115. void Mob::_on_visible_on_screen_notifier_2d_screen_exited() {
  116. queue_free();
  117. }
  118. This completes the `Mob` scene.
  119. With the player and enemies ready, in the next part, we'll bring them together
  120. in a new scene. We'll make enemies spawn randomly around the game board and move
  121. forward, turning our project into a playable game.