Don't know how to properly kill tween after tweening property

Godot Version

godot_v4.2-rc1_win64.exe

Problem

Can't kill tween after tweening property

I’m using two nodes and two different scripts:

Enemy.gd and bullseye.gd are their names.

Enemy script:

In the enemy script, bullseye is known as target_POINT

I create the tween globally at the top of the script var tween : Tween.

and connect some signals in func _ready().

func _ready():
	target_POINT = Target_point.instantiate()
	target_POINT.stop_looking_at_me.connect(tween_to_Target_point)
	target_POINT.reset.connect(start_third_timer)

and tween the selected property in the function below

func tween_to_Target_point():
	is_tweening = true
	has_reached_max_height = false
	# creates new tween
	tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_SINE)
	
	TT.start()
	#changes the nodes global position to the targets global position
	tween.tween_property(self,"global_position",target_POINT.global_position,0.18)
	#Shows point node is tweening to
	print("point_position is ", target_POINT.global_position)

The issue I’m having is that when I try to kill the tween after timer_two (Known as TT in the script) times out, its ignores that line and continues tweening whilst permorming all other output actions under the function.

func _on_timer_2_timeout():
	emit_signal("dont_follow")
	is_stunned = true
	print("can't follow")
	tween.kill()

I need some help in trying to figure out exactly how to kill the tween in the enemy script.

Full enemy script below

extends CharacterBody2D


const SPEED = 150
const JUMP_VELOCITY = -400.0
signal has_locked_onto_point
signal dont_follow
@onready var attack_collider_left = $"movment colliders/Left_attack_collider" 
@onready var attack_collider_right = $"movment colliders/Right_attack_collider"
@onready var collider_left = $"movment colliders/Left_collider"
@onready var collider_right = $"movment colliders/Right_collider"
@onready var timer = $"Timer"
@onready var TT = $"Timer2"
var float_timer = Timer.new()
var start_attack_floating : bool = false
@export var max_height: int
var has_reached_max_height: bool = false
var can_move_on_ground: bool = false
var is_tweening: bool = false
var is_stunned: bool = false
var  tween : Tween
@onready var Target_point = preload("res://Scenes/Bullseye.tscn")
var target_POINT = null
@onready var Player = get_node("../Beany")
@onready var trackers = get_node("../Trackers")
@onready var bulls = get_node("../../Bullseye")


# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity =100

func _ready():
	target_POINT = Target_point.instantiate()
	target_POINT.stop_looking_at_me.connect(tween_to_Target_point)
	target_POINT.reset.connect(start_third_timer)



func _physics_process(delta):

	#prints targets position
#region Target print
	if target_POINT == null: return
	var target_pos = to_local(target_POINT.global_position)
	print("Target_point Instantiated", target_pos)
#endregion
	
	
	# Add the gravity.
#region deals with floor logic
	if not is_on_floor() and has_reached_max_height == false:
		velocity.y += gravity * delta
	
	if is_on_floor():
		can_move_on_ground = true
#endregion
	
	#shows movement logic
#region Movement logic
	if  can_move_on_ground == true and collider_right.is_colliding():
		position.x +=  Player.position.x*delta *0.5
		is_stunned = false
		print("is moving to player")
	elif  collider_left.is_colliding():
		position.x -=  Player.position.x*delta *0.5
		#print("is moving to player")
#endregion
	
	if   collider_left.is_colliding():
		print("player is left")
	if  collider_right.is_colliding():
		print("player is right")
	
#region Attack logic
	if attack_collider_left.is_colliding() and start_attack_floating == false or attack_collider_right.is_colliding() and start_attack_floating == false :
		start_attack_floating = true
		can_move_on_ground = false
		attack_collider_left.enabled = false
		attack_collider_right.enabled = false
		print("is collidng with attack colliders on floor", attack_collider_left.enabled, attack_collider_right.enabled)
		

	if start_attack_floating == true:
		velocity.y += -15 
		can_move_on_ground = false
		print("is moving up and velocity.y == ", velocity.y)
		
	if velocity.y < max_height :
		start_attack_floating = false
		velocity.y = 0
		has_reached_max_height = true
		add_child(target_POINT)
		timer.start()
		collider_left.enabled = false
		collider_right.enabled = false
	
		print("reached max height")
