You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

场景切换时角色传送至出生点触发意外碰撞的问题排查

房间切换时误触发碰撞的问题解决

场景设置

  • 创建了RoomExit场景(蓝色“门路径”)和SpawnPosition场景(门旁十字准星标识)
  • Level1右侧有1个RoomExit;Level2左右各1个RoomExit;Level3左侧有1个RoomExit(注意:其坐标与Level2左侧RoomExit完全相同)

问题现象

从Level3左侧RoomExit退出后,玩家会按预期出现在Level2右侧,但随即触发Level2左侧RoomExit的碰撞,导致直接跳转至Level1。

现有逻辑

RoomExit工作机制

碰撞发生时,通过SignalBus单例发送OnGotoRoom信号,附带两个参数:

  • 目标场景路径(如res://Rooms/TestLevel2.tscn)
  • 目标场景内出生点节点名称(如Spawn002)

Game节点处理逻辑

监听OnGotoRoom信号后执行以下步骤:

  1. 实例化新房间场景
  2. 根据名称获取出生点的GlobalPosition
  3. 将玩家的GlobalPosition设置为该出生点坐标
  4. 将新房间添加至CurrentLevel节点
  5. 销毁前一个场景

对应的C#代码:

public void gotoRoom(string roomPath, string spawnPoint){
    // 实例化新房间
    var newRoomScene = (PackedScene)ResourceLoader.Load(roomPath);
    Room newRoom = (Room)newRoomScene.Instance();

    // 设置玩家全局位置为出生点位置
    PlayableCharacter player = GetNode<Node2D>("Player").GetChild<PlayableCharacter>(0);
    player.GlobalPosition = newRoom.GetNode<Node2D>("PlayerSpawnPoints").GetNode<Node2D>(spawnPoint).GlobalPosition;
    if (this.playerStats.character == "Robot"){
        player.GlobalPosition = new Vector2(player.GlobalPosition.x, player.GlobalPosition.y-32);
    }
    // 将新房间添加到CurrentLevel节点
    GetNode<Node2D>("CurrentLevel").AddChild(newRoom);

    // 如果已有旧房间则销毁
    if (GetNode<Node2D>("CurrentLevel").GetChildCount() != 1){
        var current_room = GetNode<Node2D>("CurrentLevel").GetChild<Room>(0);
        current_room.QueueFree();
    }

    // 将新房间移至第一个子节点位置
    GetNode<Node2D>("CurrentLevel").MoveChild(newRoom, 0);
}

推测原因

切换Level3到Level2时,玩家位置更新后,仍会触发Level2左侧RoomExit的碰撞(该出口与Level3出口坐标重合),尝试在发送信号前将玩家位置设为new Vector2(-10000, -10000)但无效。

可行解决方案

方案1:临时禁用玩家碰撞体

在房间切换过程中先禁用玩家碰撞体,完成位置设置后再延迟启用,避免位置更新前触发新房间的碰撞:

public void gotoRoom(string roomPath, string spawnPoint){
    var player = GetNode<Node2D>("Player").GetChild<PlayableCharacter>(0);
    // 禁用玩家碰撞体(根据实际组件类型调整,比如CollisionShape2D/CollisionPolygon2D)
    var playerCollider = player.GetNode<CollisionShape2D>();
    playerCollider.SetDeferred("disabled", true);

    // 原有房间实例化逻辑
    var newRoomScene = (PackedScene)ResourceLoader.Load(roomPath);
    Room newRoom = (Room)newRoomScene.Instance();

    player.GlobalPosition = newRoom.GetNode<Node2D>("PlayerSpawnPoints").GetNode<Node2D>(spawnPoint).GlobalPosition;
    if (this.playerStats.character == "Robot"){
        player.GlobalPosition = new Vector2(player.GlobalPosition.x, player.GlobalPosition.y-32);
    }

    GetNode<Node2D>("CurrentLevel").AddChild(newRoom);

    if (GetNode<Node2D>("CurrentLevel").GetChildCount() != 1){
        var current_room = GetNode<Node2D>("CurrentLevel").GetChild<Room>(0);
        current_room.QueueFree();
    }

    GetNode<Node2D>("CurrentLevel").MoveChild(newRoom, 0);

    // 下一帧再启用碰撞体,确保位置更新完成
    CallDeferred(nameof(EnablePlayerCollider), playerCollider);
}

private void EnablePlayerCollider(CollisionShape2D collider){
    collider.disabled = false;
}

方案2:给RoomExit添加触发冷却

在RoomExit脚本中添加标志位,避免短时间内重复触发碰撞:

// RoomExit脚本
private bool isTriggering = false;

private void OnBodyEntered(Node2D body){
    if (isTriggering || body.Name != "Player") return;
    isTriggering = true;
    // 发送切换信号
    SignalBus.Instance.EmitSignal("OnGotoRoom", targetRoomPath, spawnPointName);
    // 0.2秒冷却,避免误触发(可根据实际调整时长)
    GetTree().CreateTimer(0.2f).Connect("timeout", this, nameof(ResetTrigger));
}

private void ResetTrigger(){
    isTriggering = false;
}

方案3:调整房间切换顺序

先销毁旧房间,等待一帧确保销毁完成后,再添加新房间并设置玩家位置,避免新旧房间碰撞体同时存在:

public async void gotoRoom(string roomPath, string spawnPoint){
    var currentLevel = GetNode<Node2D>("CurrentLevel");
    // 先销毁旧房间
    if (currentLevel.GetChildCount() > 0){
        var current_room = currentLevel.GetChild<Room>(0);
        current_room.QueueFree();
        // 等待一帧确保旧房间销毁完成
        await ToSignal(GetTree(), "idle_frame");
    }

    // 实例化新房间并设置玩家位置
    var newRoomScene = (PackedScene)ResourceLoader.Load(roomPath);
    Room newRoom = (Room)newRoomScene.Instance();
    currentLevel.AddChild(newRoom);

    var player = GetNode<Node2D>("Player").GetChild<PlayableCharacter>(0);
    player.GlobalPosition = newRoom.GetNode<Node2D>("PlayerSpawnPoints").GetNode<Node2D>(spawnPoint).GlobalPosition;
    if (this.playerStats.character == "Robot"){
        player.GlobalPosition = new Vector2(player.GlobalPosition.x, player.GlobalPosition.y-32);
    }
}

内容的提问来源于stack exchange,提问作者DanielRead

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 12:18:55