Skip to content

Commit

Permalink
custom animations (beta!)
Browse files Browse the repository at this point in the history
  • Loading branch information
jabsatz committed Jul 18, 2022
1 parent f9c3ebb commit 163c0f3
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 40 deletions.
62 changes: 59 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Plugin for managing transitions and Node references between scenes. Expect more

Alternatively, you may download this repo from github and add the `addons` folder to your project, then enable it from your Project Settings~

## How to use
## Features

**Simple functions to change/reload scenes with a nice transition** (no manual animation required)

When SceneManager is installed, you will gain access to the `SceneManager` singleton. You can then trigger it's methods directly like so:

Expand All @@ -27,7 +29,39 @@ There are similar methods for reloading your scene and making a fade without tra

---

We have also added the Entity Singleton feature in v0.3! It's an easy way to keep track of important Nodes that only exist once in your scenes (like your player or your level tilemap), regardless of the name they have.
**Custom animations** (BETA)

Now you can add your very own `AnimationPlayer` to complement the one packaged in SceneManager!

First off, setup a new `Scene` that has an `AnimationPlayer` as its **root** node. Make and name all the animations you want, and make sure you have a `RESET` animation that keeps all your visuals out of the screen.

![Custom animation setup](/custom_animation_setup.gif)

Then, set your player in SceneManager via code (We recommend doing this as early as possible in your game. For example in the `_ready()` method of an autoload or your first scene):

```gd
SceneManager.set_animation_player('res://demo/animation_player.tscn')
```

Now, whenever you call `SceneManager.change_scene()` or any other transition function, you will be able to add new `animation_name`, `animation_name_enter` and/or `animation_name_leave` options with the name of your animation.

You can even match an enter/leave pattern from `SceneManager`! Just remember that `animation_name` takes precedence, so you'll want to use the opposite enter/leave suffix on `animation_name`.

```
SceneManager.change_scene('res://demo/test2.tscn', {"animation_name_enter": "roll", "pattern_leave": "squares"})
```

![Custom animation demonstration](/custom_animation_showcase.gif)

