r_unity3d | Unsorted

Telegram-канал r_unity3d - r/Unity3D

156

News about the Unity engine and project showcases from Reddit. Made possible with @reddit2telegram (@r_channels).

Subscribe to a channel

r/Unity3D

Selling source code of my puzzle game with infinite level

yup, the title says the most. the game is a puzzle game made in unity 2D. The game had good engagement and took me over 1 year to make this. currently selling the source code for 25$ .

Google admob and Google analytics are integrated

link :- https://play.google.com/store/apps/details?id=com.LRASTUDIO.MAZZER

https://redd.it/1smxekv
@r_Unity3D

Читать полностью…

r/Unity3D

我做了一個2D遊戲

我是一個高三生,透過自學unity跟c#做了一個小遊戲,可以幫我試玩看看嗎?可以的話希望也填一下表單🥹🥹🥹

google表單: https://forms.gle/UGvTdg55Hg3bw3Vp7

遊戲下載連結 https://drive.google.com/drive/folders/1KYKFEJ7\_r2zBKpgVbf4QJQiA11Bpc6Km?usp=sharing

https://redd.it/1smpq19
@r_Unity3D

Читать полностью…

r/Unity3D

Trouble changing code between unity versions

https://redd.it/1smri1z
@r_Unity3D

Читать полностью…

r/Unity3D

behaviour of the car should be completely unaffected by the physics of the cosmetics.

Anyone run into this before? Any brilliant ideas?

https://redd.it/1smpwlz
@r_Unity3D

Читать полностью…

r/Unity3D

Encuesta para Videojuego Indie
/r/videojuegos/comments/1smohzf/encuesta_para_videojuego_indie/

https://redd.it/1smoj3d
@r_Unity3D

Читать полностью…

r/Unity3D

Foldout state keeps resetting (UI Toolkit)
https://redd.it/1smiw9p
@r_Unity3D

Читать полностью…

r/Unity3D

[Free Asset] Save your Runtime Changes instantly with this tool "Play Mode Changes Saver (Runtime-Saver)"

Just wanted to share a small utility I released. It’s a "Runtime Saver" that keeps your component tweaks alive after you exit the Play Mode.

* **Simple to use**
* **Saves time during balancing**
* **100% Free**

Search for **"Play Mode Changes Saver (Runtime-Saver)"**
Feedback is always welcome!

https://redd.it/1smch5w
@r_Unity3D

Читать полностью…

r/Unity3D

Swing mechanic - What should I do with it?

https://redd.it/1sjio3q
@r_Unity3D

Читать полностью…

r/Unity3D

MOVIMENTO

// -----------------------

private void FlipController()

{

if (horizontalInput > 0.01f)

transform.localScale = new Vector3(3, 3, 1);

else if (horizontalInput < -0.01f)

transform.localScale = new Vector3(-3, 3, 1);

}

private void Jump()

{

if (isGrounded())

{

body.linearVelocity = new Vector2(body.linearVelocity.x, jumpForce);

anim.SetTrigger("jump");

}

}

private void CollisionCheck()

{

Vector2 direction = new Vector2(Mathf.Sign(transform.localScale.x), 0f);

RaycastHit2D hit = Physics2D.BoxCast(

capsuleCollider.bounds.center,

capsuleCollider.bounds.size,

0f,

direction,

wallCheckDistance,

groundLayer

);

isWallDetected = hit.collider != null;

canWallSlide = isWallDetected && !isGrounded();

}

// -----------------------

// LEDGE CLIMB

// -----------------------

private void CheckForLedge()

{

if (ledgeDetected && canGrabLedge && !canClimb)

{

canGrabLedge = false;

Vector2 ledgePosition = GetComponentInChildren<LedgeDetection>().transform.position;

float direction = Mathf.Sign(transform.localScale.x);

Vector2 adjustedOffset1 = new Vector2(offset1.x * direction, offset1.y);

Vector2 adjustedOffset2 = new Vector2(offset2.x * direction, offset2.y);

climbBegunPosition = ledgePosition + adjustedOffset1;

climbOverPosition = ledgePosition + adjustedOffset2;

canClimb = true;

}

}

