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

Unity DOTS子场景实体:PlayerPosition无法更新及Debug.Log失效问题

问题描述

开发包含大量动态雾对象的Unity项目,使用DOTS实现——雾对象为大量小Sprite,玩家靠近时缩小,该功能已正常运行,但需要让雾对象同步玩家位置。尝试直接传入Player的Transform失败,改为传入Vector2类型的PlayerPosition后,发现Authoring脚本中无法更新该Vector2,甚至Debug.Log无输出,怀疑是实体处于子场景或实体特性导致。

附原代码:

FogAuthoring.cs

using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

public class FogAuthoring : MonoBehaviour
{
    public float LerpSpeed;
    public float TargetScale;
    public float2 PlayerPosition;
    private Transform _player;

    private void Start()
    {
        _player = GameObject.FindWithTag("Player").transform;
        Debug.Log("A");
    }

    private void Update()
    {
        PlayerPosition = new float2(_player.position.x, _player.position.y);
        Debug.Log("B");
    }

    private class Baker : Baker<FogAuthoring>
    {
        public override void Bake(FogAuthoring authoring)
        {
            Entity entity = GetEntity(TransformUsageFlags.Dynamic);
            AddComponent(entity, new FogData
            {
                LerpSpeed = authoring.LerpSpeed,
                TargetScale = authoring.TargetScale,
                PlayerPosition = authoring.PlayerPosition
            });
        }
    }
}

public struct FogData : IComponentData
{
    public float LerpSpeed;
    public float TargetScale;
    public float2 PlayerPosition;
}

FogControllerDOTS.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using Unity.Jobs;

public partial struct FogControllerDOTS : ISystem
{
    public const float distThreshold = 5;

    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate<FogData>();
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        FogParticleJob fogParticleJob = new FogParticleJob
        {
            deltaTime = SystemAPI.Time.DeltaTime
        };

        fogParticleJob.ScheduleParallel();
    }

    [BurstCompile]
    public partial struct FogParticleJob : IJobEntity
    {
        public float deltaTime;

        public void Execute(ref LocalTransform localTransform, in FogData fogData)
        {
            float currentScale = localTransform.Scale;
            float targetScale;

            if (Vector2.Distance(new Vector2(localTransform.Position.x, localTransform.Position.y), fogData.PlayerPosition) <= distThreshold)
                targetScale = Mathf.Lerp(currentScale, 0, fogData.LerpSpeed * deltaTime);
            else
                targetScale = Mathf.Lerp(currentScale, fogData.TargetScale, fogData.LerpSpeed * deltaTime);

            localTransform = localTransform.WithScale(targetScale);
        }
    }
}

解决方案

问题根源

  1. Authoring脚本生命周期失效:实体从子场景加载后,原FogAuthoring所在GameObject会被DOTS处理为实体,MonoBehaviour的Start/Update不再执行——原GameObject可能被销毁或标记为非活跃,导致Debug.Log无输出。
  2. PlayerPosition无法同步:Baker仅在烘焙阶段将FogAuthoring.PlayerPosition赋值给FogData,之后MonoBehaviour更新的变量不会同步到ECS组件,两者是独立存储。
  3. 内存浪费:每个雾对象存储一份玩家位置完全冗余,应共享单一数据源。

修复步骤

1. 创建玩家位置组件与同步系统

为玩家实体添加位置组件,编写同步系统将玩家位置同步到ECS:

// PlayerPositionData.cs
using Unity.Entities;
using Unity.Mathematics;

[UniqueComponent]
public struct PlayerPositionData : IComponentData
{
    public float2 Position;
}

// PlayerPositionAuthoring.cs
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

public class PlayerPositionAuthoring : MonoBehaviour
{
    private class Baker : Baker<PlayerPositionAuthoring>
    {
        public override void Bake(PlayerPositionAuthoring authoring)
        {
            Entity entity = GetEntity(TransformUsageFlags.Dynamic);
            AddComponent(entity, new PlayerPositionData
            {
                Position = new float2(authoring.transform.position.x, authoring.transform.position.y)
            });
        }
    }
}