As always, check the demo in this repo or the [API](#api) docs for more info!

**BETA DISCLOSURE**: This feature is brand-new, bug reports and suggestions are appreciated in the Github issues tab!

---

**Entity Singletons**

An easy way to keep track of important Nodes that only exist once in your scenes (like your player or your level tilemap), regardless of the name they have.

First off, you just have to set the flag and name in the editor:

Expand Down Expand Up @@ -62,7 +96,7 @@ but for ease-of-use we recommend using the `reload_scene(options)` function expl

You can pass the following options to this function in a dictionary:

- `speed : float = 2`: Speed of the moving transition.
- `speed : float = 2`: Speed of the moving transition. (Also affects custom animations)
- `color : Color = Color('#000000')`: Color to use for the transition screen. It's black by default.
- `wait_time : float = 0.5`: Time spent in the transition screen while switching scenes. Leaving it at 0 with fade will result in the screen not turning black, so it waits a little bit by default.
- `skip_scene_change : Bool = false`: If set to true, skips the actual scene change/reload, leaving only the transition.
Expand All @@ -75,6 +109,9 @@ You can pass the following options to this function in a dictionary:
- `ease : float = 1.0`: Amount of ease the animation should have during the transition.
- `ease_enter : float = ease`: Amount of ease the animation should have during the fade-to-black transition.
- `ease_leave : float = ease`: Amount of ease the animation should have during the fade-from-black transition.
- `animation_name : String`: Name of an animation set in `set_animation_player()` which will be played for both in and out transitions
- `animation_name_enter : String`: Name of an animation set in `set_animation_player()` which will be played for the fade-to-black transition
- `animation_name_leave : String`: Name of an animation set in `set_animation_player()` which will be played for the fade-from-black transition

The following patterns are available out-of-the-box:

Expand Down Expand Up @@ -127,6 +164,25 @@ It can take the following options, with the same defaults as `change_scene`:
- `invert_on_leave`
- `ease`

### `func set_animation_player(animation_player: String || PackedScene)`

Set a custom `AnimationPlayer` make your own transitions easily. We recommend doing this only once as early as possible in your game, for example in the `_ready()` method of an autoload or your first scene.

This method receives a path to (or a `PackedScene` of) the location of your `AnimationPlayer` scene.

```gd
SceneManager.set_animation_player('res://demo/animation_player.tscn')
```

Your `AnimationPlayer` scene must follow these rules:

1. The root node **must** be a Godot `AnimationPlayer`
2. The animation player **must** have a `RESET` animation that keeps all your graphics outside of the viewport. This is because the animation player will always be rendered on top of your screen, so any leftovers will always be rendered (you should notice right away if this is the case)

Add your animations to this `AnimationPlayer` and then use their names in the options of any SceneManager transition function as `animation_name`, `animation_name_enter` or `animation_name_leave`

Please check the demo files in this repository for a complete example.

### `func get_entity(entity_name: String)`

Get a reference to a named entity (node) in your scene. To define entity names go to the desired node in the editor inspector and you'll see two new properties: `Singleton entity` and `Entity name`. Check the `Singleton entity` checkbox to have this node saved to the SceneManager entity dictionary and write a friendly `Entity name` to be used in this function. Afterwards, you'll be able to access it within the scene.
Expand Down
110 changes: 84 additions & 26 deletions addons/scene_manager/SceneManager.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ onready var _root := _tree.get_root()
onready var _current_scene := _tree.current_scene
onready var _animation_player := $AnimationPlayer
onready var _shader_blend_rect := $CanvasLayer/ColorRect
var _user_animation_player: AnimationPlayer

enum FadeTypes { Fade, ShaderFade }

Expand All @@ -25,12 +26,15 @@ var default_options := {
"skip_scene_change": false,
"skip_fade_out": false,
"skip_fade_in": false,
"animation_name": null
}
# extra_options = {
# "pattern_enter": DEFAULT_IMAGE,
# "pattern_leave": DEFAULT_IMAGE,
# "ease_enter": 1.0,
# "ease_leave": 1.0,
# "animation_name_enter": null,
# "animation_name_leave": null,
# }

var new_names := {
Expand Down Expand Up @@ -114,6 +118,10 @@ func _get_final_options(initial_options: Dictionary) -> Dictionary:
if not ease_key in options:
options[ease_key] = options["ease"]

for animation_name_key in ["animation_name_enter", "animation_name_leave"]:
if not animation_name_key in options:
options[animation_name_key] = options["animation_name"]

return options


Expand All @@ -136,17 +144,56 @@ func _reload_scene() -> void:
func _replace_scene(scene) -> void:
_current_scene.queue_free()
emit_signal("scene_unloaded")
var following_scene = _load_scene_resource(scene)
var following_scene = _load_resource(scene)
_current_scene = following_scene.instance()
yield(_tree.create_timer(0.0), "timeout")
_root.add_child(_current_scene)
_tree.set_current_scene(_current_scene)


func _load_scene_resource(scene) -> Resource:
if scene is PackedScene:
return scene
return ResourceLoader.load(scene)
func _load_resource(resource) -> Resource:
if resource is PackedScene:
return resource
return ResourceLoader.load(resource)


func _user_fade_out(options: Dictionary):
assert(_user_animation_player is AnimationPlayer, "No animation player was set.")
_user_animation_player.playback_speed = options["speed"]
_user_animation_player.play(options["animation_name_enter"])
yield(_user_animation_player, "animation_finished")


func _user_fade_in(options: Dictionary):
assert(_user_animation_player is AnimationPlayer, "No animation player was set.")
_user_animation_player.playback_speed = options["speed"]
_user_animation_player.play_backwards(options["animation_name_leave"])
yield(_user_animation_player, "animation_finished")


func _plugin_fade_out(options: Dictionary):
_animation_player.playback_speed = options["speed"]
_shader_blend_rect.material.set_shader_param("dissolve_texture", options["pattern_enter"])
_shader_blend_rect.material.set_shader_param("fade", not options["pattern_enter"])
_shader_blend_rect.material.set_shader_param("fade_color", options["color"])
_shader_blend_rect.material.set_shader_param("inverted", options["invert"])
var animation = _animation_player.get_animation("ShaderFade")
animation.track_set_key_transition(0, 0, options["ease_enter"])
_animation_player.play("ShaderFade")
yield(_animation_player, "animation_finished")


func _plugin_fade_in(options: Dictionary):
_animation_player.playback_speed = options["speed"]
_shader_blend_rect.material.set_shader_param("dissolve_texture", options["pattern_leave"])
_shader_blend_rect.material.set_shader_param("fade", not options["pattern_leave"])
_shader_blend_rect.material.set_shader_param(
"inverted", not options["invert"] if options["invert_on_leave"] else options["invert"]
)
var animation = _animation_player.get_animation("ShaderFade")
animation.track_set_key_transition(0, 0, options["ease_leave"])
_animation_player.play_backwards("ShaderFade")
yield(_animation_player, "animation_finished")


#region Public API
Expand Down Expand Up @@ -178,36 +225,47 @@ func fade_in_place(setted_options: Dictionary = {}) -> void:
func fade_out(setted_options: Dictionary = {}) -> void:
var options = _get_final_options(setted_options)
is_transitioning = true
_animation_player.playback_speed = options["speed"]

_shader_blend_rect.material.set_shader_param("dissolve_texture", options["pattern_enter"])
_shader_blend_rect.material.set_shader_param("fade", not options["pattern_enter"])
_shader_blend_rect.material.set_shader_param("fade_color", options["color"])
_shader_blend_rect.material.set_shader_param("inverted", options["invert"])
var animation = _animation_player.get_animation("ShaderFade")
animation.track_set_key_transition(0, 0, options["ease_enter"])
_animation_player.play("ShaderFade")

yield(_animation_player, "animation_finished")
if options["animation_name_enter"]:
yield(_user_fade_out(options), "completed")
else:
yield(_plugin_fade_out(options), "completed")
emit_signal("fade_complete")


func fade_in(setted_options: Dictionary = {}) -> void:
var options = _get_final_options(setted_options)
_shader_blend_rect.material.set_shader_param("dissolve_texture", options["pattern_leave"])
_shader_blend_rect.material.set_shader_param("fade", not options["pattern_leave"])
_shader_blend_rect.material.set_shader_param(
"inverted", not options["invert"] if options["invert_on_leave"] else options["invert"]
)
var animation = _animation_player.get_animation("ShaderFade")
animation.track_set_key_transition(0, 0, options["ease_leave"])
_animation_player.play_backwards("ShaderFade")

yield(_animation_player, "animation_finished")
if options["animation_name_leave"]:
if not options["animation_name_enter"]:
_animation_player.play("RESET")
yield(_user_fade_in(options), "completed")
else:
if options["animation_name_enter"]:
_user_animation_player.play("RESET")
yield(_plugin_fade_in(options), "completed")
is_transitioning = false
emit_signal("transition_finished")


func set_animation_player(animation_player) -> void:
assert(
animation_player is String or animation_player is PackedScene,
"set_animation_player() must receive a string (path to AnimationPlayer.tscn) or a PackedScene"
)
var loaded_animation_player = _load_resource(animation_player).instance()
assert(
loaded_animation_player is AnimationPlayer,
(
"The scene loaded from set_animation_player() (%s) must receive an AnimationPlayer"
% _user_animation_player
)
)
if _user_animation_player is AnimationPlayer:
_user_animation_player.queue_free()
_user_animation_player = loaded_animation_player
$CanvasLayer.add_child(_user_animation_player)
_user_animation_player.play("RESET")


func get_entity(entity_name: String) -> Node:
assert(
singleton_entities.has(entity_name),
Expand Down
19 changes: 17 additions & 2 deletions addons/scene_manager/SceneManager.tscn
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[gd_scene load_steps=7 format=2]

[ext_resource path="res://addons/scene_manager/ColorFade.tres" type="Animation" id=1]
[ext_resource path="res://addons/scene_manager/SceneManager.gd" type="Script" id=2]
[ext_resource path="res://addons/scene_manager/Dissolve2d.shader" type="Shader" id=3]
[ext_resource path="res://addons/scene_manager/shader_patterns/squares.png" type="Texture" id=4]
Expand All @@ -14,6 +13,21 @@ shader_param/fade = false
shader_param/inverted = false
shader_param/dissolve_texture = ExtResource( 4 )

[sub_resource type="Animation" id=2]
length = 0.001
tracks/0/type = "value"
tracks/0/path = NodePath("CanvasLayer/ColorRect:material:shader_param/dissolve_amount")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 0,
"values": [ 0.0 ]
}

[node name="SceneManager" type="Node2D"]
pause_mode = 2
script = ExtResource( 2 )
Expand All @@ -28,5 +42,6 @@ anchor_bottom = 1.0
mouse_filter = 2

[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
anims/ColorFade = ExtResource( 1 )
method_call_mode = 1
anims/RESET = SubResource( 2 )
anims/ShaderFade = ExtResource( 5 )
Binary file added custom_animation_setup.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom_animation_showcase.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions demo/animation_player.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[gd_scene load_steps=3 format=2]

[sub_resource type="Animation" id=1]
length = 0.001
tracks/0/type = "value"
tracks/0/path = NodePath("ColorRect:rect_rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0 ),
"transitions": PoolRealArray( 1 ),
"update": 0,
"values": [ -90.0 ]
}

[sub_resource type="Animation" id=2]
resource_name = "roll"
length = 0.5
tracks/0/type = "value"
tracks/0/path = NodePath("ColorRect:rect_rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/keys = {
"times": PoolRealArray( 0, 0.5 ),
"transitions": PoolRealArray( 1, 1 ),
"update": 0,
"values": [ -90.0, 0.0 ]
}

[node name="AnimationPlayer" type="AnimationPlayer"]
root_node = NodePath(".")
anims/RESET = SubResource( 1 )
anims/roll = SubResource( 2 )

[node name="ColorRect" type="ColorRect" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
rect_rotation = -90.0
color = Color( 0, 0, 0, 1 )
12 changes: 12 additions & 0 deletions demo/test.gd
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
extends Node
const animation_player = preload('res://demo/animation_player.tscn')


func _ready():
SceneManager.set_animation_player(animation_player)
yield(SceneManager, "scene_loaded")
SceneManager.get_entity("Button").connect("button_down", self, "_on_Button_button_down")
SceneManager.get_entity("CustomButton").connect(
"button_down", self, "_on_CustomButton_button_down"
)


func _on_Button_button_down():
if not SceneManager.is_transitioning:
SceneManager.change_scene(
'res://demo/test2.tscn', {"pattern_enter": "fade", "pattern_leave": "squares"}
)


func _on_CustomButton_button_down():
if not SceneManager.is_transitioning:
SceneManager.change_scene(
'res://demo/test2.tscn', {"animation_name_enter": "roll", "pattern_leave": "squares"}
)
Loading

0 comments on commit 163c0f3

Please sign in to comment.