#endregion
	
	if global_position == target_POINT.global_position:
		print("our pos is = ", global_position)
	
	#prints what state the node is in
	states()
	
	move_and_slide()



func bools_statements():
	if is_tweening == true:
		#turns off floor movement colliders
		collider_left.enabled = false
		collider_right.enabled = false
		print("is tweening")
	
	if is_stunned == true:
		velocity.x = 0
	

func prints_func():
	if attack_collider_left.is_colliding() or attack_collider_right.is_colliding() :
		print("is colliding with attack colliders, but isn't on the floor")
	else :
		print("is not collidng with attack colliders") 

func tween_to_Target_point():
	is_tweening = true
	has_reached_max_height = false
	# creates new tween
	tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_SINE)
	
	TT.start()
	#changes the nodes global position to the targets global position
	tween.tween_property(self,"global_position",target_POINT.global_position,0.18)
	#Shows point node is tweening to
	print("point_position is ", target_POINT.global_position)
	
	#setting timer varibles 
	




func states():
	if can_move_on_ground == true:
		print("In ground state")
		
	elif can_move_on_ground == false:
		print("Not in ground state")
		
	if start_attack_floating == true:
		print("In homing state")
		
	elif start_attack_floating == false:
		print("Not in homing state")
		
	if has_reached_max_height == true:
		print("In air waiting state")
		
	elif  has_reached_max_height == false:
		print("Not in air waiting state")
		
	if is_tweening == true:
		print("In attacking state")
		
	elif is_tweening == false:
		print("Not in attacking state")
		
	if is_stunned == true:
		print("In stunned state")
		
	elif is_stunned == false: 
		print("not in stunned state")
		
	else: 
		print("in no state")
	



func _on_timer_timeout():
	emit_signal("has_locked_onto_point")
	print("timer timed out")


func _on_timer_2_timeout():
	emit_signal("dont_follow")
	is_stunned = true
	print("can't follow")
	tween.kill()



func start_third_timer():
	$"Timer3".start()
	is_tweening = false
	print("reset timer has ended")
	
	



func _on_timer_3_timeout():
	print("timer three has timed out")
	can_move_on_ground = true
	is_stunned = false
	collider_left.enabled = true
	collider_right.enabled = true
	attack_collider_left.enabled = true
	attack_collider_right.enabled = true
	has_reached_max_height = false
	start_attack_floating = false
	tween.kill()

bullseye script below

extends Sprite2D
signal stop_looking_at_me
signal look_at_me
signal reset
signal leaving_scene
@onready var player = get_node("../../Beany")
@onready var enemy = get_node("../../Enemy")
@onready var follow_timer = $"Timer"
@onready var Leave_scene_T = $"Timer2"
var can_follow: bool = false
# Called when the node enters the scene tree for the first time.
func _ready():
	emit_signal("look_at_me")
	print("is_following_player", global_position)
	self.visible = true
	can_follow = true
	enemy.has_locked_onto_point.connect(stop_following)
	enemy.dont_follow.connect(cant_follow)



func _process(_delta):
	var dir = Input.get_axis("ui_left","ui_right")
	print("is not following player anymore")
	if can_follow == true:
		global_position += (player.position-global_position) + (Vector2(30,0)*dir)
	else :
		emit_signal("leaving_scene")
		#queue_free()
		global_position  +=  global_position * Vector2.ZERO 
		print("is not following entity")



func stop_following():
	print("no longer following ", global_position)
	emit_signal("stop_looking_at_me")

func cant_follow():
	can_follow = false
	#self.visible = false
	emit_signal("reset")
	#queue_free()
	print("reset")

Is there any chance you are creating two tweens and losing the reference to the first? for example if the _on_timer_2_timeout() runs twice.

Not from what I can see no, my Point_position is print only outputs one time while running the main scene, what would you advise for checking to see if one or more tween is spawned at once?

printing should be pretty accurate, maybe try to assert or if to make sure there isn’t an active tween?

assert(tween == null or (not tween.is_valid()), "Already running a tween")

Its only being created once, so that’s not the issue, it just seems to be ignoring all code that either stops or kills the tween other than when I kill it in the same function

