Browse Source

Write 3D tutorial part 9: animation

Nathan Lovato 4 years ago
parent
commit
dec794f7de
25 changed files with 374 additions and 0 deletions
  1. 373 0
      getting_started/first_3d_game/09.adding_animations.rst
  2. BIN
      getting_started/first_3d_game/img/09.adding_animations/01.animation_player_dock.png
  3. BIN
      getting_started/first_3d_game/img/09.adding_animations/02.new_animation.png
  4. BIN
      getting_started/first_3d_game/img/09.adding_animations/03.float_name.png
  5. BIN
      getting_started/first_3d_game/img/09.adding_animations/03.timeline.png
  6. BIN
      getting_started/first_3d_game/img/09.adding_animations/04.autoplay_and_loop.png
  7. BIN
      getting_started/first_3d_game/img/09.adding_animations/05.pin_icon.png
  8. BIN
      getting_started/first_3d_game/img/09.adding_animations/06.animation_duration.png
  9. BIN
      getting_started/first_3d_game/img/09.adding_animations/07.editable_timeline.png
  10. BIN
      getting_started/first_3d_game/img/09.adding_animations/08.zoom_slider.png
  11. BIN
      getting_started/first_3d_game/img/09.adding_animations/09.creating_first_keyframe.png
  12. BIN
      getting_started/first_3d_game/img/09.adding_animations/10.initial_keys.png
  13. BIN
      getting_started/first_3d_game/img/09.adding_animations/11.moving_keys.png
  14. BIN
      getting_started/first_3d_game/img/09.adding_animations/12.second_keys_values.png
  15. BIN
      getting_started/first_3d_game/img/09.adding_animations/13.second_keys.png
  16. BIN
      getting_started/first_3d_game/img/09.adding_animations/14.play_button.png
  17. BIN
      getting_started/first_3d_game/img/09.adding_animations/15.box_select.png
  18. BIN
      getting_started/first_3d_game/img/09.adding_animations/16.easing_property.png
  19. BIN
      getting_started/first_3d_game/img/09.adding_animations/17.ease_out.png
  20. BIN
      getting_started/first_3d_game/img/09.adding_animations/18.ease_out_second_rotation_key.png
  21. BIN
      getting_started/first_3d_game/img/09.adding_animations/19.ease_in_second_translation_key.png
  22. BIN
      getting_started/first_3d_game/img/09.adding_animations/20.float_animation.gif
  23. BIN
      getting_started/first_3d_game/img/09.adding_animations/21.script_icon.png
  24. BIN
      getting_started/first_3d_game/img/09.adding_animations/22.merge_from_scene.png
  25. 1 0
      getting_started/first_3d_game/index.rst

+ 373 - 0
getting_started/first_3d_game/09.adding_animations.rst

