Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

A little experiment with DOTS: survivar 2D game.

Discussion in 'Entity Component System' started by Ofreyre, May 26, 2020.

  1. Ofreyre

    Ofreyre

    Joined:
    Jul 17, 2013
    Posts:
    28
    Hi, some time ago I decided to laern a little bit of DOTS by making a simple small game 2D with minimal functionality, and test ECS viability, nothing fancy:
    • Thousands of animated enemies.
    • Tile base procedural map.
    • Items to pick and power-ups.
    • Different weapons.
    • Difficulty levels.
    • Save and load game state.
    I work hard for two months (including the primitive art) and this is the result:
    https://gamejolt.com/games/a-personal-hell/472854

    My conclusions:
    • Although I only did little optimizations, the game runs at a very high frame rate even on old low-end computers.
    • At least for this game, if design from the beginning thinking on ECS, it's far easier than OOP-mono.
    • There are some peripheral functionality that are easier with OOP-mono.
    • It's insanely easy to get very good performance.
    • It's insanely easy to save game state.
    • There is no 2D render for ECS! Although it's not complicated to implement it, this is a must.
    • Refactoring is so much easier than OOP, mainly because you don't have to refactor, only make some changes here and there.
    • Decoupling is so much easier than OOP, mainly because there is no coupling.
    I enjoyed very much this new programming pattern, I fill it gives you a level of freedom that OOP don't have. ECS let's you think in terms of functionality and no in terms of classes, removing a level of abstraction that seems unnecessary most of the time.
     
  2. OldMage

    OldMage

    Joined:
    Jun 25, 2018
    Posts:
    10
    I just started working on a 2D Dots prototype myself, yours looks great! I'm excited to see how close to your level of functionality I can get.
     
  3. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    Nicely done! Can you share more insights on how did you do 2d renderer?
     
    OldMage likes this.
  4. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Quads, quads everywhere :)
     
    OldMage likes this.
  5. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    and what about animations?
     
  6. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Really not meaning to hijack @Ofreyre 's thread, but it's been done before


     
    MNNoxMortem and OldMage like this.
  7. Ofreyre

    Ofreyre

    Joined:
    Jul 17, 2013
    Posts:
    28
    Please don't get me wrong, I don't mean to say that I'm the first to do this, or that I've invented gunpowder. I only share my personal experience about making a simple game with DOTS and not simply and not just a particular functionality (there is a great difference when you have to make many functionalities work together efficiently), and my subjective conclusions about the convenience and advantages of the ECS pattern.

    There are many ways to implement 2D render and 2D animations. I have used sparce space subdivision and shared components for render, sparce space subdivision boids and collisions, and quare diamond algorithm for procedural map generation (although very fast, very difficult to parallelize). My main criterion for my elections where parallelization or just inherent speed.

    These were my design decisions, of course there are many different options, and common experience will tell us more efficient options.

    Cheers and good luck!
     
    OldMage and nicolasgramlich like this.
  8. Ofreyre

    Ofreyre

    Joined:
    Jul 17, 2013
    Posts:
    28
    For 2D render I wanted to support:
    • Depth based on Y coordinate aprox. This is because some game objects (not gameObject) have children, or because the origin of mesh used for the sprite may not coincide with the position on screen. This gave me more flexibility and optimization.
    • Animations from which sprites are in different textures.
    • Flipping
    • Only render sprites in screen.
    This was my solution:
    • Hope this help.RenderData struct that holds the mesh (quad) and material.
    • An array of RenderData. Every sprite is associated with on of the RenderData elements of the array.
    • Create arrays of sprites sorted by their "Y" coordinate (y + offsey). Each array has only sprites associated with the same RenderData. The last is important because you can pass one mesh and material with each call to Graphics.DrawMesh or Graphics.DrawMeshInstanced. The less calls the better.
    • I used a NativeMultiHashMap<int, SpatialRender> for a sparce partition, where the key = "Y" * (a float), and add the sprite data (position, RenderData index, matrix and animation frame) to the partition only if it's in screen. This give me a presort set of sprites because each key belongs to a range of "Y" positions. Because I sort the sprites of each key to send them to render, this gives more control over the amount of sprites in each sort, instead of sorting all the sprites together.
    • Then copy the keys to an array, sort the array and loop through it, put the values of each key on arrays, change to new array if the RenderData of the sprite changes. Every step can be parallelized.
    • The job that fills the NativeMultiHashMap<int, SpatialRender> works over all entities that have Translation, RenderFrameData (has an index to array of RenderData, the sprite frame, flip info, Y offset) and the job that handles animation modifies RenderFrameData component of entities that also have TimeControl component. That is how the render system renders any sprite, it doesn't matter if it's part of an animation or not. For the render system the frame of RenderData is only a frame, for the animation system is an animation frame. The components may have different meaning for different sytems, indeed, the systems give mening to the components.

    Hope this helps.
     
    Krajca likes this.
  9. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Nicely done Ofreyre. And thanks for writing up your experience.

    It's great to hear that the foundation of ECS is working well for you.

    And yes we are still missing high-perf functionality / features on top of it to make all kinds of games out of the box easier, instead of you having to write your own 2D render layer. Those are definitely coming.
     
    Aratow, OldMage, Krajca and 3 others like this.