【Code Monkey 代码猴子】使用 DOTS 制作完整RTS游戏系列教程の学习笔记

14-0:学到一个理念,没有UI游戏逻辑也要能正常运行。

14-1:

EntityQueryBuilder.WithAll<T> 得到的是enable的T,而WithPresent<T>则只要有T就会返回

14-2:适配画布缩放

float canvasScale = canvas.transform.localScale.x;
selectionAreaRectTransform.anchoredPosition = new Vector2(selectionAreaRect.x, selectionAreaRect.y) / canvasScale;
selectionAreaRectTransform.sizeDelta = new Vector2(selectionAreaRect.width, selectionAreaRect.height) / canvasScale;

14-3:鼠标左键按下时立即更新单位选择框UI

private void UnitSelectionManager_OnSelectionAreaStart(object sender, EventArgs e)
{
  selectionAreaRectTransform.gameObject.SetActive(true);
  UpdateVisual();
}

15-1:以下两种写法作用一样

entityQuery = new EntityQueryBuilder(Allocator.Temp).WithAll<PhysicsWorldSingleton>().Build(entityManager);
entityQuery = entityManager.CreateEntityQuery(typeof(PhysicsWorldSingleton));

15-2:要搞清楚三个参数都是什么意思

Filter = new CollisionFilter {
GroupIndex = 0,
BelongsTo = ~0u,
CollidesWith = 1 << 6
}

 16-1:关键代码,生成圆环阵型中的下一个位置

float3 ringVector = math.rotate(quaternion.RotateY(angle), new float3(ringSize * (ring + 1), 0f, 0f));

.

17-1:System顺序半随机,在每个unity编辑器种可能不同,但Build后确定

17-2:这一节有bug,连续框选同一个物体,会取消选中

18-1:prefab variant,覆盖本体的字段,一个小技巧是修改本体的字段值,然后改变体的字段值,再把本体的字段值改回来,会发现变体的字段值变粗体了

19-1:计时器

            shootAttack.ValueRW.timer -= SystemAPI.Time.DeltaTime;
            if (shootAttack.ValueRO.timer > 0)
            {
                continue;
            }
            shootAttack.ValueRW.timer = shootAttack.ValueRO.timerMax;

20-1:WithEntityAccess可以通过组件获得实体,注意迭代器声明的entity放在最后一个参数

        foreach ((
            RefRO<Health> health,
            Entity entity
            )
            in SystemAPI.Query<
                RefRO<Health>>().WithEntityAccess())
        {

        }

20-2:禁用burst更好调试

20-3:EntityCommandBuffer解决循环后销毁entity

        //EntityCommandBuffer entityCommandBuffer = new EntityCommandBuffer(Allocator.Temp); ---> entityCommandBuffer.Playback(state.EntityManager);
        EntityCommandBuffer entityCommandBuffer = 
            SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged);//会在帧的末尾自动调用playback

20-3:给预制在场景中的gameobject添加LinkedEntityGroupAuthoring,即可解决无法销毁子物体的问题,而代码生成的实体已有改组件,所以不需要手动添加

21-1:给子弹加上刚体才能移动,不然localPosition虽然值在改变,但是实际物体没有要移动

22-1:entityCommandBuffer.DestroyEntity(entity)在下一帧的ParentSystem中销毁实体,实际是要等到cleanup组件被销毁,才会最终销毁实体

 22-2:判断实体无效的方法

                if (!SystemAPI.Exists(target.ValueRO.targetEntity) || !SystemAPI.HasComponent<LocalTransform>(target.ValueRO.targetEntity))
                {
                    target.ValueRW.targetEntity = Entity.Null;
                }

评论区有人提出可以用state.EntityManager.Exists,但也有人说SystemAPI效率显著优于EntityManager,SystemAPI是基于Burst 编译的,EntityManager是传统的管理器对象

并且提到,用Exists其实也有类似的问题,Unity在这一帧的末尾只是销毁了LocalTransform组件,在下一帧的中间才销毁Entity,尝试获取LocalTransform的时候就会发现实体还在,组件没了

23-1:设置子弹世界坐标

            float3 bulletSpawnWorldPosition = localTransform.ValueRO.TransformPoint(shootAttack.ValueRO.bulletSpawnLocalPosition);
            SystemAPI.SetComponent<LocalTransform>(bulletEntity, LocalTransform.FromPosition(bulletSpawnWorldPosition));
            //SystemAPI.SetComponent(bulletEntity, LocalTransform.FromPosition(localTransform.ValueRO.Position + shootAttack.ValueRO.bulletSpawnLocalPosition));//我认为这样也可以

24-1:collisionWorld.OverlapSphere 有可能检测到待销毁的Entity。所以要判空

                    if (!SystemAPI.Exists(distanceHit.Entity) || !SystemAPI.HasComponent<LocalTransform>(distanceHit.Entity))
                    {
                        continue;
                    }

25-1:持久化保存Random随机对象,不然每次new Random或者不存RW回去的话,都是相同的值

Random random = randomWalking.ValueRO.random;
//do something
randomWalking.ValueRW.random = random;

25-2:使用EntityCommandBuffer进行AddComponent操作,否则会报错

25-3:此节有个bug,僵尸生成时,会在世界坐标原点闪一下再回到生成器的位置,这个看后续有没有处理

26-1:把对象的TransformUsageFlags设置为NonUniformScale(或者预制体本身缩放不均匀,在Bake时会自动添加PostTransformMatrix组件),然后设置PostTransformMatrix,即可实现对单一轴缩放

barVisualPostTransformMatrix.ValueRW.Value = float4x4.Scale(healthNormalized,0f,0f);

26-2:rotation设置要本地化

localTransform.ValueRW.Rotation = quaternion.LookRotation(parentLocalTransform.InverseTransformDirection(cameraForward), math.up());

26-3:Camera.main与[BurstCompile]冲突,期待后面用job的重构

27-1:一种事件的实现方式

    public OnShootEvent onShoot;

    public struct OnShootEvent 
    {
        public bool isTriggered;
        public float3 shootFromPosition;
    }

 28-1:从僵尸中心发射线,判断是否碰到了攻击对象的碰撞体,射线长度是僵尸的colliderSize和一个偏移量,其中这个colliderSize是胶囊碰撞体的的半径而不是直径

 28-2:发现一个神奇的现象,如果把胶囊碰撞体radius调的大于height,就无法发生碰撞了,

评论区早就有人遇到这个问题了,害我查了半天:

 胶囊碰撞体由两个半球体和一个圆柱体组成,其有效高度要求 圆柱部分的长度 ≥ 0。当半径超过高度的一半时,圆柱部分的长度会变为负数(Height - 2*Radius < 0),导致胶囊的几何形状无法正常生成,进而破坏碰撞检测逻辑

29-1:初始设置的一种方法:创建一个空的组建来标记设置某些东西,然后再对应的系统中运行一次后移除组件

29-2:SystemAPI之外,另一种启用或禁用组件的方式

EnabledRefRW<MoveOverride> moveOverrideEnabled

moveOverrideEnabled.ValueRW = false

29-3:WithDisabled:让查询匹配某个组件被禁用的情况

posted @ 2025-06-16 18:45  zerozabuu  阅读(250)  评论(0)    收藏  举报