Trying to do super mario world style map movement, but pathfollow2d wont work?

Godot Version

4.3 stable

Question

i’m trying to make a super mario world overworld map for my game with up, down, right and left exit possibilities for the tiles it interacts with.
What keeps happening is i have the input of if pressed ‘Right’ to follow the set pathfollow2d but it only moves 4 pixels at a time not moving with delta. something is effecting this and I don;t know if once the player reaches the next tile will the new pathfollow2d be set?

here’s the code:

extends Node2D

class_name OverworldTile

signal send_level_name(level_name)
signal send_area_name(area_name)
signal send_level_path(level_scene_path)

@onready var overworld_player: OverworldPlayer = %OverworldPlayer
@export var move_speed := 4.0

@export var interation_enabled : bool = false
@export var level_scene_path : String
@export var level_name : String 
@export var area_name : String
@export var revealed : bool = false

@export_group("ConnectedPaths")
@export var path_up : PathFollow2D
@export var path_down : PathFollow2D
@export var path_left : PathFollow2D
@export var path_right : PathFollow2D
@export_group("")

@export_group("Enabled Directions")
@export var can_move_up = false
@export var can_move_down = false
@export var can_move_left = false
@export var can_move_right = false
@export_group("")

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	pass

func _process(delta):
	if Input.is_action_just_pressed("right") && can_move_right == true:
		path_right_moving(delta)

func path_right_moving(delta):
	move_speed = 4
	path_right.progress += move_speed * delta
	if path_right.progress_ratio == 1:
		move_speed = 0

func _on_overworld_player_on_level():
	send_level_name.emit(OverworldMan.level_name == level_name)
	send_area_name.emit(OverworldMan.area_name == area_name)
	send_level_path.emit(OverworldMan.level_scene_path == level_scene_path)

is_action_just_pressed only triggers for the first frame the input is pressed. Maybe you want is_action_pressed

same thing happens, I tried that, both pressed and just_pressed do the same thing, moves it 4 pixels then stops

is there any reason can_move_right would be set to false? can you show more of your code, that just_pressed condition is the only error I see.

this is all the code for this, the can_move_right is just to check on the tile that you can then it lets the player be able to use the input to move

If this is all the code then I promise you is_action_pressed will work as long as can_move_right is checked in the inspector. Even the progress_ratio == 1 check will likely fail, and the move_speed = 4 above it makes the check worthless as the movement has already been applied.

Are you reparenting somthing to another tile? This code doesn’t change the player either.

this code is for the level tiles, SMW blinking spaces, once the mario sprite is on it, it tells the overworld game manager what the level is and where that level scene is, if you can move to next level you can move mario using the remote transform node and it moves you to the next then reset so it can do it all again until a level scene is loaded.

If you have multiple of these tiles and no other scripts then they will all trigger the same movement at the same time, if each has a remote transform to the same one mario character, then the last in process order will be mario’s position, not any tile in specific.

is_action_pressed moves it smoothly but only if i hold it down, it like the delta input doesn’t even count, weird

delta is only a floating point number, often 0.016, this is the elapsed frame time, you can use delta to ensure time-based movement instead of frame-based movement; this time-based movement is more consistent on different hardware i.e. a player on a high framerate 144hz monitor takes the same amount of time to move as a standard 60hz player, if it was frame-based the 144hz player would move over twice as fast.

If you want the tiles to progress their entire path progress, you should use a tween instead, and only once, something like this

func _input(event: InputEvet) -> void:
	if event.is_action_pressed("right") and can_move_right:
		path_right_moving()
		set_process_input(false) # disable further inputs

func path_right_moving():
	var move_tween := path_right.create_tween()
	const MOVE_TIME = 2.0 # seconds
	move_tween.tween_property(path_right, "progress_ratio", 1.0, MOVE_TIME).from(0.0)

i forgot yes the overworld player has some script that deals with it, sorry i got mixed up. (i started the map screen about 2 weeks ago and only just got back to it so kinda trying to remember what i did.)

the tile is called TouchedTile in this

here’s the Overworld_player.gd:
extends CharacterBody2D

class_name OverworldPlayer

signal on_level()
signal off_level()

@onready var animated_sprite_2d: PlayerAnimatedMapSprite = $AnimatedSprite2D
@onready var interactive_level_tile: OverworldTile = $"../LevelHolder/InteractiveLevelTile"
@onready var interactive_level_tile_2: OverworldTile = $"../LevelHolder/InteractiveLevelTile2"
@onready var touched_tile = preload("res://Scripts/World_Scripts/interactive_overworld_tile.gd")
@onready var world_ui: WorldUI = $"../Camera2D/WorldUI"
@onready var level_start_text: Label = $"../Camera2D/LevelStart/LevelStartText"
@onready var level_start_black: ColorRect = $"../Camera2D/LevelStart/LevelStartBlack"
@onready var level_start: CanvasLayer = $"../Camera2D/LevelStart"


var input_free: bool
var levelname:String
var local_area: String
var level_path: String
@export var on_map_tile : bool = false

# Level Paths -------------------------------------------------------------------------------------
var path1_1_to_1_2 = false
var path1_2_to_1_1 = false

func _ready() -> void:
	if OverworldMan.spawn_point != null && OverworldMan.spawn_point != Vector2.ZERO:
		global_position = OverworldMan.spawn_point
		
	input_free = true
	level_start.visible = false
	level_start_black.self_modulate.a = 0.0
	level_start_text.self_modulate.a = 0.0
	
	

