Giter Club home page Giter Club logo

edgar-unity's Introduction


Edgar for Unity

Configurable procedural level generator for Unity

Introduction | Key features | PRO version | Limitations | Getting started | Installation | Example | Get in touch

Introduction

This project is a Unity plugin for procedural generation of 2D dungeons (and platformers) and aims to give game designers a complete control over generated levels. It combines graph-based approach to procedural generation with handmade room templates to generate levels with a feeling of consistency. Under the hood, the plugin uses Edgar for .NET.

Graph-based approach

You decide exactly how many rooms you want in a level and how they should be connected, and the generator produces levels that follow exactly that structure. Do you want a boss room at the end of each level? Or da shop room halfway through the level? Everything is possible with a graph-based approach.

Handmade room templates

The appearance of individual rooms is controlled with so-called room templates. These are pre-authored building blocks from which the algorithm chooses when generating a level. They are created with Unity tilemaps, but they can also contain additional game objects such as lights, enemies or chests with loot. You can also assign different room templates to different types of rooms. For example, a spawn room should probably look different than a boss room.

Key features

  • Procedural dungeon generator
  • Describe the structure of levels with a graph of rooms and connections
  • Control the appearance of rooms with handmade room templates
  • Connect rooms either directly with doors or with short corridors
  • Easy to customize with custom post-processing logic
  • Supports Unity 2018.4 and newer
  • Currently works only in 2D but may support 3D in future
  • Comprehensive documentation
  • Multiple example scenes included

PRO version

There are two versions of this asset - free version and PRO version. The free version contains the core functions of the generator and should be fine for simple procedural dungeons. The PRO version can be bought on the Unity Asset Store and contains some additional features. As of now, the PRO version contains features like platformer generator or isometric levels and also two advanced example scenes. If you like this asset, please consider buying the PRO version to support the development.

  • Coroutines - Call the generator as a coroutine so that the game does not freeze when generating a level (docs)
  • Custom rooms - It is possible to add additional fields to rooms and connections in a level graph (docs)
  • Platformers - Generator that is able to produce platformer levels (docs, example)
  • Isometric - Simple example of isometric levels (example)
  • Dead Cells - Tutorial on how to generate levels that are similar to Dead Cells (docs)
  • Enter the Gungeon - Tutorial on how to generate levels that are similar to Enter the Gungeon (docs)
  • Custom input - Modify a level graph before it is used in the generator (e.g. add a random secret room) (docs)
  • Fog of War - Hide rooms in a fog until they are explored by the player (docs)
  • Minimap support (docs)
  • Door sockets - Use door sockets to specify which doors are compatible (docs)
  • Directed level graphs - Use directed level graphs together with entrance-only and exit-only doors to have better control over generated levels (docs)

Limitations

  • Still in alpha version - there may be some breaking changes in the API
  • Some level graphs may be too hard for the generator - see the guidelines
  • The graph-based approach is not suitable for large levels - we recommend less than 30 rooms
  • Not everything can be configured via editor - some programming knowledge is needed for more advanced setups

Getting started

Install the asset (instructions are below) and head to the documentation. The documentation describes all the basics and also multiple example scenes that should help you get started.

Installation

There are several ways of installing the plugin:

via .unitypackage

Go to Releases and download the unitypackage that's included in every release. Then import the package to Unity project (Assets -> Import package -> Custom package).

How to update

In order to be able to download a new version of the plugin, we recommend to not change anything inside the Assets/ProceduralLevelGenerator folder. At this stage of the project, files are often moved, renamed or deleted, and Unity does not handle that very well.

The safest way to update to the new version is to completely remove the old version (Assets/ProceduralLevelGenerator directory) and then import the new version. (Make sure to backup your project before deleting anything.)

via Package Manager

Add the following line to the packages/manifest.json file under the dependencies section (you must have git installed):

 "com.ondrejnepozitek.edgar.unity": "https://github.com/OndrejNepozitek/Edgar-Unity.git#upm"

To try the examples, go to the Package Manager, find this plugin in the list of installed assets and import examples.

Note: When importing the package, I've got some weird "DirectoryNotFoundException: Could not find a part of the path" errors even though all the files are there. If that happens to you, just ignore that.