private void LedgeClimbOver()

{

canClimb = false;

body.gravityScale = originalGravityScale;

transform.position = climbOverPosition;

Invoke("AllowLedgeGrab", 0.1f);

}

private void AllowLedgeGrab() => canGrabLedge = true;

// -----------------------

// ANIMAZIONI

// -----------------------

private void HandleAnimations()

{

if (isWallSliding)

{

anim.SetBool("Run", false);

anim.SetBool("fall", false);

anim.SetBool("isWallSliding", true);

}

else

{

anim.SetBool("Run", horizontalInput != 0);

anim.SetBool("grounded", isGrounded());

anim.SetBool("fall", body.linearVelocity.y < -0.1f && !isGrounded());

anim.SetBool("isWallSliding", false);

anim.SetBool("canClimb", canClimb);

}

if (Input.GetKeyDown(KeyCode.X))

{

anim.SetBool("isAttacking", true);

}

}

// -----------------------

// ATTACCO

// -----------------------

public void Attack()

{

Collider2D[\] enemy = Physics2D.OverlapCircleAll(attackPoint.transform.position, radius, enemies);

foreach (Collider2D enemyGameObject in enemy)

{

Debug.Log("Hit Enemy");

enemyGameObject.GetComponent<EnemyHealth>().health -= damage;

}

}

public void EndAttack()

{

anim.SetBool("isAttacking", false);

}

// -----------------------

// UTILITY

// -----------------------

private bool isGrounded()

{

RaycastHit2D raycastHit = Physics2D.BoxCast(

capsuleCollider.bounds.center,

capsuleCollider.bounds.size,

0f,

Vector2.down,

0.1f,

groundLayer

);

return raycastHit.collider != null;

}

private void OnDrawGizmos()

{

if (capsuleCollider == null) return;

Gizmos.color = Color.red;

Vector3 direction3 = Application.isPlaying

? new Vector3(Mathf.Sign(transform.localScale.x), 0, 0)

: Vector3.right;

Gizmos.DrawLine(capsuleCollider.bounds.center, capsuleCollider.bounds.center + direction3 * wallCheckDistance);

if (attackPoint != null)

{

Gizmos.DrawWireSphere(attackPoint.transform.position, radius);

}

}

}

thank you in advance!❤️

https://redd.it/1sjefji
@r_Unity3D

Читать полностью…

r/Unity3D

2D Character Controller (no Rigidbody2D)?

Let's face it: collision checking is hard, and I'm glad Unity has a solution for 3D games with the Character Controller, but there is nothing like that for 2D as far as I know.

I', doing a Top-Down game and I don't want to deal with Rigidbody's physics, so I tried to do my own collide and slide with poor results (I'm not a professional so the complex math it requires is beyond me). I could use the Character Controller in 2D too, but it only detects 3D colliders (of course).

I'm pretty sure I could find a solution in the Asset Store, or I could bash my head over and over to get the collide and slide method working, but is there really no way to have the collision checking of the Rigidbody2D without having to deal with physics?

https://redd.it/1sjemyg
@r_Unity3D

Читать полностью…

r/Unity3D

Mod text in game.
/r/Unity3D/comments/1sjcgav/mod_text_in_game/

https://redd.it/1sjcgvj
@r_Unity3D

Читать полностью…

r/Unity3D

Made a new interactive main menu for Koloboke game

https://redd.it/1sj3l4c
@r_Unity3D

Читать полностью…

r/Unity3D

Help with making a character controller

https://redd.it/1sj323m
@r_Unity3D

Читать полностью…

r/Unity3D

"Can I Take Your Wheels for a Spin?"

https://redd.it/1six875
@r_Unity3D

Читать полностью…

r/Unity3D

Unity's Job System (`IJobFor.ScheduleParallel`). On a 6-core CPU, 6 chunks are solving simultaneously. Cross-chunk conflicts at boundaries happen occasionally, and they're handled cooperatively: uncollapse the conflicting edge cells, reopen that chunk, let it re-solve just that boundary area.

# Wall #3: "Backtracking kills everything"

When WFC hits a contradiction (a cell with zero valid tiles), most approaches either restart the entire grid or pop a single snapshot off a stack. At scale, both of these hurt:

* Full restart: you just threw away 100K collapsed cells because of 1 bad choice
* Single-step undo: can take hundreds of backtracks to escape a real dead-end

**What fixed it: Progressive depth + deferred execution.** When a contradiction happens:

1. **In-job**: try undoing the last 1, 3, 7, or 15 steps (escalating depth based on repeated failures), all inside the Burst-compiled job
2. **If that fails**: flag it for the *next* job dispatch. The main thread just writes an integer to a shared array, and the Burst worker handles the heavier restore on the next frame
3. **If the backtrack stack is fully exhausted**: restart just that one chunk, not the whole grid

I profiled this before and after. Before, backtracking was eating **473ms per frame** on large grids (mostly from sequential AC-3 + repropagation running on the main thread). After deferring that work to parallel Burst workers, the main-thread cost dropped to basically nothing.

# Wall #4: "Propagation lookup overhead"

When you collapse a cell, you BFS outward and ask each neighbor: "given what I just placed, what tiles are still valid for you?" This requires looking up compatibility rules.

Most implementations use a dictionary or hashmap: `rules[(tileID, direction)]` returns the set of compatible tiles. That works fine at small scale. But propagation is the **hottest loop** in WFC. On a million-cell grid, you're hitting that lookup millions and millions of times during a single generation run.

**What fixed it: Pre-computed flat array.** At startup, I flatten the hashmap into a plain array indexed by `[moduleIdx * 4 + direction]`. Array access is a single pointer offset; hashmap access involves hashing, bucket traversal, and potentially collision resolution. I didn't profile the exact per-lookup difference, but after switching, my propagation phase got noticeably faster on the profiler timeline. At the volume of lookups WFC does, even small per-lookup savings compound into real seconds.

# Wall #5: "Too many contradictions in the first place"

Even with great backtracking, contradictions are expensive. The best fix is not having them.

**What fixed it: Look-ahead selection.** Before committing to a tile, I check: "would placing this tile cause any of my 4 neighbors to have zero valid options?" If yes, skip it and try the next candidate. It's a simple 1-hop look-ahead (not deep search), and it prevents a huge chunk of contradictions for well-designed tilesets.

How much it helps depends heavily on your tileset. Look at the benchmark table above: the 25-tile palette and the 331-tile palette are running on the exact same solver with the exact same optimizations. The 10x speed difference is almost entirely from how often contradictions occur. Simpler, cleaner edge rules = fewer dead ends = look-ahead catches almost everything. Complex tile interactions = more situations where even look-ahead can't prevent a contradiction 2-3 hops away.

# How these work together

None of these optimizations work well in isolation. Multi-step execution without Burst compilation would be slow managed C#. Burst without multi-step would still have a million main-thread round-trips. Chunk parallelism without deferred backtracking would stall every time a chunk contradicts. And all of the above without look-ahead would spend most of their time backtracking. They're force multipliers for each other, which is why the combined result is so much better than any single optimization would explain.

# Things I wish I knew when I started

1. **Profile before you optimize.** My first

Читать полностью…

r/Unity3D

How large is your Behaviour Tree?
https://redd.it/1smv188
@r_Unity3D

Читать полностью…

r/Unity3D

We're validating a tool idea before building it — 4 min survey for game devs and 3D artists

I'm Hemanth, an Unreal generalist from India. My friend and I are thinking of building a tool that helps game studios, indie devs, and freelancers catch 3D asset issues before they break in-engine — saving hours of back and forth.

Before we write a single line of code, we want to hear from real artists and devs first. No pitch, no product links — just 14 honest questions about your current workflow.

Takes 4 minutes 👇

https://forms.gle/oPFKtY2yTZSzLFeGA

Really appreciate any responses. Happy to share the results with anyone interested once we have enough data!

https://redd.it/1smssoc
@r_Unity3D

Читать полностью…

r/Unity3D

RogueMuncher DevLog - Speedmuncher character breakdown
https://www.youtube.com/watch?v=iNSSm7Sut9g

https://redd.it/1smq779
@r_Unity3D

Читать полностью…

r/Unity3D