func _process(delta):
	
	var path: Path2D = null
	if Input.is_action_just_pressed("Up") and touched_tile.can_move_up:
		animated_sprite_2d.play("Up")
		touched_tile.path_movement_up
		path = touched_tile.path_up
	elif Input.is_action_just_pressed("down") and touched_tile.can_move_down:
		animated_sprite_2d.play("Down")
		touched_tile.path_movement_down
		path = touched_tile.path_down
	elif Input.is_action_just_pressed("left") and touched_tile.can_move_left:
		animated_sprite_2d.play("Left")
		touched_tile.path_movement_left
		path = touched_tile.path_left
	elif Input.is_action_just_pressed("right") and touched_tile.can_move_right:
		animated_sprite_2d.play("Right")
		touched_tile.path_movement_right
		path = touched_tile.path_right
	if not path:
		return
	#var movement: Vector2 = (position - prev_position)
	#prev_position = position
	#
	#var moving_angle: float = movement.angle()
	#if Util.between(moving_angle, -TAU / 8, TAU / 8):
		#animated_sprite_2d.play("Left")
	#elif Util.between(moving_angle, TAU, / 8, 3 * TAU / 8)
		#animated_sprite_2d.play("Down")
	#elif moving_angle >= 3 * TAU / 8 or moving_angle <= -3 * TAU / 8:
		#animated_sprite_2d.play("Left")
		#animated_sprite_2d.flip_h = true
	#else:
		#animated_sprite_2d.play("Up")

# Set pan speed
var PanSpeedKey = 8
# Called when the node enters the scene tree for the first time.
func _input(event):	
	if Input.is_action_just_pressed("jump") && on_level.connect(_on_interactive_level_tiel_area_entered) && input_free == true:
		animated_sprite_2d.play("Enter")
		input_free = false
		level_start.visible = true
		$"../Camera2D/Coin".play()
		$"../WorldMapMusic".stop()
		await get_tree().create_timer(0.8).timeout
		level_start_black.self_modulate.a = 1.0
		var tween = get_tree().create_tween()
		tween.tween_property(level_start_black, "modulate:a", 0, 0)
		tween.chain().tween_property(level_start_black, "modulate:a", 1, 0.2).finished
		await get_tree().create_timer(0.5).timeout
		level_start_text.self_modulate.a = 1.0
		await get_tree().create_timer(1).timeout
		SceneManager.change_scene(level_path, {"pattern": "squares", "speed": 6})
	


# Handles entering a level.

#func _handle_entering_level():
	#
	## if Mario isn't touching a level tile, do nothing
	#if not touched_tile:
		#return
		#
	## if Mario is still moving, do nothing.
	#if moving:
		#return
		#
	## if no butto to enter a level is pressed, do nothing.
	#if not _enter_level_button_pressed():
		#return
		#
	## if the level tile doesn't have a level scene set, return.
	#if not touched_tile.level_scene_path:
	#return
	#
	## emit the "entered level" signal
	#entered_level.emit(touched_tile)

#func _on_interactive_overworld_tile_player_touched() -> void:
#
#func handle_level_tile_collision(collision: KinematicCollision2D):
	#if collision.get_collider() is OverworldTile:
		#if Input.is_action_just_pressed("jump"):
			#handle_overworldtile_collision()
			#
#func handle_overworldtile_collision():
	#var overworld_tile = OverworldTile
	#OverworldMan.level_name = levelname

func _on_interactive_level_tiel_area_entered(area: Area2D):
	print("entered tile")
	on_map_tile = true
	levelname = interactive_level_tile.level_name
	local_area = interactive_level_tile.area_name
	level_path = interactive_level_tile.level_scene_path
	OverworldMan.level_name = levelname
	OverworldMan.area_name = local_area
	OverworldMan.level_scene_path = level_path
	on_level.emit("on_level")

func _on_interactive_level_tile_2_area_entered(area: Area2D):
		print("entered tile")
		on_map_tile = true
		levelname = interactive_level_tile_2.level_name
		local_area = interactive_level_tile_2.area_name
		level_path = interactive_level_tile_2.level_scene_path
		OverworldMan.level_name = levelname
		OverworldMan.area_name = local_area
		OverworldMan.level_scene_path = level_path
		on_level.emit("on_level")

I am a newbie at this, this is my first fortay into programming and started in mid feb

Seems like you intend to call the path_movement_* functions from here, but failed to add the parenthesis to actually call the function; you may be getting several script warnings along the lines of “this statement has no effect”

Try adding parenthesis, for example

elif Input.is_action_just_pressed("right") and touched_tile.can_move_right:
	animated_sprite_2d.play("Right")
	touched_tile.path_movement_right() # with parenthesis
	path = touched_tile.path_right

I do that but it come back with an error:
cannot call non-static function “path_movement_right()” on this class “OverworldTile” directly. Make and instance instead

Ah, your touched_tile is a preload to the script, not an actual tile. Not sure how you’d want to fix that, suppose it should be an @export var touched_tile: Node, or deduced through some collision detection like _on_interactive_level_tiel_area_entered seems to be handling.

so while player is colliding with the area2d of the level tile, it can change a var in player, say it’s called Tile_path_right and when the _on_interactive_level_tiel_area_entered is working it makes the Tile_path_right work with the touched_tile function “Path_movement_right()” ?

also when i put that into the _on_interactive_leve_tiel_area_entered, the path_movement_right()
wants delta in the parenthesis but it’s not in that part what could i put for it to ignore it since I know null will just kill it?

wish there was a tutorial for something even close to this on multiple pathfollow2d on a map but they’re isn’t