GithubHelp home page GithubHelp logo

alexeybond / godot-constraint-solving Goto Github PK

View Code? Open in Web Editor NEW
246.0 7.0 11.0 6.12 MB

WFC (Wave Function Collapse) and generic constraint satisfaction problem solver implementation for Godot 4

License: MIT License

GDScript 100.00%
godot-4 wave-function-collapse constraint-satisfaction-problem procedural-generation godot godot-addon godot4 wfc

godot-constraint-solving's Introduction

WFC (Wave Function Collapse) and generic constraint satisfaction problem solver implementation for Godot 4

Screenshot of generated tile map

Features:

  • Backtracking support. This addon implements backtracking, so it's possible to generate maps that are guaranteed to have no broken cells. However, it may take a lot more time (and more memory) so it's possible to disable backtracking or limit the number of attempts.
  • Multithreading. Generation of a 2d map using WFC algorithm can be split into few tasks. Some of the tasks can be executed concurrently. The algorithm is (in most cases) able to detect if it's impossible to split the task. It falls back to single-threaded generation in such cases.
  • Learning from example. 2d WFC generator is able to infer rules from an example of a valid map. The algorithm also tries to infer some valid cell combinations beyond those provided in the example. In cases when the algorithm produces some invalid or not-nice-looking cell combinations, it's possible to also provide examples of cell combinations that should not appear in the final result. Or stop the generator from searching for additional cell combinations and provide all possible combinations in the initial example.
  • Supports different node types:
    • TileMap (including some hexagonal tilemaps, see example)
    • GridMap (a flat map in one of XY/YZ/XZ planes can be generated)
    • Support of other node types can be added.
  • Supports tile probabilities. Probabilities of specific tiles can be adjusted. In case of TileMap a builtin probability property or a custom data layer can be used. In case of GridMap, probability can be stored as metadate attribute of a mesh.
  • Not just WFC. Addon contains a generic implementation of a solver capable of solving subclass of constraint satisfaction problems on top of which a WFC implementation is built. This generic solver can be reused for tasks different from WFC.

What's not (yet) implemented:

  • 3d map generation. Generation of 3d maps (for GridMaps or multi-layered TileMaps) is not yet implemented.
  • Wrapping.
  • Lazy/dynamic generation. For some games it may make sense to generate parts of level dynamically when they are (about to) become visible to player.
  • Rules editor. Currently it's possible to "learn" WFC rules in running game only, not in editor. Rules can be edited by modifying sample maps, using standard editor tools. There is no special editor for WFC rules.
  • Better demo/examples.
  • Symmetry. In cases when a cell can be rotated (GridMap), the algorithm treats each combination of tile type and rotation as a separate tile type. So, you have to specify possible adjacent tiles for all rotations of each tile (in fact, just few are enough - the algorithm is able to infer other combinations automatically in most cases).

Installing

This addon is available in Godot Asset Library and thus can be installed using editor's built-in addon manager. Alternatievely, you can download an archive from releases page or from current branch.

Important: in order to make WFC2DGenerator node available, you should enable the plugin in project settings after adding addon files to the project: Screenshot of project settings dialog with plugin enabled

How to use

WFC2DGenerator node

The easiest way to use this addon to generate a map is by using a WFC2DGenerator node.

