Interaction with custom Animation

Godot Version

4.3

Question

Hi guys, I’m new here, actually that’s my first post here.
Anyway, I need some help!
I’m new to Godot as well but trying to make a game.
So I can’t find any useful information for my problem.
I made a basic FPS controller an interaction system so I can walk around, press buttons, and pick up items.
But now i want more!
My goal is to make a special interaction (to use an old wire phone)
And I want it to work like this:

  • When the player gets close enought to the phone he can press E to interact
  • Then the player centers to the phone, locks and the special animation plays (the animation of the phone call)
  • After the animation ends, the player gets controls back

For now, I’ve made a phone model and the “pick up → talk → pull down” animation
and made a new inherited scene like this:
Phone (Node3D)
├── MeshInstance3D (for the phone)
├── MeshInstance3D (for the handset)
└── AnimationPlayer (for the phone use animation) that has animation named “pick up the phone”

But anyway I can’t make it work As I want :frowning:
Someone may have seen something similar and can give me a link or advice.
I’ve tried do google it and watch youtube videos but didn’t find anything helpful.

If you wonder how to get your player in the right place automatically to start the animation you could use tweens and disable the players input to move him to the desired location and move the camera of the player to the desired location. If the player is on his desired position you can start the animation of your phone and your players hands.
I have never done this and don’t know if there is any better way to handle this but that would be my approach to make a smooth transition between pressing the interact button and playing the animation.

The interact script of the phone:

@export var desired_player_position : Node3D
@export var desired_camera_target : Node3D
@export var procedural_animation_duration : float ## time until player reaches the desired position to start the none procedural animation
@export var animation_duration : float ## time until player can move again / animation duration
@export var animation_player : AnimationPlayer

func start_animation_and_get_data() -> Dictionary:
	start_animation()
	return {
		"global_player_position" : desired_player_position.global_position, 
		"global_camera_target_position" : desired_camera_target.global_position,
		"procedural_animation_duration" : procedural_animation_duration,
		"animation_duration" : animation_duration,
	}

func start_animation():
	await get_tree().create_timer(procedural_animation_duration).timeout
	animation_player.play("my animation")

The interact script of the player:

@export var camera : Node2D
@export var interaction_ray : RayCast3D
var locked_input = false

func interact():
	if interaction_ray.is_colliding() and interaction_ray.get_collider().is_in_group("interact able with animation"):
		var animation_data : Dictionary = interaction_ray.get_collider().start_animation_and_get_data()
		
		var position_tween = get_tree().create_tween()
		var rotation_tweem = get_tree().create_tween()
		var camera_tween = get_tree().create_tween()
		
		position_tween.tween_property(self, "global_position", animation_data["global_player_position"], animation_data["procedural_animation_duration"])
		position_tween.tween_property(self, "global_rotation_degrees:y", rad_to_deg(atan((animation_data["global_camera_target_position"].y - animation_data["global_player_position"].y) / (animation_data["global_camera_target_position"].x - animation_data["global_player_position"].x))), animation_data["procedural_animation_duration"])
		position_tween.tween_property(self, "rotation_degrees:x", rad_to_deg(atan((animation_data["global_camera_target_position"].y - camera.global_position.y) / (animation_data["global_camera_target_position"].x - camera.global_position.x))), animation_data["procedural_animation_duration"])
		
		locked_input = true
		await get_tree().create_timer(animation_data["animation_duration"]).timeout
		locked_input = false

This might be a litlle confusing and I am sure there are things that could be optimized, but I am basicly pulling the player to the desired spot with the tween and pointing the camera on the camera target for your animation. the data for the desired player position and camera target is stored in the script of the node you interact with and then returned via the start_animation_and_get_data() function to the player.
I hope this approach helps.

You might find something useful here:

Thank you so much!
You gave me a right idea and now I’m closer to the desired behavior but not so close anyway :smile:
Here is my current code that moves the player to the target position and rotates him according a given value (and that’s enough for now)

extends InteractableObject

@export var player : CharacterBody3D
@export var player_target : Marker3D
@export var animation_duration : float = 2

func _interact():
	var pos_tween: Tween = get_tree().create_tween()
	
	# Move the player
	pos_tween.parallel().tween_property(
		player, "global_position", player_target.global_position, animation_duration
	).set_trans(Tween.TRANS_CUBIC)
	# Rotates the player towards given direction (degrees)
	pos_tween.parallel().tween_property(
		player, "global_rotation_degrees:y", 90, animation_duration
	).set_trans(Tween.TRANS_CUBIC)
	

But it only works with StaticBody3D and CSGMesh. And I still can’t interact with my “Phone” model :frowning. The player just ignores it, so I don’t see any prompt when I point on it. Something is wrong with the collisions, I think, but I can’t figure it out yet.


For now my question is how to make my custom blender model interactable and then I think I know how to play the animation from animationplayer.

If you want to point you camera towards your phone to interact you need to shoot a raycast from your camera. If your raycast collides with the phone’s collider you can interact/show an interaction text.

Camera3D
|- Raycast3D

Add a collider (static or area) to your phone to interact with / to collide with your raycast. In this example the physics body (area3d or StaticBody3D) is in the “interactable” group to determine if it is interactable (you can add your node to a group via the node tab then groups). You have to change some settings for your raycast if you want it to be able to collide with areas:

@export var raycast : Raycast3D
var can_interact : bool = false

func _physics_process(delta):
   can_interact = raycast.is_colliding() and raycast.get_collider().is_in_group("interactable")

Thank you again!
As it was earlier your post led me to the solution!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.