I think its not playing because the property is only changed in the function and all other functions around it only have the global variable to go off of, question is how do I kill it inside of the tween_to_Target_point(): function without canceling it entirely.

And if not that how to get the other functions to know that tween has changed.

If the tween is not playing then I think it’s because you keep editing the global_position every frame in the _process function. The else clause is especially pointless, adding zero every frame.

I’ll try editing the code to where the node spawns just before the tween starts instead of it tracking the player for a few seconds to see if that solves my issue.

I appreciate the help

Sorry its taken me so long to write back, I just got a new error massage stating that I cant add the bullseye node after its deleted because its no longer a subclass of the enemy node.

here’s the code:

Enemy tween script:

func tween_to_Target_point():
	if tween:
		tween.kill()
		print("has killed tween")
	add_child(target_POINT)
	has_reached_max_height = false
	# creates new tween
	tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_SINE)
	
	TT.start()
	#changes the nodes global position to the targets global position
	tween.tween_property(self,"global_position",target_POINT.global_position,0.18)
	#tween.kill()
	#tween = null
	#Shows point node is tweening to
	print("point_position is ", target_POINT.global_position)
	if  tween.is_valid():
		print(" tween is connected right")
	#setting timer varibles 
	print("has tween ", times_tweened," time/s")









func states():
	if can_move_on_ground == true:
		print("In ground state")
		
	elif can_move_on_ground == false:
		print("Not in ground state")
		
	if start_attack_floating == true:
		print("In homing state")
		
	elif start_attack_floating == false:
		print("Not in homing state")
		
	if has_reached_max_height == true:
		print("In air waiting state")
		
	elif  has_reached_max_height == false:
		print("Not in air waiting state")
		
	if is_tweening == true:
		print("In attacking state")
		
	elif is_tweening == false:
		print("Not in attacking state")
		
	if is_stunned == true:
		print("In stunned state")
		
	elif is_stunned == false: 
		print("not in stunned state")
		
	else: 
		print("in no state")




func _on_timer_timeout():
	emit_signal("has_locked_onto_point")
	print("timer timed out")
	is_tweening = true
	tween_to_Target_point()



func _on_timer_2_timeout():
	emit_signal("dont_follow")
	is_stunned = true
	is_tweening = false
	print("can't follow")
	times_tweened += 1



func start_third_timer():
	$"Timer3".start()
	is_tweening = false
	print("reset timer has ended")




func _on_timer_3_timeout():
	print("timer three has timed out")
	can_move_on_ground = true
	is_stunned = false
	collider_left.enabled = true
	collider_right.enabled = true
	attack_collider_left.enabled = true
	attack_collider_right.enabled = true
	has_reached_max_height = false
	start_attack_floating = false

Bullseye script:

func _ready():
	emit_signal("look_at_me")
	print("is_following_player", global_position)
	self.visible = true
	can_follow = true
	enemy.has_locked_onto_point.connect(stop_following)
	enemy.dont_follow.connect(cant_follow)
	enemy.Kill_bull.connect(Kill)
	player.moved_right.connect(change_to_right_offset)
	player.moved_left.connect(change_to_left_offset)
	global_position = (player.position + offset_from_player)




func _process(_delta):
	print("is not following player anymore")
	if can_follow == false :
		emit_signal("leaving_scene")
		print("is not following entity")



func stop_following():
	print("no longer following ", global_position)
	emit_signal("stop_looking_at_me")

func cant_follow():
	can_follow = false
	#self.visible = false
	emit_signal("reset")
	#queue_free()
	print("reset")
	
func Kill():
	queue_free()
	print("killed bull :)")

For more context killing the tween is not an issue anymore.

The line that gives me the error is add_child(target_POINT)

What’s the exact error? How is target_POINT defined? What does it extend?

target_point is defined as a PackedScene.

The bug states that the function add_child is invalid because the object I’m trying to instance of argument 1 previously freed and is no longer a subclass of the expected argument class.

I just fixed it.

I just had to re-instantiate the Target_point (or the pack scene I was talking about) after killing the Bullseye node.

		target_POINT = Target_point.instantiate()

And now its working just fine.

Thank you for your help @gertkeno, I’m extremely thankful!

1 Like