To do so follow the following steps:

  1. Create (or use existing one) a tile set (if you're going to generate a 2d tile map) or mesh library (in case of a grid map).
  2. Make a map (a TileMap or GridMap) with examples of how your tiles should be used.
  3. Create a TileMap or GridMap generated map will be written to. The new map should use the same tile set/mesh library as one created on step 2. You may place some tiles on that map (either manually or procedurally), generator will take them into account and fill other cells accordingly. But try to not create an unsolvable puzzle when doing so.
  4. Create a WFC2DGenerator node and set the following properties:
    • target should point to a map node that will contain a generated map - one created at step 3
    • positive_sample should point to a node that contains an example of a valid map - created at step 2
    • rect should contain a rect of target map that will be filled by generator
    • there are some other settings that may influence behavior and performance of the generator, feel free to experiment with those after you have a basic setup running
  5. Run the generator. By default it will start as soon as a scene runs. However, you can clear start_on_ready flag and call start() method on generator node manually. For example, that can be useful if you fill some of cells in target map procedurally.

The resulting setup may look like the following:

Example of WFC2DGenerator setup

Examples of such setups can be found in examples folder.

It may make sense to create and keep a minimal scene with generator, sample map and target map - just to ensure that samples are good enough to generate a good map with your tile set.

If some of tile combinations produced by generator don't look good - try adding a negative samples map and place those combinations there.

Preconditions

By default the generator will read exsting tiles from a map node it generates content for and will place other tiles to make them fit with existing ones. This behavior makes it possible to combine WFC with other procedural generation algorithms (or manually pre-made level pieces): the previous algorithm may place some tiles and let WFC fill the remaining space. However, this way it is only possible to specify the exact tile that should be placed in specific cell. The preconditions API allows to limit possible cell contents in a more flexible way - by defining a set of tiles allowed in given cell.

The addon includes a precondition (example) that generates a random set of connected road cells surrounded by wall cells a.k.a. a "dungeon". The user can configure which tiles are "roads" and which are "walls" using custom data layers (in case of TileMaps) or metadata (in case of GridMaps) of the tiles. It isn't likely to fit specific needs of any actual game but it may serve as an example and/or starting point.

Advanced use

WFC2DGenerator node is a high-level convenient wrapper for lower-level components. In some cases it may be useful to use the low-level components directly. See sudoku demo as an example.

You can extend different classes of this addon to achieve a desired behavior different from what is available by default. For example, you can:

  • add support for different map types by implementing your own WFCMapper2D subclass
  • add support of global constraints by extending WFC2DProblem
  • use your own versions of internal components with the same interface as WFC2DGenerator by creating your own subclass of WFC2DGenerator

There is no detailed documentation (at least, for now) on how to use or extend internal components of the addon. So please refer to source code to find a way to do what you need and feel free to ask questions in github issues.

Copyright notes

This addon is licenced under MIT licence.

Examples/demos use assets ("tiny dungeon" tileset, "nature kit" models pack) from Kenney. Hexagonal tilemap example uses "Underearth Hex Dungeon" tile set.

This addon uses GUT for unit testing (not included in downloadable archive).

The logo is generated using Stable Diffusion.

godot-constraint-solving's People

Contributors

alexeybond avatar supamiu avatar totallygatsby 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

godot-constraint-solving's Issues

[Docs] How to make a good sample?

After using the addon I feel like I'm still struggling with the same thing: creating a proper sample.

Trying to imitate the one in the example scene will quickly hit limits, like when you have multiple types of ground tiles or you want to create different kind of biomes.

I feel like this might be a nice priority to have for the docs, feel free to close if that's not the case tho.

[Docs] Custom Preconditions2D

Hello,
I want to preface with how much I appreciate this addon as it offers a very complete and relatively easy to use integration of WFC. I am trying to make a custom Precondition2D that uses perlin noise (Or simplex noise) to create denser forested areas and plains that are to be populated by WFC. However, I am having trouble understanding how to use the bitset object and the mapper object. I'm also uncertain on how to add the custom precondition to the generator. Would it be possible to have a small crash course? The dungeon example isn't super explanatory...

[Discussion] Tile probability

After working on layered map support, I realized that it'd be better for me to use this for my game rather than trying to make my own solution, because your solver works like a charm and you seem to be very comfortable with it and keeping it up to date.

What I'm missing for my game is mostly multi-layers (which is being done in #4) and tiles probability.

This issue is mostly to open the discussion on how it could be implemented and what the solver should accomplish to be considered as working with probabilities.

The features

In order to be considered done, the solver would have to be able to read a weight from each tile in a tileset, tile by tile. Setting this info could be done using different methods, one of which being using custom property painting in the tileset editor (or even using the built-in probability property), the other being using some kind of configuration resource for more advanced cases.

A feature that's not required per se but that could be interesting would be to have conditional weights, for instance tile A having a weight of 5 next to tile B but weight of 2 next to tile C, that's a more advanced case that would probably only be solved using a custom resource or a custom ruleset (maybe tile aliases or something to help with that?)

The implementation

If we're using the built-in approach to have a basic probability system, we could simply read the tile's properties to grab probability from that, then using it to computed a weighted random when picking the tile in the solver (I'm failing to see where tho)

Using a more custom approach would require giving cells a probability using a config resource, which could be useful to handle Scene Tiles (which is something I'm doing in my game to have collectable elements placed directly as tiles so they match the grid perfectly).

Maybe we should just make it either support both approaches or just the custom one so worst case, everything can be done with it even tho it'd be worse in terms of DX

Rerun on different Rect.

Hello. Again not an issue, but just want to ask/discuss the plugin.

I am trying to dynamically load chunks of the world as the camera moves around. Basically the algorithm would basically just load the tiles in the world that are visible on the camera. Is there an easy way to rerun the WFC algorithm on a different Rect2i on the same TileMap?

Thanks!

Does this respect the various properties of the tile?

First of all, this is an awesome plugin. I apologize for making an issue post as there's no discussion post options. This is not an issue, just a question. Does this plugin respect the various properties painted onto the tile? e.g. custom data, physics, navigation, etc?

[Docs] Samples and Negative Samples

Hello, I've just started a project and was really curious on using your WFC generator to help generate my isometric world map.
I was reading through another Docs request about good samples, and I'm trying to understand exactly how to make good Positive samples and Good negative samples. Currently, I am trying to generate a city scape using the WFC, but the generator seems to generate a lot of combinations that were either not given in the positive sample or it would generate combinations that were normally regarded as bad from the negative sample.

It would be useful to have some visual examples of how the generation logic is supposed to function so that we can work around it. For example, if I place a tile by itself on the negative sample example will that be understood that that tile should never be placed, or will it understand that I mean to say that that tile should never be alone? As in, it should always have at least 1 neighbouring tile.

Here I have what I'm using currently, and I have complete matrices = false added to the base code.

In case you want some context on the blocks, they're just for visual reference I know it looks nothing like a city lol
But the tiles should represent:
grey = exterior tiles,
black = road tiles,
green = wall tiles,
red = interior tiles,
orange = door tiles,
blue = end of road tiles

Prositive Sample
PositiveSample
Negative Sample
NegativeSample
Result (Long Road is prebuilt by me)
Result

Big key dungeons

Hello, to make a lock and key dungeon:

Generate your dungeon with the locked doors.

How to make a series of separate walkable regions with wfc? And maybe also ensure that there is somewhere a certain tile (door) between each A -> A+1 region. And maybe a way to find the door of each region, so a key can be generated and linked to it.

Air tiles

Hello, how to make it use empty space in the sample? With walls in a GridMap it gave an error assert(mapper.size()>1) because it was only one tile type. Actually there are two types: wall and empty. Add another wall type, it runs but it fills target region with solid wall, ignores free space in positive sample.

How to use your addon to generate dungeons.

Hi there! I've been busy messing around with your addon and trying to find a way to generate rooms that connect in a 2D Tilemap. I was going to try to mess with the rules, but am unsure how it works.
Do you know any good resources on how to use your WFCRules?

[Suggestion] Multi-Layered TileMap Support

I see according to the features implementation it is not yet added. Is there any known workarounds or any current plans to add it to this plugin? If i want to add trees on top of tiles already placed, with the current features this is not possible (see image below.)

Positive Rule Map
image

Generated Output
image

[Docs] Add metadata to GridMap

I am encountering difficulties using WFC2DPreconditionDungeon with the GridMap demo. Specifically, I am unsure how to set metadata for grid tiles, such as "wfc_dungeon_road" and "wfc_dungeon_wall." I have attempted various combinations, including setting the metadata for each MeshInstance3D within the library-source, but have been unsuccessful in retrieving the metadata in the code (the assert(not passable_domain.is_empty()) in /wfc/problems/2d/preconditions/precondition_2d_dungeon.gd always fails).

Is it possible to use WFC2DPreconditionDungeon with gridmap?
How can I set the metadata in my grid tiles?

Suppose that I want to add WFC2DPreconditionDungeon with the gridmap demo, what are the steps that I should follow to set the "wfc_dungeon_road" and "wfc_dungeon_wall" metadata to the mesh?

Am I supposed to set metadata for each MeshInstance3D inside the library-source?

I can contribute by adding some documentation regarding that part when everything will be more clear.

[Discussion] Why gdscript and not something else?

Wouldn't the speed be better using C# or something else? I'm not trying to offend anyone, but I'd really like to know what was the benefit of picking gdscript. Read multiple times that iteration is faster in other languages etc.

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.