DOT Training Sample —— Jump The Gun

https://github.com/Unity-Technologies/DOTS-training-samples


Original

代码清单:

  • Box.cs
  • CameraControl.cs
  • Cannonball.cs
  • CannonballPrefab.cs
  • Game.cs
  • Options.cs
  • OptionsToggle.cs
  • Parabola.cs
  • Player.cs
  • SliderProp.cs
  • Tank.cs
  • TerrainArea.cs
1. Box.cs

构成场地的各个立方体。

namespace JumpTheGun {

    public class Box : MonoBehaviour {

                //立方体位置的间隔,默认为1,当立方体长度也为1时,正好无缝
        public const float SPACING = 1; 

                // 竖直方向的偏移,没什么用
        public const float Y_OFFSET = 0;

                // 最低高度
        public const float HEIGHT_MIN = .5f;

                //最低高度的颜色
        public static Color MIN_HEIGHT_COLOR = Color.green;

                //最高高度的颜色
        public static Color MAX_HEIGHT_COLOR = new Color(99 /255f, 47 /255f, 0 /255f);

                //当前box处于第几行、第几列
        public int col { get; private set; }
        public int row { get; private set; }

                //高度访问器,修改高度时,调用UpdateTransform
        public float height {
            get {
                return _height;
            }
            set {
                _height = value;
                UpdateTransform();
            }
        }
              //  顶部的高度(local space)
        public float top {
            get {
                return transform.localPosition.y + height / 2;
            }
        }
            //   颜色访问器
        public Color color {
            get {
                return meshRenderer.material.color;
            }
            set {
                meshRenderer.material.color = value;
            }
        }
           // 设置box的行、列、高度,并调用UpdateTransform
        public void SetBox(int col, int row, float height){
            this.col = col;
            this.row = row;
            this.height = height;
            UpdateTransform();
        }
         
//检测box是否与参数传入的正方体碰撞
        public bool HitsCube(Vector3 center, float width) {
                        //box的bounds
            Bounds boxBounds = new Bounds(transform.localPosition, new Vector3(SPACING, height, SPACING));
                        //正方体的bounds
            Bounds cubeBounds = new Bounds(center, new Vector3(width, width, width));

            return boxBounds.Intersects(cubeBounds);
        }

//更新transform组件,且根据高度改变颜色
 // 访问Game.instance.maxTerrainHeight获得最高高度
        public void UpdateTransform(){

            Vector3 pos = new Vector3(col * SPACING, height / 2 + Y_OFFSET, row * SPACING);
            Vector3 scale = new Vector3(1, height, 1);

            transform.localPosition = pos;
            transform.localScale = scale;

            // 改变颜色,根据高度线性插值
            if (Mathf.Approximately(Game.instance.maxTerrainHeight, HEIGHT_MIN)) {
                color = MIN_HEIGHT_COLOR;
            } else {
                color = Color.Lerp(MIN_HEIGHT_COLOR, MAX_HEIGHT_COLOR, (height - HEIGHT_MIN) / (Game.instance.maxTerrainHeight - HEIGHT_MIN));
            }

        }
              //box被炮弹打中后,降低一定高度
        public void TakeDamage(){
            height = Mathf.Max(HEIGHT_MIN, height - Game.instance.boxHeightDamage);
        }

        // Use this for initialization
        void Awake() {
            meshRenderer = GetComponent<MeshRenderer>();
        }   
             // 高度
        private float _height = 1;
          // Mesh renderer引用,用来修改颜色
        private MeshRenderer meshRenderer;

    }

}
2. Cannonball.cs && CannonballPrefab.cs
  • CannonballPrefab.cs: 单例,引用加农炮prefab资源,之后可用CannonballPrefab.instance.cannonballPrefab访问
  • Cannonball.cs 静态成员和方法实现对象池。发射方法,根据抛物线更新自身位置,检测碰撞。依赖TerrainArea类、Game类
3. Parabola.cs

抛物线类。只有两个静态方法。

其余省略,面向对象大家都比较熟悉就不赘述了。本来想画下类之间的依赖关系图。但其实就是一团乱麻。当然,你可以想方设法让代码看起来“漂亮”些,但其实复杂度的本源是问题本身。你对问题的数据分析和建模,以及采用的算法才是你该着重思考的部分。传统的面向对象经常使我们没法跳出框架,尝试用不同的角度去分析问题,更不用说,它经常使我们陷入一些无意义的框架陷阱。


Ported

代码清单:

  • BlockTransformSystem.cs
  • CannonballSystem.cs
  • ComponentTypes.cs
  • FollowPlayer.cs
  • NewGameProxy.cs
  • NewGameSystem.cs
  • Options.cs
  • Parabola.cs
  • PlayerBouceSystem.cs
  • PlayerPositionCacheSystem.cs
  • TankAimSystem.cs
  • TankFireSystem.cs
  • TerrainSystem.cs

主要是找ECS和非ECS之间的状态(数据)传递的方法。虽然传统的OOP有很多弊端,但并不是一无是处。同样的,ECS也不是解决所有问题的银色子弹。我们要灵活地应用它们来解决问题。