How to update

After installing the package, Unity adds something like this to your manifest.json:

  "lock": {
    "com.ondrejnepozitek.edgar.unity": {
      "hash": "fc2e2ea5a50ec4d1d23806e30b87d13cf74af04e",
      "revision": "upm"
    }
  }

Remove it to let Unity download a new version of the plugin.

Do not clone the repository

When installing the plugin, use the two methods described above. If you clone the repository directly, you will probably get an error related to some tests and missing types/namespaces. This error is caused by the fact that the plugin is developed with Unity 2018.4 (for better compatibility), but the assembly definition format was changed in newer versions (a reference to the Edgar .NET assembly is missing).

Workflow

1. Draw rooms and corridors

2. Prepare the structure of the level

3. Generate levels

Examples

Get in touch

If you have any questions or want to show off what you created with Edgar, come chat with us in our discord server. Or if you want to contact me personally, use my email ondra-at-nepozitek.cz or send me a message on Twitter at @OndrejNepozitek.

Terms of use

The plugin can be used in both commercial and non-commercial projects, but cannot be redistributed or resold. If you want to include this plugin in your own asset, please contact me, and we will figure that out.

edgar-unity's People

Contributors

ondrejnepozitek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

edgar-unity's Issues

Rule tiles that are aware of individual rooms

If we want to use a rule tile e.g. for walls, we specify how should the tile look depending on neighbouring tiles. Hovewer, if two walls from different rooms are parallel and right next to each other, the walls are strangely connected to the other room (see the attached image).

image

Possible solution:
Add an option to differentiate between tiles from the current room and tiles from a different room. In that case, a dungeon generator would have to provide a method to check whether two tiles belong to the same or to different rooms.

Not possible to create doors with length one

This is due to the fact that the layout generator does not support having doors that consist of only a single vertex (it needs at least one edge).

One possible approach to this problem is to simply remove the length check from the door handler in the generator. Such doors would have to be accompanied with an explicitly set direction, because when the point is a corner point of the polygon, we do not know from which direction should the rooms be connected. This functionality is already supported by OrthogonalLine.

Changing the random seed value last minute

Sorry to bother I was just hoping to get your input on this.

I am currently trying to change the dungeon seed value right before it generates the dungeon, my project is a multiplayer game so my ultimate plan is to have the server generate a random dungeon and when clients connect it will tell the clients what seed to use, to make things easier on me I'm hoping to leave the "Use Random Seed" open on but change the seed right before the generator uses it so all clients generate the same dungeon.

I'm not against disabling the "Use Random Seed" open for clients joining a server but it would be easier for me to leave it on.

Any ideas? :)

Disable colliders before copying tiles

It seems that if colliders are enabled on shared tilemaps when we copy individual room templates, Unity tries to incrementally rebuild them to update their shape. When working with bigger levels, this can cause noticeable lag (up to 10 seconds on levels with hundreds of rooms, 1-2 seconds on medium sized levels).

Based on some quick tests, it seems like it is better to first disable all colliders and only enable them after all tiles are copied to the final level. The difference might not be noticeable on small levels, but it went from 10 seconds to cca 2 seconds iirc. We need to do some additional performance tests.

There are at least 2 possible approaches to handle this:

  1. We can split tilemap layers initialization into 2 stages. In the first stage, we would create tilemaps without colliders and in the second stage, we would only add the colliders. The second stage would be called only after all the tiles were copied. The problem of this approach is that it makes it harder for uses to implement their own tilemap initialization logic because they would need to split the logic.
  2. Before copying tiles, we can go through all the colliders of individual tilemap layers, mark which ones were enabled and then disable them. We would then copy all the tiles and after that we would enable all the collider that were disabled in the previous step. The advantage of this approach is that it is transparent for users. We just have to make sure that there are no problems with disabling and then enabling colliders.

Support for multiple layered tilemaps

It is often needed to layer multiple tiles on each other. Mainly where an image does not have its own background and should be therefore placed e.g. on a floor tile.

Configure which doors can be connected together

This feature will be included only in the PRO version.