@@ -0,0 +1,373 @@
+Character animation
+===================
+
+In this final lesson, we'll use Godot's built-in animation tools to make our
+characters float and flap. You'll learn to design animations in the editor and
+use code to make your game feel alive.
+
+|image0|
+
+We'll start with an introduction to using the animation editor.
+
+Using the animation editor
+--------------------------
+
+The engine comes with tools to author animations in the editor. You can then use
+the code to play and control them at runtime.
+
+Open the player scene, select the player node, and add an animation player node.
+
+The *Animation* dock appears in the bottom panel.
+
+|image1|
+
+It features a toolbar and the animation drop-down menu at the top, a track
+editor in the middle that's currently empty, and filter, snap, and zoom options
+at the bottom.
+
+Let's create an animation. Click on *Animation -> New*.
+
+|image2|
+
+Name the animation "float".
+
+|image3|
+
+Once you created the animation, the timeline appears with numbers representing
+time in seconds.
+
+|image4|
+
+We want the animation to start playback automatically at the start of the game.
+Also, it should loop.
+
+To do so, you can click the button with an "A+" icon in the animation toolbar
+and the looping arrows, respectively.
+
+|image5|
+
+You can also pin the animation editor by clicking the pin icon in the top-right.
+This prevents it from folding when you click on the viewport and deselect the
+nodes.
+
+|image6|
+
+Set the animation duration to ``1.2`` seconds in the top-right of the dock.
+
+|image7|
+
+You should see the gray ribbon widen a bit. It shows you the start and end of
+your animation and the vertical blue line is your time cursor.
+
+|image8|
+
+You can click and drag the slider in the bottom-right to zoom in and out of the
+timeline.
+
+|image9|
+
+The float animation
+-------------------
+
+With the animation player node, you can animate most properties on as many nodes
+as you need. Notice the key icon next to properties in the *Inspector*. You can
+click any of them to create a keyframe, a time and value pair for the
+corresponding property. The keyframe gets inserted where your time cursor is in
+the timeline.
+
+Let's insert our first keys. Here, we will animate both the translation and the
+rotation of the *Character* node.
+
+Select the *Character* and click the key icon next to *Translation* in the
+*Inspector*. Do the same for *Rotation Degrees*.
+
+|image10|
+
+Two tracks appear in the editor with a diamond icon representing each keyframe.
+
+|image11|
+
+You can click and drag on the diamonds to move them in time. Move the
+translation key to ``0.1`` seconds and the rotation key to ``0.2`` seconds.
+
+|image12|
+
+Move the time cursor to ``0.5`` seconds by clicking and dragging on the gray
+timeline. In the *Inspector*, set the *Translation*'s *Y* axis to about
+``0.65`` meters and the *Rotation Degrees*' *X* axis to ``8``.
+
+|image13|
+
+Create a keyframe for both properties and shift the translation key to ``0.7``
+seconds by dragging it on the timeline.
+
+|image14|
+
+.. note::
+
+    A lecture on the principles of animation is beyond the scope of this
+    tutorial. Just note that you don't want to time and space everything evenly.
+    Instead, animators play with timing and spacing, two core animation
+    principles. You want to offset and contrast in your character's motion to
+    make them feel alive.
+
+Move the time cursor to the end of the animation, at ``1.2`` seconds. Set the Y
+translation to about ``0.35`` and the X rotation to ``-9`` degrees. Once again,
+create a key for both properties.
+
+You can preview the result by clicking the play button or pressing Shift
+D. Click the stop button or press S to stop playback.
+
+|image15|
+
+You can see that the engine interpolates between your keyframes to produce a
+continuous animation. At the moment, though, the motion feels very robotic. This
+is because the default interpolation is linear, causing constant transitions,
+unlike how living things move in the real world.
+
+We can control the transition between keyframes using easing curves.
+
+Click and drag around the first two keys in the timeline to box select them.
+
+|image16|
+
+You can edit the properties of both keys simultaneously in the *Inspector*,
+where you can see an *Easing* property.
+
+|image17|
+
+Click and drag on the curve, pulling it towards the left. This will make it
+ease-out, that is to say, transition fast initially and slow down as the time
+cursor reaches the next keyframe.
+
+|image18|
+
+Play the animation again to see the difference. The first half should already
+feel a bit bouncier.
+
+Apply an ease-out to the second keyframe in the rotation track.
+
+|image19|
+
+Do the opposite for the second translation keyframe, dragging it to the right.
+
+|image20|
+
+Your animation should look something like this.
+
+|image21|
+
+.. note::
+
+    Animations update the properties of the animated nodes every frame,
+    overriding initial values. If we directly animated the *Player* node, it
+    would prevent us from moving it in code. This is where the *Pivot* node
+    comes in handy: even though we animated the *Character*, we can still move
+    and rotate the *Pivot* and layer changes on top of the animation in a
+    script.
+
+If you play the game, the player's creature will now float!
+
+If the creature is a little too close to the floor, you can move the *Pivot* up
+to offset it.
+
+Controlling the animation in code
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We can use code to control the animation playback based on the player's input.
+Let's change the animation speed when the character is moving.
+
+Open the *Player*'s script by clicking the script icon next to it.
+
+|image22|
+
+In ``_physics_process()``, after the line where we check the ``direction``
+vector, add the following code.
+
+::
+
+   func _physics_process(delta):
+       #...
+       #if direction.length() > 0:
+           #...
+           $AnimationPlayer.playback_speed = 4
+       else:
+           $AnimationPlayer.playback_speed = 1
+
+This code makes it so when the player moves, we multiply the playback speed by
+``4``. When they stop, we reset it to normal.
+
+We mentioned that the pivot could layer transforms on top of the animation. We
+can make the character arc when jumping using the following line of code. Add it
+at the end of ``_physics_process()``.
+
+::
+
+   func _physics_process(delta):
+       #...
+       $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse
+
+Animating the mobs
+------------------
+
+Here's another nice trick with animations in Godot: as long as you use a similar
+node structure, you can copy them to different scenes.
+
+For example, both the *Mob* and the *Player* scenes have a *Pivot* and a
+*Character* node, so we can reuse animations between them.
+
+We're going to duplicate the animation using a feature called "merge from
+scene".
+
+Open the *Mob* scene, right-click on the *Mob* node and select *Merge From
+Scene*.
+
+|image23|
+
+Double-click ``Player.tscn`` to open it and import the *AnimationPlayer*. That's
+it; all monsters will now play the float animation.
+
+We can change the playback speed based on the creature's ``random_speed``. Open
+the *Mob*'s script and at the end of the ``initialize()`` function, add the
+following line.
+
+::
+
+   func initialize(start_position, player_position):
+       #...
+       $AnimationPlayer.playback_speed = random_speed / min_speed
+
+And with that, you finished coding your first complete 3D game.
+
+**Congratulations**!
+
+In the next part, we'll quickly recap what you learned and give you some links
+to keep learning more. But for now, here are the complete ``Player.gd`` and
+``Mob.gd`` so you can check your code against them.
+
+Here's the *Player* script.
+
+::
+
+   extends KinematicBody
+
+   # Emitted when the player was hit by a mob.
+   signal hit
+
+   # How fast the player moves in meters per second.
+   export var speed = 14
+   # The downward acceleration when in the air, in meters per second per second.
+   export var fall_acceleration = 75
+   # Vertical impulse applied to the character upon jumping in meters per second.
+   export var jump_impulse = 20
+   # Vertical impulse applied to the character upon bouncing over a mob in meters per second.
+   export var bounce_impulse = 16
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(delta):
+       var direction = Vector3.ZERO
+
+       if Input.is_action_pressed("move_right"):
+           direction.x += 1
+       if Input.is_action_pressed("move_left"):
+           direction.x -= 1
+       if Input.is_action_pressed("move_down"):
+           direction.z += 1
+       if Input.is_action_pressed("move_up"):
+           direction.z -= 1
+
+       if direction.length() > 0:
+           direction = direction.normalized()
+           $Pivot.look_at(translation + direction, Vector3.UP)
+           $AnimationPlayer.playback_speed = 4
+       else:
+           $AnimationPlayer.playback_speed = 1
+
+       velocity.x = direction.x * speed
+       velocity.z = direction.z * speed
+
+       # Jumping
+       if is_on_floor() and Input.is_action_just_pressed("jump"):
+           velocity.y += jump_impulse
+
+       velocity.y -= fall_acceleration * delta
+       velocity = move_and_slide(velocity, Vector3.UP)
+
+       for index in range(get_slide_count()):
+           var collision = get_slide_collision(index)
+           if collision.collider.is_in_group("mob"):
+               var mob = collision.collider
+               if Vector3.UP.dot(collision.normal) > 0.1:
+                   mob.squash()
+                   velocity.y = bounce_impulse
+
+       $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse
+
+
+   func die():
+       emit_signal("hit")
+       queue_free()
+
+
+   func _on_MobDetector_body_entered(_body):
+       die()
+
+And the *Mob*'s script.
+
+::
+
+   extends KinematicBody
+
+   # Minimum speed of the mob in meters per second.
+   export var min_speed = 10
+   # Maximum speed of the mob in meters per second.
+   export var max_speed = 18
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(_delta):
+       move_and_slide(velocity)
+
+
+   func initialize(start_position, player_position):
+       translation = start_position
+       look_at(player_position, Vector3.UP)
+       rotate_y(rand_range(-PI / 4, PI / 4))
+
+       var random_speed = rand_range(min_speed, max_speed)
+       velocity = Vector3.FORWARD * random_speed
+       velocity = velocity.rotated(Vector3.UP, rotation.y)
+
+       $AnimationPlayer.playback_speed = random_speed / min_speed
+
+
+   func _on_VisibilityNotifier_screen_exited():
+       queue_free()
+
+.. |image0| image:: img/squash-the-creeps-final.gif
+.. |image1| image:: img/09.adding_animations/01.animation_player_dock.png
+.. |image2| image:: img/09.adding_animations/02.new_animation.png
+.. |image3| image:: img/09.adding_animations/03.float_name.png
+.. |image4| image:: img/09.adding_animations/03.timeline.png
+.. |image5| image:: img/09.adding_animations/04.autoplay_and_loop.png
+.. |image6| image:: img/09.adding_animations/05.pin_icon.png
+.. |image7| image:: img/09.adding_animations/06.animation_duration.png
+.. |image8| image:: img/09.adding_animations/07.editable_timeline.png
+.. |image9| image:: img/09.adding_animations/08.zoom_slider.png
+.. |image10| image:: img/09.adding_animations/09.creating_first_keyframe.png
+.. |image11| image:: img/09.adding_animations/10.initial_keys.png
+.. |image12| image:: img/09.adding_animations/11.moving_keys.png
+.. |image13| image:: img/09.adding_animations/12.second_keys_values.png
+.. |image14| image:: img/09.adding_animations/13.second_keys.png
+.. |image15| image:: img/09.adding_animations/14.play_button.png
+.. |image16| image:: img/09.adding_animations/15.box_select.png
+.. |image17| image:: img/09.adding_animations/16.easing_property.png
+.. |image18| image:: img/09.adding_animations/17.ease_out.png
+.. |image19| image:: img/09.adding_animations/18.ease_out_second_rotation_key.png
+.. |image20| image:: img/09.adding_animations/19.ease_in_second_translation_key.png
+.. |image21| image:: img/09.adding_animations/20.float_animation.gif
+.. |image22| image:: img/09.adding_animations/21.script_icon.png
+.. |image23| image:: img/09.adding_animations/22.merge_from_scene.png