Help me, internet. I need advice on creating Physics2D-enabled cosmetics

Hey everyone, I've got a problem I've been working on for too long and I would love a fresh set of eyes if anyone has any ideas.

In my game, players control a car. I'd like to set up a cosmetics system where players can equip cosmetics that react appropriately with physics (say, a wobbly antenna, or a hat that bounces up and down), but without the mass or drag of the cosmetic affecting the car’s physics (I.E. cosmetics shouldn't affect gameplay).

So if you take the wobbly antenna as an example, the basic setup currently looks like this:

* **Car:** A dynamic Rigidbody2D. It's sometimes moves via AddForce, and sometimes via MovePosition, depending on the context (cutscenes and stuff).
* **CosmeticBase:** A kinematic Rigidbody2D that serves as an anchor point for the rest of the cosmetic.
* **Antenna:** The various dynamic Rigidbody2Ds that make up the actual cosmetic. For now, just image that it's a single dyanmic GameObject attached to the CosmeticBase via a SpringJoint2D.

So in theory, the CosmeticBase follows the Car, and the Antenna is jointed to the CosmeticBase - so the Antenna follows the Car with realistic physics, without itself impacting the physics of the Car.

I cannot for the life of me get this to actually work, though. Here's what I've tried:

**Attempt #1: Simple Parenting**

Having the CosmeticBase be a child of the Car GameObject makes the CosmeticBase track the Car beautifully, but because it's now being moved via transform, the Antenna/Base Joint doesn't behave properly. The physics looks terrible and the Antenna GameObjects are frequently disconnected from and trying to "catch up" to the CosmeticBase.

**Attempt #2: Joint Between Car and Base**

The CosmeticBase now tracks properly and the Antenna/Base Joint works as intended, but this obviously isn't viable because then the mass, drag, etc. of the Antenna are transferred into the Car's physics.

**Attempt #3: Move Position on the CosmeticBase**

After removing the Base/Car Joint and unparenting the Base from the Car, I tried this. In FixedUpdate, I added this onto the CosmeticBase:

BaseKinematicRb2D.position = CarRb2D.position + offset;
BaseKinematicRb2D.rotation = CarRb2D.rotation;

This ALMOST works, but not quite. The Antenna/Base Joint works properly, and the CosmeticBase TRIES to match the Car's position, but the CosmeticBase visually lags behind the Car pretty clearly - I assume because it's matching the Car position from the previous "tick", due to how the Physics system works. It's being set to where the Car WAS, not where it's GOING. I tried messing with the Interpolation settings of all the Rigidbody2D's involved, but nothing changed the end result.

**Attempt #4: Same as above, but with predictive velocity**

The closest I've gotten so far is this:

Vector2 predictedPos = (Vector2)CarRb2D.transform.TransformPoint(positionOffset) + CarRb2D.velocity * Time.fixedDeltaTime;
float predictedRot = CarRb2D.rotation + CarRb2D.angularVelocity * Time.fixedDeltaTime + rotationalOffset;

BaseKinematicRb2D.MovePosition(predictedPos);
BaseKinematicRb2D.MoveRotation(predictedRot);

Where the CosmeticBase "guesses" where it's supposed to be on this Physics step based on the position of the Car and its current velocity.

It's... okay. It sort of works when the Car is being moved by AddForce, but obviously for sudden accelerations or decelerations there can be some noticeable shifting. I don't love it. Obviously it doesn't work at all for MovePosition, but it wouldn't be too hard to add that in too.

The thing is, this isn't exactly a super unique thing that I'm designing, so I feel like there must be some industry standard solution here that I'm missing. I can't imagine Rocket League is using predictive velocities. Anyone have any suggestions?

To summarize, the goal here is:

I would like Dynamic, Physics2D-enabled cosmetics to be able to follow a car, realistically reacting to its movements. However, the physics

Читать полностью…

r/Unity3D

Released our Unity Pinball game where the world is 3D, but the all the physics is in 2D.

https://redd.it/1sminxv
@r_Unity3D

Читать полностью…

r/Unity3D

We’ve implemented a mechanic for creating robots that help the player work on an automated farm
https://redd.it/1smhkt3
@r_Unity3D

