Godot 4.3:实现TileMap玩家按住方向键持续移动功能
需求与现有代码
我之前用Flash做游戏,现在因为Flash过时转用Godot。已经实现了玩家控制角色在TileMap上移动,但想要实现按住方向键时,角色沿对应方向持续移动,直到进入与不可行走瓷砖相邻的可行走瓷砖为止。以下是我目前的代码:
extends Sprite2D class_name PacMan @export var move_speed: float = 1 @onready var tile_map: TileMap = $"../TileMap" @onready var sprite_2d: Sprite2D = $Sprite2D @onready var animation_player: AnimationPlayer = $Sprite2D/AnimationPlayer var is_moving = false func _physics_process(delta: float) -> void: if is_moving == false: return if global_position == sprite_2d.global_position: is_moving = false return sprite_2d.global_position = sprite_2d.global_position.move_toward(global_position, move_speed) func _ready() -> void: animation_player.play("chomping") func _process(delta: float) -> void: if is_moving: return if Input.is_action_pressed("up"): rotation_degrees = 90 move(Vector2.UP) is_moving = true elif Input.is_action_pressed("down"): rotation_degrees = 270 move(Vector2.DOWN) is_moving = true elif Input.is_action_pressed("left"): rotation_degrees = 0 move(Vector2.LEFT) is_moving = true elif Input.is_action_pressed("right"): rotation_degrees = 180 move(Vector2.RIGHT) is_moving = true func move(direction: Vector2): var current_tile: Vector2i = tile_map.local_to_map(global_position) var target_tile: Vector2i = Vector2i( current_tile.x + direction.x, current_tile.y + direction.y, ) var tile_data: TileData = tile_map.get_cell_tile_data(0, target_tile) if tile_data.get_custom_data("walk_tiles") == false: return #Player movement is_moving = true global_position = tile_map.map_to_local(target_tile) sprite_2d.global_position = tile_map.map_to_local(current_tile)
实现思路与代码修改
核心逻辑是:角色到达一个瓷砖后,自动检测当前按住方向的下一个瓷砖是否可行走,可行走就继续移动,不可行走则停止。具体修改如下:
1. 添加方向记录变量
新增变量保存当前按住的方向,避免每次移动都重新检测输入:
var is_moving = false var current_move_direction: Vector2 = Vector2.ZERO # 新增
2. 持续更新按住的方向
修改_process函数,不管角色是否在移动,都实时更新当前按住的方向:
func _process(delta: float) -> void: current_move_direction = Vector2.ZERO if Input.is_action_pressed("up"): current_move_direction = Vector2.UP rotation_degrees = 90 elif Input.is_action_pressed("down"): current_move_direction = Vector2.DOWN rotation_degrees = 270 elif Input.is_action_pressed("left"): current_move_direction = Vector2.LEFT rotation_degrees = 0 elif Input.is_action_pressed("right"): current_move_direction = Vector2.RIGHT rotation_degrees = 180
3. 到达瓷砖后自动续接移动
修改_physics_process函数,处理移动逻辑的同时,在角色到达目标瓷砖后自动检查下一个可行走瓷砖:
func _physics_process(delta: float) -> void: if is_moving: # 用距离判断避免浮点精度问题 if sprite_2d.global_position.distance_to(global_position) < 0.1: sprite_2d.global_position = global_position is_moving = false # 到达后检查是否可以继续移动 if current_move_direction != Vector2.ZERO: try_continue_move(current_move_direction) else: # 乘delta保证不同帧率下速度一致 sprite_2d.global_position = sprite_2d.global_position.move_toward(global_position, move_speed * delta) else: # 未移动时,有方向键按下就触发移动 if current_move_direction != Vector2.ZERO: try_continue_move(current_move_direction)
4. 封装移动检查逻辑
新增函数统一处理瓷砖可行性检查和移动触发:
func try_continue_move(direction: Vector2): var current_tile: Vector2i = tile_map.local_to_map(sprite_2d.global_position) var target_tile: Vector2i = current_tile + direction var tile_data: TileData = tile_map.get_cell_tile_data(0, target_tile) # 检查目标瓷砖是否存在且可行走 if tile_data != null and tile_data.get_custom_data("walk_tiles") == true: is_moving = true global_position = tile_map.map_to_local(target_tile)
5. 移除原move函数
原move函数的逻辑已被try_continue_move替代,可直接删除。
修改后的完整代码
extends Sprite2D class_name PacMan @export var move_speed: float = 200 # 建议调大,配合delta使用更合理 @onready var tile_map: TileMap = $"../TileMap" @onready var sprite_2d: Sprite2D = $Sprite2D @onready var animation_player: AnimationPlayer = $Sprite2D/AnimationPlayer var is_moving = false var current_move_direction: Vector2 = Vector2.ZERO func _physics_process(delta: float) -> void: if is_moving: if sprite_2d.global_position.distance_to(global_position) < 0.1: sprite_2d.global_position = global_position is_moving = false if current_move_direction != Vector2.ZERO: try_continue_move(current_move_direction) else: sprite_2d.global_position = sprite_2d.global_position.move_toward(global_position, move_speed * delta) else: if current_move_direction != Vector2.ZERO: try_continue_move(current_move_direction) func _ready() -> void: animation_player.play("chomping") func _process(delta: float) -> void: current_move_direction = Vector2.ZERO if Input.is_action_pressed("up"): current_move_direction = Vector2.UP rotation_degrees = 90 elif Input.is_action_pressed("down"): current_move_direction = Vector2.DOWN rotation_degrees = 270 elif Input.is_action_pressed("left"): current_move_direction = Vector2.LEFT rotation_degrees = 0 elif Input.is_action_pressed("right"): current_move_direction = Vector2.RIGHT rotation_degrees = 180 func try_continue_move(direction: Vector2): var current_tile: Vector2i = tile_map.local_to_map(sprite_2d.global_position) var target_tile: Vector2i = current_tile + direction var tile_data: TileData = tile_map.get_cell_tile_data(0, target_tile) if tile_data != null and tile_data.get_custom_data("walk_tiles") == true: is_moving = true global_position = tile_map.map_to_local(target_tile)
关键说明
- 用
distance_to替代直接相等判断,避免浮点精度导致的移动停止异常。 - 移动速度乘以
delta,保证在不同帧率下移动速度一致。 - 持续记录方向键状态,角色到达瓷砖后自动触发下一次移动检查,实现按住方向键持续移动的效果。
- 松开方向键时,
current_move_direction重置为ZERO,角色到达当前瓷砖后自动停止。
内容的提问来源于stack exchange,提问作者SPONGEBOB RED YT channel
相关产品推荐
相关产品推荐

