Randomized teleporting mechanic isn't working

Godot Version

4.6.2

Question

It’s just sraight up not working at all. I’m using this tutorial: https://youtu.be/JtgteirSn2g?si=qUVTu8BOg3T7FuKS

If you share code, please wrap it inside three backticks or replace the code in the next block:

extends CharacterBody2D
class_name Boss

@export var max_hp : int = 3

var hp : int = 3
var current_position : int = 0
var positions : Array[Vector2]
var direction : Vector2
var beam_at: Array[beamattack]

@onready var animated_sprite = $AnimatedSprite2D
@onready var progress_bar = $UI/ProgressBar

func _ready():
	set_physics_process(false)
	hp = max_hp
	for c in $PositionTargets.get_children():
		positions.append( c.global_position)
		print( positions )
	$PositionTargets.visible = false
	await get_tree().create_timer(2).timeout
	for b in $beamattacks.get_children():
		beam_at.append( b )
	teleport(0)

func energyattack():
	var _b: Array [ int ]
	match current_position:
		0,2:
			if current_position == 0:
				_b.append(0)
				_b.append( randi_range(1,2))
			else:
				_b.append(2)
				_b.append(randi_range(0,1))
		1, 3:
			if current_position == 3:
				_b.append(5)
				_b.append( randi_range(3, 4))
			else:
				_b.append(3)
				_b.append(randi_range(4,5))
	for b in _b:
		beam_at[b].attack()

func idle():
	await get_tree().create_timer(3).timeout
	
	var _t : int = current_position
	while _t == current_position:
		_t = randi_range(0, 3)
		teleport(_t)
		pass

func teleport( location : int) -> void:
	await get_tree().create_timer( 1 ).timeout
	$".".global_position = positions[ location ]
	current_position = location
	pass

func physics_process(_delta):
	direction = %Player.position - position
	if direction.x < 0:
		animated_sprite.flip_h = true
	else:
		animated_sprite.flip_h = false
	
	
func _physics_process(delta):
	velocity = direction.normalized() * 40
	move_and_collide(velocity * delta)

func take_damage():
	hp -= 1
	print("boddamefg")
	animated_sprite.play("damaged")
	if hp < 1:
		defeat()

func defeat():
	animated_sprite.play("damaged")
	set_collision_layer_value(3,false)
	set_collision_mask_value(1, false)

You need to provide more details. What happens vs what you expect to happen? What did you already try to do? Try adding more print() statements to understand what the code does and doesn’t do.

Do you really think we will be watching 2.5h tutorial to find the rootcause and fix the issue? :slight_smile: You could have at least pointed to any specific part of the video that you are stuck on or don’t understand.

I recommend checking this video:

2 Likes

Yeah sorry I was working on an energy drink and no sleep so I wasn’t really able to properly explain myself. I’m usually much better articulated and always get helpful answers to my questions, so the video was unnecessary but thanks anyways.

The video tutorial was split into a bunch of clearly labeled sections but I probably should have said that and pointed out which ones.
What I expect to happen is for the the boss to teleport to 1 of 4 places marked on the map randomly in intervals. What it does instead is either nothing (the boss is completely stationary) or the boss will really quickly teleport to every point all at once so it looks like it’s all over the screen and glitching super fast. I’m using the teleport mechanism part of the tutorial for this. (It’s in the teleporting mechanism section near the end)
I’ve followed the tutorial very closely, but I am having some problems understanding the arrays, so the issue might lie there, and I just didn’t see it. I’ll try the printing part when I can and update in the replies if I find anything. Hope this clarifies things.

To be clear, there are many of us who are never going to watch a video. It’s asking way too much of us. Especially since we don’t know what you did in following the video. If you are really having a problem with the video, you should be checking the comments in the video to see if anyone else had the problem, and if not, posting in the video comments first. The video that @wchc linked to would have told you this.

Having said that, I will try to help you without watching the video.


I looked at your code, and you are being taught a TON of bad habits. So I am going to rewrite your code and explain in the comments why I changed things. But the real reason I’m rewriting it is so I can figure out what it’s doing and try to help you.

This code is your problem. You’re getting stuck in a while loop. Because _t is set equal to current_position. Then is calls teleport() with a new value for _t and assigns it to current_position. Then it returns to the while loop and now _t is still equal to current_position because you just changed them both.