It is currently not possible to choose which doors can be connected together. For example, we could use this with isometric levels to mark the elevation of individual doors. Or we may want to choose that some door positions are reserved only for a specific type of rooms.

This issue needs more information.

Are there any ways to get size(or bounds) of the rooms? (or the room's doors position)

I have a question: How to get the size of each generated rooms? In my game, i want to know the which rooms the player's at so the way to do it is just take the room position and the size of the room and compare it to the player position. I know how to get the room position but what about the size? Or it will be easier if you can show me how to get the doors position?

Generator does not appear to work for dungeons that require a collider on floor

Hi,

I have a player and some enemies that are required to jump. For this the floor component is required to have a collider.

I managed to simply add this in script by adding the following function to DungeonTilemapLayersHandler:

protected void AddFloorCollider(GameObject gameObject)
        {
            var tilemapCollider2D = gameObject.AddComponent<TilemapCollider2D>();
            tilemapCollider2D.usedByComposite = true;
            var compCollider2D = gameObject.AddComponent<CompositeCollider2D>();
            compCollider2D.geometryType = CompositeCollider2D.GeometryType.Polygons;
            gameObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Static;
        }

But from how the dungeon is stitched together I believe this is a problem for me, as the dungeon appears to work visually but the floor colliders prevent the player from leaving the room. As can be seen from the screenshot below:

floor_problem2

As can be seen, rather than a continuous collider across the entire floor we have individual floor colliders on each room template and when stitched together block the movement of the player.

Furthermore, this is a problem with instantiating enemies players in post-process as well, as the player prefab is considered part of the template and the collider is drawn on top of the player preventing its movement.

collider drawn on top of player preventing its movement discussed here
The above screenshot shows this, when moved the knight very slowly moves along the polygon collider drawn on top of floor :(

Is there any way I can get around this problem?

Thanks

Level Graph grid Snapping

Snapping the room nodes to the grid so we can have a little nicer placement would be amazing but I guess it's a little pet peeve of mine haha. :)

Isometric levels

This feature will be included only in the PRO version.

The plugin should be able to support isometric levels.

Progress:

  • Display room template outline
  • Display doors
  • Manual door mode - does not work, the algorithm to convert mouse position to cell position must be changed
  • Make sure that Isometric Z as Y tilemaps do not break anything because there can be multiple tiles with the same x and y coordinates but different z
  • Add example scene
  • Update documentation

Current state:
The basics are already working
image

How to close off dead end?

I saw in the examples that your rooms was tight (all the outlines have tiles) and when the corridoors connect to it, it would disable the tile at the door's position. I didn't use corridor in my game (it was a 2d side scroller) so how could i achived this? While I testing by having tiles at door's position i noticed that if the room was generated first it would disable the tile at the connection, if not it wouldn't.

Templating features

The basic usacase should be that templating scripts run after all markers are emitted from the previous tasks in the pipeline.

For now, I can imagine the following templating algorithms:

  • correction of walls if wall tiles must follow some predefined rules (e.g. the tileset is a Wang tileset)
  • templating based on predefined room templates
    • either using a template that is completely premade or applying some kind of procedural algorithm

It should be possible to combine multiple algorithms:

  • using predefined templates and then applying wall correction

The combination of multiple algorithm shows us that the order of individual algorithms may be important. We have basically two options:

  • either we make the algorithms completely independent (predefined templates are instantiated without walls so the correction algorithm does not care whether it is run before or after that)
  • or we have always think about the execution order, with the benefit of code being often simpler because we can overwrite actions of previous steps

For templating algorithms to work correctly, we will have to provide information about which tile belongs to which room (if the dungeon consists of individual rooms).

There must be at least one point??

Hi,

So I followed the documentation and followed the default tilemap layers order and created a few basic room and corridors. I added them to the level graph and when I click on generate dungeon in the generator I get the following error:

ArgumentException: There must be at least one point
Assets.ProceduralLevelGenerator.Scripts.Generators.Common.RoomTemplates.RoomTemplatesLoader.GetPolygonFromTiles (System.Collections.Generic.HashSet`1[T] allPoints) (at Assets/ProceduralLevelGenerator/Scripts/Generators/Common/RoomTemplates/RoomTemplatesLoader.cs:30)
Assets.ProceduralLevelGenerator.Scripts.Generators.Common.RoomTemplates.RoomTemplatesLoader.GetPolygonFromTilemaps (System.Collections.Generic.ICollection`1[T] tilemaps) (at Assets/ProceduralLevelGenerator/Scripts/Generators/Common/RoomTemplates/RoomTemplatesLoader.cs:114)
Assets.ProceduralLevelGenerator.Scripts.Generators.Common.RoomTemplates.RoomTemplatesLoader.GetPolygonFromRoomTemplate (UnityEngine.GameObject roomTemplate) (at Assets/ProceduralLevelGenerator/Scripts/Generators/Common/RoomTemplates/RoomTemplatesLoader.cs:134)
Assets.ProceduralLevelGenerator.Scripts.Generators.Common.RoomTemplates.RoomTemplatesLoader.GetRoomTemplate (UnityEngine.GameObject roomTemplatePrefab, System.Collections.Generic.List`1[T] allowedTransformations) (at Assets/ProceduralLevelGenerator/Scripts/Generators/Common/RoomTemplates/RoomTemplatesLoader.cs:177)
Assets.ProceduralLevelGenerator.Scripts.Generators.Common.LevelDescription`2[TRoom,TConnection].GetRoomTemplate (UnityEngine.GameObject roomTemplatePrefab) (at Assets/ProceduralLevelGenerator/Scripts/Generators/Common/LevelDescription.cs:98)
System.Linq.Enumerable+SelectListIterator`2[TSource,TResult].ToList () (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
System.Linq.Enumerable.ToList[TSource] (System.Collections.Generic.IEnumerable`1[T] source) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
Assets.ProceduralLevelGenerator.Scripts.Generators.Common.LevelDescription`2[TRoom,TConnection].GetCorridorRoomDescription (System.Collections.Generic.List`1[T] roomTemplatePrefabs) (at Assets/ProceduralLevelGenerator/Scripts/Generators/Common/LevelDescription.cs:85)
Assets.ProceduralLevelGenerator.Scripts.Generators.Common.LevelDescription`2[TRoom,TConnection].AddCorridorConnection (TConnection connection, System.Collections.Generic.List`1[T] corridorRoomTemplates, TRoom corridorRoom) (at Assets/ProceduralLevelGenerator/Scripts/Generators/Common/LevelDescription.cs:59)
Assets.ProceduralLevelGenerator.Scripts.Generators.DungeonGenerator.PipelineTasks.FixedInputPipelineTask`1+<Process>d__1[TPayload].MoveNext () (at Assets/ProceduralLevelGenerator/Scripts/Generators/DungeonGenerator/PipelineTasks/FixedLevelGraphPipelineConfig.cs:59)
Assets.ProceduralLevelGenerator.Scripts.Pipeline.PipelineRunner+<GetEnumerator>d__1.MoveNext () (at Assets/ProceduralLevelGenerator/Scripts/Pipeline/PipelineRunner.cs:144)
Assets.ProceduralLevelGenerator.Scripts.Pipeline.PipelineRunner.Run (System.Collections.Generic.IEnumerable`1[T] pipelineItems, System.Object payload) (at Assets/ProceduralLevelGenerator/Scripts/Pipeline/PipelineRunner.cs:18)
Assets.ProceduralLevelGenerator.Scripts.Generators.DungeonGenerator.DungeonGenerator.Generate () (at Assets/ProceduralLevelGenerator/Scripts/Generators/DungeonGenerator/DungeonGenerator.cs:79)
Assets.ProceduralLevelGenerator.Scripts.Generators.DungeonGenerator.Editor.DungeonGeneratorInspector.OnInspectorGUI () (at Assets/ProceduralLevelGenerator/Scripts/Generators/DungeonGenerator/Editor/DungeonGeneratorInspector.cs:63)
UnityEditor.UIElements.InspectorElement+<>c__DisplayClass55_0.<CreateIMGUIInspectorFromEditor>b__0 () (at <9ba1187881a84a08b5f9c4cb333d7a6a>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

The only thing I changed in my room templates prefabs is to add tilemap collider and composite collider to the floor, as they need to have colliders for the players to not fall through. Can this be the reason for the error?

Thank you

Tilemap didn't save the transform of a tile

This is a problem that i've already knew in unity when i was trying to save and spawn an array of tiles. If you rotated or flipped the tile when drawing (by pressing "[" or "Shift+[") and then saved it back into an array it wouldn't save the transform of it so when you spawned the tiles back it would be flipped back to normal which was really weird. Some of my rooms while drawing i flipped and it wouldn't be stored. Are there any ways to get around it? I would recommend you to store the transform of the tiles (Matrix4x4) in the next update.

Can you please explain adding enemies to documentation?

Hi,

Ive been trying to make this run on a tileset that I bought and I'm a bit stuck. The tilemap is similar to Example2 as there is a thin layer above the walls. Can I use the same corridor correction file for my tileset that you used for Example 2, or will I have to write my own?

And from reading the comments on your blog, I understand that enemies can also be automaticaly spawned as part of the generation pipeline. But there is no mention of it in the documentation.

Can you please explain how that would be possible?

Thank you

Make MarkerType a scriptable object

MarkerType is currently an enum. This makes it impossible to add a new marker type without changing the original source code.

Possible solution is to use a scriptable object instead. It would let users at their own marker types and write scripts that can work with them.

Example usecase:

  1. Create custom marker type for torches
  2. Create pipeline task that is able to add torches to the layout
  3. Use a basic templating task to assign torch tile to the torch marker type

It would be also nice to have a typesafety similar to enums. One possible approach is to make a class that will hold references to individual marker types, e.g. MarkerType.Wall, MarkerType.Floor etc..

how to save generated map but in game ?

for exaample , play the project then click generate map then back to main menu then go to that map agan without generating level but use last generated map

How to change the structure of layers in room templates?

I wanted to change the order and add some layers to the room templates like:
background 1: order 0
background 2: order 1
walls: order 2 (collider)
collideable: order 3
other 1, 2, 3: ...
And i also wanted my collider to not use Composite by default and also added some gameobject.
How to do that? I didn't understand the docs well.

Extracting room templates from an existing tilemap

It would be nice to be able to extract room templates easily from an existing tilemap.

Example usecase:

  1. generate a random tilemap consisting of different rooms
  2. find a nice looking room
  3. (optional) modify the room
  4. extract the room from the tilemap and create a new tilemap only with that room

Contradiction in documentation and license

Hey there, just found your project and it's cool. You got some nice docs too, good job, that is some great work. :)

I just wanted to let you know there is a contradiction in your ReadMe and the actual license being used to serve the code.

Terms of use
The plugin can be used in bot commercial and non-commerical projects but cannot be redistributed or reselled. If you want to include this plugin in your own asset, please contact me and we will figure that out.

But you're using an MIT License which explicitly states that it gives the [...] rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software [...]
.

image

I'm not a lawyer so I'm not sure at all, but I think the license has priority here. Anyway, I just wanted you to know. I don't think this can be solved for current project (or at least current revision) since the license is already part of the code. There may be more appropriate licenses out there if you don't want people to distribute. Some people also use the concept of double license so anyone can use the code if they grab the project from source, but they may have to pay to use a prebuilt library, etc.

Again, since I'm not a lawyer, maybe I'm totally wrong and having a Terms of use in the documentation may have priority, but that's probably just confusing.

Feel free to close this issue and have an awesome day ;)

Improve the process of creating pipeline tasks

When creating a pipeline task, I often forget to add the attribute and receive a runtime error. It would be better to have a convention instead of the attribute.

It also feels quite useless to pass the payload as an argument to the Process() function because the object lives only for the duration of a single pipeline run. The problem is that if the task consists of multiple methods that require access to the payload, we have to create a new class variable for the payload. It would be maybe better to pass to payload through a public property instead.

One possible solution would be to add a common ancestor that would contain both the Config property and the Payload property. However, the IPipelineInterface probably should not be deleted if, for some reason, someone does not want to use the common ancestor.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.