Skip to content

Commit ee74280

Browse files
docs(plugin): enhance skills with game patterns and troubleshooting (#604)
* docs(plugin): enhance skills with game patterns and troubleshooting - Add Properties section and all state overloads to JAction skill - Add async game patterns (cooldown, DoT, wave spawner, health regen) - Add troubleshooting sections to JAction, JObjectPool, and editor-ui - Add zero-GC game patterns to JObjectPool (bullet pool, enemy spawner) - Add game dev examples to editor-ui (settings panel, build tool, asset browser) - Create new game-patterns skill with comprehensive zero-GC async patterns - Register new skill in plugin.json Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> * fix(plugin): address code review feedback on skill documentation - Add null-conditional operator for TakeDamage in DoT pattern - Change ReadOnlySpan to array in async wave spawner (ref structs invalid) - Add Reset() methods to BulletLifetimeState and TypewriterState - Add Cleanup/Dispose methods to ComboSystem, RegenState, TooltipTrigger - Fix TypewriterState to use static lambda with state parameter - Ensure all JActions are properly disposed in long-running patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> * fix(plugin): remove inaccurate comment about closure in typewriter pattern The code was already fixed to use static lambda with state parameter, so the comment suggesting it uses a closure was incorrect. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> * docs(plugin): clarify ReadOnlySpan vs array usage in async/sync methods - Add sync version of wave spawner using ReadOnlySpan<T> with ref readonly - Clarify that async methods cannot use ReadOnlySpan (ref struct limitation) - Note ReadOnlyMemory<T> as alternative for async with .Span access Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> --------- Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent aa49bc3 commit ee74280

File tree

5 files changed

+955
-9
lines changed

5 files changed

+955
-9
lines changed

.claude-plugin/plugin.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"./.claude-plugin/skills/jaction",
2424
"./.claude-plugin/skills/jobjectpool",
2525
"./.claude-plugin/skills/messagebox",
26-
"./.claude-plugin/skills/editor-ui"
26+
"./.claude-plugin/skills/editor-ui",
27+
"./.claude-plugin/skills/game-patterns"
2728
]
2829
}

