An editor tool I designed and built for scene-anchored design feedback — sticky notes placed directly in a level, with a Kanban board, and two-way sync to Trello and Jira that actually keeps both sides current. Built first for Unity, then rebuilt natively for Unreal Engine 5.7 as a C++ editor plugin.
Scene-anchored design notes with a List and Kanban board
Two-way Trello & Jira integrations
Unity (C#) and Unreal 5.7 (C++)
Solo: design and implementation
An editor tool for scene-anchored design feedback - bugs, art notes, TODOs pinned to the exact spot in a level they're about, browsable as a filterable list or a full drag-and-drop Kanban board, with optional two-way sync to Trello and Jira that goes both directions for real. Available for Unity, with a native C++ port for Unreal Engine 5.7.
Typing in a note's text field was severely laggy for a reason that had nothing to do with typing - the Inspector's "last touched" Git info was spawning a fresh git log subprocess and synchronously blocking the main thread on every single repaint, and IMGUI repaints repeatedly while a text field has focus. I fixed it by caching the result per scene for 30 seconds, which eliminated the per-keystroke process spawn entirely.
I initially mapped a note's Category to Jira's issue Type (Bug/Story/Task), copying the same shape as Trello's Category-to-List mapping without checking whether that was actually the right analog. It wasn't - real testing against a live Jira project showed Kanban columns never matched what a Jira user actually saw on their own board, because the thing that behaves like a column is Status, not Type. Correcting it meant integrating with Jira's separate Transitions API, since status changes aren't a normal field update there.
Pushing a note with no due date or assignee to Jira was failing with confusing validation errors. Unity's JsonUtility doesn't reliably omit null fields when serializing a class - a null string becomes an empty string rather than disappearing - so an unset field was being sent as an actual (invalid) value instead of being left out. The fix was building the request body manually rather than trusting JsonUtility with optional fields.
Several confirmation dialogs were being shown synchronously from inside an open layout group mid-repaint, which is a real source of intermittent "Mismatched LayoutGroup" errors in Unity's Editor GUI system. I deferred those dialogs to run after the current layout pass completes instead, rather than mid-frame.
JsonUtility diverges from a "real" JSON serializer, and when to build a request body by hand instead of trusting it.I built Design Notes around one core idea: a note should be a real object in the scene, not an entry in a separate list disconnected from the level it's about. Everything else — the Kanban board, the integrations, the sync logic — is built on top of that one decision. Then I rebuilt it from scratch for Unreal, where the same idea had to be expressed through completely different engine systems.
A note is a MonoBehaviour on a real GameObject, not an entry in some
external database. It's saved with the scene, visible to the whole team the moment
they pull it, and shows up as a custom-rendered world-space sticky note via
SceneView callbacks — billboarded toward the camera, colour-coded by
Category and Priority, with a small minimap for navigating large scenes. I built a
plain-label fallback style too, since not everyone wants the sticky-note skin.
Trello and Jira aren't hardcoded into the core tool — they implement a small
IDesignNoteIntegration interface, discovered automatically by scanning
every loaded assembly for implementors via AppDomain.CurrentDomain.GetAssemblies().
A third-party integration someone writes themselves needs zero registration anywhere
in the core package - it just shows up. I cached the discovery after the first scan,
since reflecting across every assembly is expensive enough that doing it on every
Editor repaint would have been its own performance problem.
Editing a linked note's text, category, or due date pushes the change back to Trello or Jira automatically — but debounced roughly 1.5 seconds after you stop typing, so it's one request per pause in editing rather than one per keystroke. On the Jira side, a note's Category maps to the issue's Status (To Do / In Progress / Done) rather than its Type, since Status is what actually behaves like a Kanban column - and changing it needs a separate call to Jira's Transitions endpoint rather than a normal field update, since only certain transitions are valid from a given current status.
Adding two-way comment sync introduced a real regression I had to diagnose after the
fact: checking every linked note for new comments made a blocking HTTP call per note,
all inside the same EditorApplication.update tick the poll timer fires
on - so the Editor visibly froze, scaling with however many notes were linked. I fixed
it with a rotating cursor that checks only a small, fixed batch of notes per poll tick
instead of all of them at once, picking up where it left off next time. Every note
still gets checked eventually, just spread across several poll cycles instead of one
synchronous burst.
Unreal has no equivalent of Unity's SceneView GUI callbacks, so pins live
in a custom editor mode, FDesignNotePinEdMode. Clicking in the viewport
runs a line trace against level geometry and places a pin where it hits. Each pin is
drawn with FDynamicMeshBuilder as a category-coloured quad with priority
and due-date corner flags and a HUD text label. Getting the colours to show at all took
some digging: GEngine->EmissiveMeshMaterial silently ignores vertex
colours, so every pin rendered identically until I switched to the engine's
WidgetVertexColorMaterial.
In Unity I needed a custom build-processing pass to strip notes out of player builds.
In Unreal, ADesignNotePin sets bIsEditorOnlyActor, so the
engine's own cooking pipeline drops pins automatically and there's nothing extra to
maintain. The shared rosters (Categories, Priorities, Tags, Team Members) are
UDataAssets, and I split integration config in two:
UDesignNoteIntegrationSettings holds what the team commits to source
control, while UDesignNoteIntegrationCredentials keeps each user's API
tokens local, so secrets never end up in the repo.
The full two-way loop — push on create, card updates, comment pulls, auto-import —
runs on a small FDesignNoteHttp helper that pumps
FHttpManager::Tick to complete requests in sequence. Debounced edit sync
hooks PostEditChangeProperty on the pin, which fires a static
OnPinPropertyChanged delegate guarded behind WITH_EDITOR;
background polling runs on FTSTicker. Click-to-jump links in Trello card
descriptions are served by a local server built on FHttpServerModule
and IHttpRouter, which focuses the pin in the editor when opened.
Unreal's default Details panel just lists properties, which is a poor way to read a
comment thread. FDesignNotePinDetailsCustomization implements
IDetailCustomization to replace it entirely with
SDesignNotePinInspector, a Slate widget covering every field plus
threaded comments, due-date quick-set buttons, viewport screenshot capture, the
level's Git "last touched" info, and Trello link and sync controls — the same
workflow as the Unity Inspector, built on Unreal's own UI framework.