func idle():
	await get_tree().create_timer(3).timeout
	
	var _t : int = current_position
	while _t == current_position:
		_t = randi_range(0, 3)
		teleport(_t)
		pass

func teleport( location : int) -> void:
	await get_tree().create_timer( 1 ).timeout
	$".".global_position = positions[ location ]
	current_position = location
	pass

I fixed your code and the bug:

#CharacterBody2D is a bad choice for this object, but I'm going to leave it alone.
class_name Boss extends CharacterBody2D #These should be put on one line like this for readability

const STARTING_LOCATION = 0 #Eliminating magic number

@export var max_hp : int = 3

var hp : int = 3
var current_position : int = 0
var positions : Array[Vector2]
var direction : Vector2
var beam_at: Array[beamattack]

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D #Added a type for better code completion when typing
@onready var progress_bar: ProgressBar = $UI/ProgressBar #Added a type for better code completion when typing
@onready var position_targets = $PositionTargets #Assigned this node path to a variable
@onready var beam_attacks = $beamattacks #Assigned this node path to a variable
@onready var player = %Player #Assigned this node path to a variable


func _ready():
	set_physics_process(false)
	hp = max_hp
	for target in position_targets.get_children(): #Renamed "c" to "target" and replaced node path with @onready variable
		positions.append(target.global_position)
		print(positions)
	position_targets.visible = false #Replaced node path with @onready variable
	await get_tree().create_timer(2).timeout
	for beam_attack in beam_attacks.get_children(): #renamed "b" to beam_attack and replaced node path with @onready variable
		beam_at.append(beam_attack)
	teleport(STARTING_LOCATION) #Eliminating magic number


# I left this alone because it's so messed up I don't know where to start and
# I can't even guess what "_b" or "b" stand for, or why an array is used here
# since only one option is possible.
func energyattack():
	var _b: Array [ int ]
	match current_position:
		0,2:
			if current_position == 0:
				_b.append(0)
				_b.append(randi_range(1,2))
			else:
				_b.append(2)
				_b.append(randi_range(0,1))
		1, 3:
			if current_position == 3:
				_b.append(5)
				_b.append(randi_range(3, 4))
			else:
				_b.append(3)
				_b.append(randi_range(4,5))
	for b in _b:
		beam_at[b].attack()


func idle():
	await get_tree().create_timer(3).timeout
	
	var location : int = current_position
	while location == current_position:
		var new_location = randi_range(0, 3) #Changed "location" to "new_location" to fix bug and break the loop
		teleport(new_location)
		#Removed pass as it does nothing


func teleport(location: int) -> void:
	await get_tree().create_timer( 1 ).timeout
	global_position = positions[location] #Removed $"." reference as it is unnecessary
	current_position = location
	#Removed pass as it does nothing


func physics_process(_delta):
	direction = player.position - position  #Replaced node path with @onready variable
	if direction.x <= 0: #Changed from less than to less than or equal to, prevents bug where direction equals zero.
		animated_sprite.flip_h = true
	else:
		animated_sprite.flip_h = false
	
	
func _physics_process(delta):
	velocity = direction.normalized() * 40
	move_and_collide(velocity * delta)


func take_damage():
	hp -= 1
	print("boddamefg")
	animated_sprite.play("damaged")
	if hp < 1:
		defeat()


func defeat():
	animated_sprite.play("damaged")
	set_collision_layer_value(3, false)
	set_collision_mask_value(1, false)
2 Likes

The change of current_position should be delayed by 1 second because of the await in teleport(). In the initial code, I would expect the while-loop to run until the random number assigned to _t doesn’t match the current position (potentially calling teleport() multiple times and creating multiple co-routines). Your code will make the while-loop run for 1 second, until the first co-routines eventually change current_position.


If the idea behind this while-loop was to make sure the new random value differs from the current position, I’d recommend removing the loop completely:

	var available_locations : Array = range(positions.size())
	available_locations.erase(current_position)
	teleport(available_locations.pick_random())
1 Like

I agree with @hyvernox

This is working but it says that size is a non existent function in base node 2d. I’m guessing this means that we need to change the type. The position targets are all sprite 2ds so maybe there’s a different node I can use to be able to use six and also not throw all the references to it off whack.

var location: Array = range(position_targets.size())

Use the positions array, not the position_targets node reference.