.claude-plugin/skills/editor-ui/SKILL.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,134 @@ enum JustifyContent { Start, Center, End, SpaceBetween }
363363
enum AlignItems { Start, Center, End, Stretch }
364364
```
365365

366+
## Game Development Examples
367+
368+
### Settings Panel
369+
```csharp
370+
public class GameSettingsWindow : EditorWindow
371+
{
372+
private JToggle _vsyncToggle;
373+
private JDropdown<int> _fpsDropdown;
374+
private JProgressBar _volumeSlider;
375+
376+
[MenuItem("Game/Settings")]
377+
public static void ShowWindow() => GetWindow<GameSettingsWindow>("Game Settings");
378+
379+
public void CreateGUI()
380+
{
381+
var root = new JStack(GapSize.MD);
382+
root.style.paddingTop = Tokens.Spacing.Lg;
383+
root.style.paddingRight = Tokens.Spacing.Lg;
384+
root.style.paddingBottom = Tokens.Spacing.Lg;
385+
root.style.paddingLeft = Tokens.Spacing.Lg;
386+
387+
// Graphics Section
388+
var graphics = new JSection("Graphics")
389+
.Add(
390+
new JFormField("VSync", _vsyncToggle = new JToggle(true)),
391+
new JFormField("Target FPS", _fpsDropdown = new JDropdown<int>(
392+
new() { 30, 60, 120, -1 },
393+
defaultValue: 60,
394+
formatSelectedValue: static fps => fps == -1 ? "Unlimited" : $"{fps} FPS",
395+
formatListItem: static fps => fps == -1 ? "Unlimited" : $"{fps} FPS")));
396+
397+
// Audio Section
398+
var audio = new JSection("Audio")
399+
.Add(new JFormField("Master Volume", _volumeSlider = new JProgressBar(0.8f)
400+
.WithHeight(20)));
401+
402+
// Actions
403+
var actions = new JButtonGroup(
404+
new JButton("Apply", ApplySettings, ButtonVariant.Primary),
405+
new JButton("Reset", ResetSettings, ButtonVariant.Secondary));
406+
407+
root.Add(graphics, audio, actions);
408+
rootVisualElement.Add(root);
409+
}
410+
}
411+
```
412+
413+
### Build Tool Window
414+
```csharp
415+
public class BuildToolWindow : EditorWindow
416+
{
417+
private JLogView _logView;
418+
private JProgressBar _progress;
419+
private JStatusBar _status;
420+
421+
public void CreateGUI()
422+
{
423+
var root = new JStack(GapSize.MD);
424+
425+
// Build Configuration
426+
var config = new JSection("Build Configuration")
427+
.Add(
428+
new JFormField("Platform", JDropdown<BuildTarget>.ForEnum(BuildTarget.StandaloneWindows64)),
429+
new JFormField("Development", new JToggle(false)),
430+
new JFormField("Output", new JTextField("", "Select output folder...")));
431+
432+
// Progress Section
433+
var progressSection = new JSection("Progress")
434+
.Add(
435+
_progress = new JProgressBar(0f).WithSuccessOnComplete(),
436+
_status = new JStatusBar("Ready", StatusType.Info));
437+
438+
// Log Output
439+
_logView = new JLogView(200).WithMinHeight(200).WithMaxHeight(400);
440+
441+
// Actions
442+
var actions = new JButtonGroup(
443+
new JButton("Build", StartBuild, ButtonVariant.Primary),
444+
new JButton("Clean", CleanBuild, ButtonVariant.Warning),
445+
new JButton("Cancel", CancelBuild, ButtonVariant.Danger));
446+
447+
root.Add(config, progressSection, _logView, actions);
448+
rootVisualElement.Add(root);
449+
}
450+
451+
private void UpdateProgress(float value, string message)
452+
{
453+
_progress.Progress = value;
454+
_status.Text = message;
455+
_logView.LogInfo(message);
456+
}
457+
}
458+
```
459+
460+
### Asset Browser Panel
461+
```csharp
462+
public class AssetBrowserPanel : EditorWindow
463+
{
464+
public void CreateGUI()
465+
{
466+
var root = new JStack(GapSize.MD);
467+
468+
// Navigation
469+
var nav = new JRow()
470+
.Add(
471+
new JIconButton("\u2190", GoBack, "Back"),
472+
new JIconButton("\u2192", GoForward, "Forward"),
473+
new JIconButton("\u2191", GoUp, "Parent"),
474+
JBreadcrumb.FromPath("Assets", "Prefabs", "Characters"))
475+
.WithAlign(AlignItems.Center);
476+
477+
// Toolbar
478+
var toolbar = new JRow()
479+
.Add(
480+
new JTextField("", "Search assets..."),
481+
new JDropdown<string>(new() { "All", "Prefabs", "Materials", "Textures" }),
482+
new JToggleButton("Grid", "List", true))
483+
.WithJustify(JustifyContent.SpaceBetween);
484+
485+
// Status
486+
var status = new JStatusBar("24 items", StatusType.Info);
487+
488+
root.Add(nav, toolbar, status);
489+
rootVisualElement.Add(root);
490+
}
491+
}
492+
```
493+
366494
## Example: Complete Editor Window
367495

368496
```csharp
@@ -409,3 +537,60 @@ public class MyEditorWindow : EditorWindow
409537
}
410538
}
411539
```
540+
541+
## Troubleshooting
542+
543+
### Theme Not Updating
544+
- **Problem:** Colors don't change when switching Unity theme
545+
- **Solution:** Token colors are evaluated at component creation time. Recreate components or use `schedule.Execute()` to refresh on theme change:
546+
```csharp
547+
rootVisualElement.schedule.Execute(() => {
548+
// Recreate or update component colors
549+
myCard.style.backgroundColor = Tokens.Colors.BgSurface;
550+
}).Every(1000);
551+
```
552+
553+
### Binding Not Working
554+
- **Problem:** SerializedProperty binding doesn't update UI
555+
- **Solution:** Ensure the property path is correct and call `Bind()` on the root:
556+
```csharp
557+
var textField = new JTextField();
558+
textField.BindProperty(serializedObject.FindProperty("myField"));
559+
rootVisualElement.Bind(serializedObject);
560+
```
561+
562+
### Component Not Visible
563+
- **Problem:** Added component doesn't appear
564+
- **Check:**
565+
- Parent has `flexGrow = 1` if using flex layout
566+
- Component has non-zero width/height
567+
- Parent visibility is not hidden
568+
569+
### Buttons Not Responding
570+
- **Problem:** Click events not firing
571+
- **Solution:** Ensure callback is not null and component is enabled:
572+
```csharp
573+
var btn = new JButton("Click", () => Debug.Log("Clicked"));
574+
btn.SetEnabled(true); // Ensure enabled
575+
```
576+
577+
### Layout Issues
578+
- **Problem:** Components overlap or have wrong size
579+
- **Solution:** Use JStack for vertical, JRow for horizontal layouts:
580+
```csharp
581+
// Wrong: direct Add to root
582+
rootVisualElement.Add(component1);
583+
rootVisualElement.Add(component2); // May overlap
584+
585+
// Correct: use layout container
586+
var stack = new JStack();
587+
stack.Add(component1, component2);
588+
rootVisualElement.Add(stack);
589+
```
590+
591+
### Performance with Many Components
592+
- **Problem:** Editor window slow with many items
593+
- **Solution:** Use virtualization for large lists, limit JLogView maxLines:
594+
```csharp
595+
var log = new JLogView(maxLines: 100); // Limit entries
596+
```

0 commit comments

Comments
 (0)