// PlayerPositionSyncSystem.cs
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;

[BurstCompile]
public partial struct PlayerPositionSyncSystem : ISystem
{
    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate<PlayerPositionData>();
        state.RequireForUpdate<LocalTransform>();
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        foreach ((RefRW<PlayerPositionData> posData, RefRO<LocalTransform> transform) in 
                 SystemAPI.Query<RefRW<PlayerPositionData>, RefRO<LocalTransform>>())
        {
            posData.ValueRW.Position = new float2(transform.ValueRO.Position.x, transform.ValueRO.Position.y);
        }
    }
}
  • 将PlayerPositionAuthoring挂载到玩家GameObject,烘焙后玩家实体将拥有PlayerPositionData组件。
  • PlayerPositionSyncSystem每帧同步玩家Transform位置到ECS组件。

2. 修改雾对象组件与系统

移除FogData中的PlayerPosition字段,在雾系统中共享玩家位置:

// 修改后的FogData
public struct FogData : IComponentData
{
    public float LerpSpeed;
    public float TargetScale;
}

// 修改后的FogAuthoring.cs
using Unity.Entities;
using UnityEngine;

public class FogAuthoring : MonoBehaviour
{
    public float LerpSpeed;
    public float TargetScale;

    private class Baker : Baker<FogAuthoring>
    {
        public override void Bake(FogAuthoring authoring)
        {
            Entity entity = GetEntity(TransformUsageFlags.Dynamic);
            AddComponent(entity, new FogData
            {
                LerpSpeed = authoring.LerpSpeed,
                TargetScale = authoring.TargetScale
            });
        }
    }
}

// 修改后的FogControllerDOTS.cs
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;

public partial struct FogControllerDOTS : ISystem
{
    public const float distThreshold = 5;

    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate<FogData>();
        state.RequireForUpdate<PlayerPositionData>();
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        PlayerPositionData playerPos = SystemAPI.GetSingleton<PlayerPositionData>();

        FogParticleJob fogParticleJob = new FogParticleJob
        {
            deltaTime = SystemAPI.Time.DeltaTime,
            playerPosition = playerPos.Position
        };

        fogParticleJob.ScheduleParallel();
    }

    [BurstCompile]
    public partial struct FogParticleJob : IJobEntity
    {
        public float deltaTime;
        public float2 playerPosition;

        public void Execute(ref LocalTransform localTransform, in FogData fogData)
        {
            float currentScale = localTransform.Scale;
            float targetScale;

            float2 fogPos = new float2(localTransform.Position.x, localTransform.Position.y);
            if (math.distance(fogPos, playerPosition) <= distThreshold)
                targetScale = math.lerp(currentScale, 0, fogData.LerpSpeed * deltaTime);
            else
                targetScale = math.lerp(currentScale, fogData.TargetScale, fogData.LerpSpeed * deltaTime);

            localTransform = localTransform.WithScale(targetScale);
        }
    }
}
  • 移除FogAuthoring中无效的生命周期方法和冗余变量。
  • 通过SystemAPI.GetSingleton获取全局玩家位置,传递给Job。
  • 用math.distance替代Vector2.Distance,适配Burst编译提升性能。

额外注意事项

  • 若玩家未用ECS实体管理,可修改PlayerPositionSyncSystem直接查找玩家Transform同步(不推荐,尽量统一用ECS):
    [BurstCompile(CompileSynchronously = true)]
    public partial struct PlayerPositionSyncSystem : ISystem
    {
        private Transform _playerTransform;
    
        public void OnCreate(ref SystemState state)
        {
            _playerTransform = GameObject.FindWithTag("Player").transform;
            state.RequireForUpdate<PlayerPositionData>();
        }
    
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var playerPos = SystemAPI.GetSingletonRW<PlayerPositionData>().ValueRW;
            playerPos.Position = new float2(_playerTransform.position.x, _playerTransform.position.y);
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 07:49:59