Читать полностью…

r/Unity3D

I prototyped a one-handed Star Fox-style mobile game! And it somehow works way better than I expected! Gyro = aim, tap = shoot, swipe = move

https://redd.it/1smdscu
@r_Unity3D

Читать полностью…

r/Unity3D

Making puzzle for my 2d horror game
https://redd.it/1sji1ra
@r_Unity3D

Читать полностью…

r/Unity3D

Can you help me to add the roll/dash into my code for my platform?

i'm not a programmer, and i do all with tutorial on youtube, but i want to make my own game, could you please help me? how can i do a dodge, roll/dash only when the player is on the ground? i'd like it to have i-frames and that he can bypass the enemy (the enemy have the layer enemy if it can be helpful)

now i'll send the code here so you can see it, tell me if i have to be more specific🫡

using UnityEngine;

using UnityEngine.SceneManagement;

public class PlayerMovement : MonoBehaviour

{

[Header("Movement")\]

[SerializeField\] private float speed = 5f;

[SerializeField\] private float jumpForce = 12f;

[Header("Collision")\]

[SerializeField\] private LayerMask groundLayer;

[SerializeField\] private float wallCheckDistance = 0.5f;

[Header("Attack")\]

[SerializeField\] private float attackCooldown = 0.5f;

private float attackTimer;

public GameObject attackPoint;

public float radius;

public LayerMask enemies;

public float damage;

[Header("Ledge info")\]

[SerializeField\] private Vector2 offset1;

[SerializeField\] private Vector2 offset2;

// Componenti

private Rigidbody2D body;

private Animator anim;

private CapsuleCollider2D capsuleCollider;

// Movimento

private float horizontalInput;

private bool isWallDetected;

private bool canWallSlide;

private bool isWallSliding;

// Ledge

[HideInInspector\] public bool ledgeDetected;

private bool canGrabLedge = true;

private bool canClimb;

private Vector2 climbBegunPosition;

private Vector2 climbOverPosition;

// Gravità originale

private float originalGravityScale;

// Singleton per evitare duplicati

public static PlayerMovement Instance { get; private set; }

private void Awake()

{

// Se esiste già un player (da una scena precedente), distruggi questo duplicato

if (Instance != null && Instance != this)

{

Destroy(gameObject);

return;

}

Instance = this;

DontDestroyOnLoad(gameObject); // Il player sopravvive al cambio scena

body = GetComponent<Rigidbody2D>();

anim = GetComponent<Animator>();

capsuleCollider = GetComponent<CapsuleCollider2D>();

originalGravityScale = body.gravityScale;

// Ascolta l'evento di caricamento scena per posizionare il player

SceneManager.sceneLoaded += OnSceneLoaded;

}

private void OnDestroy()

{

SceneManager.sceneLoaded -= OnSceneLoaded;

}

// Chiamato automaticamente ogni volta che una scena finisce di caricare

private void OnSceneLoaded(Scene scene, LoadSceneMode mode)

{

if (PlayerSpawnManager.Instance != null && PlayerSpawnManager.Instance.hasCustomSpawn)

{

transform.position = PlayerSpawnManager.Instance.spawnPosition;

PlayerSpawnManager.Instance.Clear();

}

}

private void Update()

{

// --- INPUT ---

horizontalInput = Input.GetAxis("Horizontal");

// --- FLIP PLAYER ---

FlipController();

// --- CONTROLLO MURO ---

CollisionCheck();

// --- MOVIMENTO E SALTO ---

if (!canClimb)

{

if (isWallSliding)

body.linearVelocity = new Vector2(0, body.linearVelocity.y);

else

body.linearVelocity = new Vector2(horizontalInput * speed, body.linearVelocity.y);

if (Input.GetKeyDown(KeyCode.Z))

Jump();

}

// --- LEDGE GRAB ---

CheckForLedge();

// --- ANIMAZIONI ---

HandleAnimations();

// --- ATTACCO ---

attackTimer -= Time.deltaTime;

if (Input.GetKeyDown(KeyCode.X) && attackTimer <= 0)

{

anim.SetBool("isAttacking", true);

attackTimer = attackCooldown;

}

}

private void FixedUpdate()

{

// --- WALL SLIDE ---

if (isWallDetected && canWallSlide)

{

isWallSliding = true;

body.linearVelocity = new Vector2(

body.linearVelocity.x,

Mathf.Clamp(body.linearVelocity.y, -2f, float.MaxValue)

);

}

else

{

isWallSliding = false;

}

// --- LEDGE CLIMB ---

if (canClimb)

{

body.gravityScale = 0;

body.linearVelocity = Vector2.zero;

body.MovePosition(Vector2.Lerp(body.position, climbBegunPosition, Time.fixedDeltaTime * 8f));

if (Vector2.Distance(body.position, climbBegunPosition) < 0.05f)

{

LedgeClimbOver();

}

}

else

{

body.gravityScale = originalGravityScale;

}

}

// -----------------------

// FUNZIONI

Читать полностью…

r/Unity3D

Cooking instant noodles like a bum in my VR anime narrative game

https://redd.it/1sjbrx0
@r_Unity3D

Читать полностью…

r/Unity3D

Burnt out making a mobile game

https://redd.it/1sj7w9s
@r_Unity3D

Читать полностью…

r/Unity3D

I made an Anime Fight with Lofi Music and Yes you can control the character, do you think is a good Idea?
https://redd.it/1sj6xzx
@r_Unity3D

Читать полностью…

r/Unity3D

Why is my prefab spawning on the wrong Z?

I have it pretty obviously on -3 for Z, but whenever it spawns, it goes to 0! Nothing fancy, just... Don't know why this happens at all

The spawned sprite

Prefab



https://redd.it/1sj1zgh
@r_Unity3D

Читать полностью…

r/Unity3D

instinct was "propagation is slow, optimize propagation." The profiler showed backtracking was 6x worse. I wasted a week on the wrong thing before I looked at actual data.
2. **Your tileset is your biggest performance lever.** Look at the benchmark table. 25 tiles vs 331 tiles on the same solver, the complex palette is roughly 10x slower. If your WFC is slow, look at your tiles before you look at your code.
3. **NativeArrays aren't optional for Unity.** Switching from managed C# arrays to `NativeArray` for grid state alone cut my generation time significantly, because Burst can't optimize managed heap access. If you're using Burst, everything in the hot path needs to be in unmanaged memory.
4. **Watch your encoding limits.** I encode adjacency rules with a key like `(moduleIdx << 8) | direction`. That uses the lower 8 bits for direction (only need 2 bits for N/S/E/W, but I left room). The catch is that the module index is in the upper bits, and this key is stored in a 32-bit int. In my compatibility table (flat array), there's no problem. But in the hashmap version, this encoding pattern only works cleanly for up to 256 tiles. If you go beyond that, you need wider keys. (Modules/tiles are the individual pieces in the palette that WFC places, in my case isometric sprite tiles.)
5. **Ring buffers over stacks for backtracking.** A managed `Stack<T>` or `List<T>` allocates memory on every push. A pre-allocated `NativeArray` with circular indexing has zero GC pressure and works inside Burst jobs.

# Tech stack

* Unity 6 with Burst Compiler + Job System
* All data in `NativeArray` / `NativeHashMap` (zero garbage collection during generation)
* `IJobFor.ScheduleParallel` for chunk-level parallelism
* 1024-bit bitmasks (32 x int32) for tracking possible tiles per cell
* Multi-pass system: Terrain, then Structures, then Props, each building on locked results from the prior pass


Happy to answer questions or talk details on any of this. Scaling WFC was one of the hardest optimization challenges I've worked through, but also one of the most rewarding.

https://redd.it/1sit4d4
@r_Unity3D

Читать полностью…

r/Unity3D

Wave Function Collapse at 4 million tiles in 23 seconds. Here's every wall I hit scaling it in Unity.

Most WFC implementations top out around 100×100 before things get painfully slow. I spent months pushing mine to handle grids 400x larger, and I want to share what I learned along the way.

[25 Tiles \(Simple\)](https://i.redd.it/nya4wwbi2mug1.gif)

# The numbers

I tested with three different tilesets to see how constraint complexity affects generation time. Same solver, same hardware, same grid sizes:

|Grid Size|Cells|25 Tiles (Simple)|41 Tiles (Medium)|331 Tiles (Complex)|
|:-|:-|:-|:-|:-|
|100×100|10K|0.19s|0.47s|1 - 3s|
|250×250|62.5K|0.50s|0.94s|4 - 10s|
|500×500|250K|1.60s|2.49s|30 - 40s|
|1000×1000|1M|**5.46s**|**7.74s**|**50s - 1m15s**|
|2000×2000|4M|**22.22s**|**29.97s**|**4m 23s**|

The "Simple" palette has 25 tiles with clean, predictable edge connections. The "Complex" one is my actual hand-crafted isometric tileset with 331 tiles, multiple terrain types, biome transitions, and tight directional constraints.

A few things jump out. First, the 25-tile and 331-tile palettes are running on the exact same solver with the exact same optimizations, and the complex one is roughly 10x slower. More tiles means more constraint checks per propagation step, more potential contradictions, and more backtracking. **Your tileset design is the single biggest performance variable.**

Second, the solver gets more efficient as the grid gets larger:

|Grid|Cells|Time|Scaling|
|:-|:-|:-|:-|
|100×100|10,000|0.19s|baseline|
|250×250|62,500|0.50s|6.25x cells, 2.6x time|
|500×500|250,000|1.60s|25x cells, 8.4x time|
|1000×1000|1,000,000|5.46s|100x cells, 28.7x time|
|2000×2000|4,000,000|22.22s|400x cells, 117x time|

At 2000×2000, you'd expect 400x the time of 100×100 if it scaled linearly. Instead it's only 117x. Larger grids have more chunks running in parallel (better CPU utilization), and the fixed startup costs (building adjacency rules, initializing buffers, etc.) get spread across more cells.

**41 Tiles (Medium)**: [Full resolution from this link](https://drive.google.com/file/d/1ye6UmCuRalcfEu6d5WrwNW7CY2ASf_2k/view?usp=sharing)

[41 Tiles \(Medium\)](https://preview.redd.it/hux28cz97mug1.png?width=1490&amp;format=png&amp;auto=webp&amp;s=32d87395e73c7cdd937edad459b1a5cbe34fa834)

**331 Tiles (Complex)**: [Full resolution from this link](https://drive.google.com/file/d/1IKgPMVCoTeIbUdJOeaiGgaz1EzP3I8wy/view?usp=sharing)

[331 Tiles \(Complex\)](https://preview.redd.it/wfq0ih168mug1.png?width=1522&amp;format=png&amp;auto=webp&amp;s=2c1ae0a43ea83e6ef1ee207d7a0bbe2693bc9ace)

# The walls I hit (and what actually fixed them)

There are fast WFC implementations out there (fast-wfc in C++ is great for moderate grids, Tessera has a solid AC-4 solver, etc.). The problem isn't that WFC is slow at small scale. The problem is that it **falls apart at large scale** due to bottlenecks that don't show up until you're past \~200×200. Here's what I ran into:

# Wall #1: "One cell per frame"

Standard WFC works like this:

1. Find lowest entropy cell
2. Collapse it
3. Propagate constraints
4. **Return to main thread**
5. Repeat

For a 1,000,000 cell grid, that's 1 million round-trips between the solver and the main thread. Each one has overhead: scheduling, memory synchronization, state copying.

**What fixed it: Multi-step execution.** Instead of collapsing 1 cell and returning, I collapse **50 cells per job dispatch** inside a single Burst-compiled function. But the tricky part is that you also need **in-job backtracking** for this to work. If you collapse 50 cells and hit a contradiction at cell #30, you need to undo and retry *without leaving the job*. I use a ring-buffer snapshot system for this: save a lightweight snapshot before each collapse, and roll back to it if things go wrong.

# Wall #2: "The grid is one big problem"

WFC seems inherently sequential: collapse a cell, propagate, pick the next. But it doesn't have to be.

**What fixed it: Chunk parallelism.** I divide the grid into chunks and process each one independently on a separate CPU core using

Читать полностью…
Subscribe to a channel