Method 1: Cache State in System (ECS -> Monobehavior)

我们可以将数据保存一份在system中,之后我们可以在Monobehavior中使用World.Avtive.GetOrCreateSystem得到system实例就能访问数据了。ECS有明确要求Component不能有方法,但System里是可以有成员变量的(并不是鼓励,我们需要仔细考虑)。关于System间的依赖关系,我们尽可能通过系统的执行顺序,和读写ComponentData来减少。

Method 2: Save reference in System (Monobehavior <-> ECS)

其实跟方法1本质相同,当我们允许在System保存变量后,就可以使用GameObject.Find方法在ComponentSystem中获得Monobehavior的实例引用。

Method 3 : Singleton or Global Variable

单例和全局变量很显然也可以轻易的获得引用。但我们要警惕,全局的mutable变量很有可能带来灾难。

Method 4: Singleton Component/Entity ?

ECS的很大优势在于其内存布局,大大加快我们遍历数据的速度。但这有个前提,需要许多同类型的对象,“Where there is one,there is many”。可在游戏中,我们同样有许多“manager”,它们只需要一个实例。对于这种情况,应该与普通的Component区别对待。Unity还在完善和优化这种少量Entity的情况。

代码详情:

1. ComponentType.cs

所有Component Data类型。

namespace JumpTheGun {
    // Use a 2D position for block entities, mainly to avoid the default
    // EndFrameTransformSystem (in favor of BlockTransformSystem).
    struct BlockPositionXZ : IComponentData
    {
        public float2 Value;
    }
    struct BlockHeight : IComponentData
    {
        public float Value;
    }
    // Index of a block in the terrain cache array.
    struct BlockIndex : IComponentData
    {
        public int Value;
    }

//抛物线轨迹
    struct ArcState : IComponentData
    {
        public float3 Parabola; // a, b, c parameters
        public float StartTime;
        public float Duration;
        public float3 StartPos;
        public float3 EndPos;
    }
//坦克开火cd
    struct TankFireCountdown : IComponentData
    {
        public float SecondsUntilFire;
    };

    // Marks blocks whose LocalToWorld matrix need to be updated after
    // their height changes.`
    struct UpdateBlockTransformTag : IComponentData {}
    // Tags to differentiate tank base & tank cannon entities.
    // TODO(@cort): use IJobChunk to filter based on MeshInstanceRenderer value instead?
    struct TankBaseTag : IComponentData {}
    struct TankCannonTag : IComponentData {}
}
2. BlockTransformSystem.cs

Block指构成地形的立方块。这个系统的作用是更新blocks的transform,即LocalToWorld Matrix。

Jobs List:
1)BlockTransformJob 。
Query:

权限 ComponentType
Read BlockPositionXZ
Read BlockHeight
Read UpdateBlockTransformTag
ReadWrite LocalToWorld
因为我们需要在Job中删除UpdateBlockTransformTag组件,所以必须使用EntityCommandBuffer。这里我们使用BeginSimulationEntityCommandBufferSystem提供command buffer。
namespace JumpTheGun
{
    // For blocks marked with UpdateBlockTransformTag, use their position
    // and height to compute a new LocalToWorld matrix.
    [UpdateAfter(typeof(TerrainSystem))]
    [UpdateBefore(typeof(TransformSystemGroup))]
    public class BlockTransformSystem : JobComponentSystem
    {
        //[BurstCompile] // Can't currently add/remove components in Burst jobs
        struct BlockTransformJob : IJobForEachWithEntity<BlockPositionXZ, BlockHeight, UpdateBlockTransformTag, LocalToWorld>
        {
            public EntityCommandBuffer.Concurrent CommandBuffer;

            public void Execute(Entity entity, int index, [ReadOnly] ref BlockPositionXZ position,
                [ReadOnly] ref BlockHeight height, [ReadOnly] ref UpdateBlockTransformTag _, ref LocalToWorld localToWorld)
            {
                float3 fullPos = new float3(position.Value.x, 0.0f, position.Value.y);
                float3 fullScale = new float3(1.0f, height.Value, 1.0f);
                // TODO(cort): Use WriteGroups here instead
                localToWorld = new LocalToWorld
                {
                    Value = math.mul(float4x4.Translate(fullPos), float4x4.Scale(fullScale))
                };
                CommandBuffer.RemoveComponent<UpdateBlockTransformTag>(index, entity);
            }
        }

        private BeginSimulationEntityCommandBufferSystem _barrier;
        protected override void OnCreateManager()
        {
            _barrier = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
        }

        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            var handle = new BlockTransformJob
            {
                CommandBuffer = _barrier.CreateCommandBuffer().ToConcurrent(),
            }.Schedule(this, inputDeps);
            _barrier.AddJobHandleForProducer(handle);
            return handle;
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,729评论 6 517
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,226评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 169,461评论 0 362
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,135评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,130评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,736评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,179评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,124评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,657评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,723评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,872评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,533评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,213评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,700评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,819评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,304评论 3 379
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,876评论 2 361