BIN
getting_started/first_3d_game/img/09.adding_animations/01.animation_player_dock.png


BIN
getting_started/first_3d_game/img/09.adding_animations/02.new_animation.png


BIN
getting_started/first_3d_game/img/09.adding_animations/03.float_name.png


BIN
getting_started/first_3d_game/img/09.adding_animations/03.timeline.png


BIN
getting_started/first_3d_game/img/09.adding_animations/04.autoplay_and_loop.png


BIN
getting_started/first_3d_game/img/09.adding_animations/05.pin_icon.png


BIN
getting_started/first_3d_game/img/09.adding_animations/06.animation_duration.png


BIN
getting_started/first_3d_game/img/09.adding_animations/07.editable_timeline.png


BIN
getting_started/first_3d_game/img/09.adding_animations/08.zoom_slider.png


BIN
getting_started/first_3d_game/img/09.adding_animations/09.creating_first_keyframe.png


BIN
getting_started/first_3d_game/img/09.adding_animations/10.initial_keys.png


BIN
getting_started/first_3d_game/img/09.adding_animations/11.moving_keys.png


BIN
getting_started/first_3d_game/img/09.adding_animations/12.second_keys_values.png


BIN
getting_started/first_3d_game/img/09.adding_animations/13.second_keys.png


BIN
getting_started/first_3d_game/img/09.adding_animations/14.play_button.png


BIN
getting_started/first_3d_game/img/09.adding_animations/15.box_select.png


BIN
getting_started/first_3d_game/img/09.adding_animations/16.easing_property.png


BIN
getting_started/first_3d_game/img/09.adding_animations/17.ease_out.png


BIN
getting_started/first_3d_game/img/09.adding_animations/18.ease_out_second_rotation_key.png


BIN
getting_started/first_3d_game/img/09.adding_animations/19.ease_in_second_translation_key.png


BIN
getting_started/first_3d_game/img/09.adding_animations/20.float_animation.gif


BIN
getting_started/first_3d_game/img/09.adding_animations/21.script_icon.png


BIN
getting_started/first_3d_game/img/09.adding_animations/22.merge_from_scene.png


+ 1 - 0
getting_started/first_3d_game/index.rst

@@ -62,5 +62,6 @@ Contents
    06.jump_and_squash
    06.jump_and_squash
    07.killing_player
    07.killing_player
    08.score_and_replay
    08.score_and_replay
+   09.adding_animations
 
 
 .. |image0| image:: img/squash-the-creeps-final.gif
 .. |image0| image:: img/squash-the-creeps-final.gif