Compare commits

...

15 Commits

Author SHA1 Message Date
716f55c138 Add player skin override and update game assets
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Добавлен компонент подмены спрайтов игрока перед началом игры.
- Обновлены сцена уровня, префабы, шейдеры, настройки импорта и данные призов.
- Зафиксирован embedded-пакет Unity MCP 9.6.8 и обновлены связанные файлы проекта.
2026-09-22 13:21:18 +03:00
4550e85e30 Add spawner override diagnostics to PickPrefab
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- В метод PickPrefab класса EntitySpawner добавлено логирование Debug.Log при успешном переопределении префаба зоной.
- Это позволяет легко проверять, какие спавнеры и на каких точках подверглись влиянию зоны переопределения.
2026-08-13 14:20:10 +03:00
ad744301dd Fix spawner zone override targeting and BoxCollider2D bounds
- Добавлено свойство spawnerNameFilter (по умолчанию 'Enemy') в EntitySpawnerZone, чтобы зоны переопределения применялись только к спавнерам врагов и не заменяли префабы в CoinSpawner (спавнерах кристаллов) или других спавнерах предметов.
- Реализована точная математическая проверка IsPointInside для BoxCollider2D, что делает ее независимой от Z-координаты и тиков физического движка Unity.
- Исправлено поведение в EntitySpawner: теперь при поиске зоны проверяется zone.MatchesSpawner.
2026-08-13 14:12:19 +03:00
28f5409187 Implement EntitySpawnerZone override system
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Создан скрипт EntitySpawnerZone.cs для выделения зон замены префабов в Unity Editor.
- Модифицирован EntitySpawner.cs для поддержки зон при выборе префаба на основе координат точки спавна.
2026-08-07 14:39:18 +03:00
3d1772af15 Restore transparent gaps inside cartoon fire
- Обновлены пороги отсечки в InjectToonFireToShaderGraph.cs: внешний порог установлен на 1.0 (соответствует оригинальному альфа-порогу).
- Это восстанавливает прозрачные отверстия/пробелы внутри пламени.
2026-08-06 14:50:21 +03:00
a1c052c879 Fix duplicate edges crash when re-running graph injector
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Обновлен InjectToonFireToShaderGraph.cs: добавлена очистка всех связей кастомной ноды node_cartoon_fire_custom перед повторной сборкой связей.
- Это полностью решает проблему ArgumentException: An item with the same key has already been added при открытии ShaderGraph в Unity.
2026-08-06 14:11:36 +03:00
75b8b75590 Make toon fire borders sharp and dynamic
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Обновлена Toon-функция в InjectToonFireToShaderGraph.cs.
- Убрано финальное сглаживание по texAlpha, что позволило сделать внешнюю границу пламени острой и динамически колыхающейся.
2026-08-06 14:08:08 +03:00
087f122639 Adjust toon fire shadergraph thresholds for proper noise animation
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Скорректированы пороговые значения (thresholds) в Fire_Shader.shadergraph до 0.85, 1.0 и 1.15.
- Это восстанавливает динамическую волнистость и движение пламени, избавляя от статичного вида маски.
2026-08-06 14:06:40 +03:00
afca8cb7e3 Clip cartoon fire by texture alpha mask to fix square bounds
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Обновлен InjectToonFireToShaderGraph.cs: добавлен вход для текстурной альфы.
- Обновлен Fire_Shader.shadergraph: выходная альфа теперь умножается на исходный альфа-канал текстуры (маски пламени).
- Это полностью решает проблему рендеринга огня в виде квадратного блока.
2026-08-06 14:04:54 +03:00
8b41d48b7f Modify original Fire_Shader graph with custom toon function for VFX Graph
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Изменен исходный файл Fire_Shader.shadergraph для поддержки в VFX Graph.
- Внедрена Custom Function нода с кодом трехступенчатого мультяшного закрашивания на основе цвета частиц.
- Удалены временные шейдеры и скрипты.
2026-08-06 14:02:54 +03:00
150f84decd Make fire shader more cartoonish like the reference
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Создан URP HLSL шейдер CartoonFire.shader с процедурным FBM-шумом искажения и 3-ступенчатой постеризацией цветов (красный/оранжевый/желтый).
- Создан редакторский скрипт ApplyCartoonFireShader.cs для быстрого переключения материала.
- Материал Fire_Material.mat обновлен и переведен на новый мультяшный шейдер с настроенными параметрами.
2026-08-06 13:54:29 +03:00
a0211d50fc Replace highlighted platform with lever-activated sign
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Заменена уменьшенная платформа TriggeringPlatform_2 на сцене 1-1 на табличку со стрелкой влево (Signs_Arrow_Left).
- Создан C# скрипт ShowObjectWithVFX.cs для активации скрытых объектов со спецэффектами частиц (sparks).
- Добавлен вспомогательный редакторский скрипт ReplacePlatformWithSign.cs для автоматической перестройки сцены.
- Рычаг на уровне 1-1 привязан к активации таблички: табличка изначально скрыта, при переключении рычага появляется с эффектом искр.
2026-08-05 16:42:23 +03:00
77f7f9ef39 Use prize icons in rules page and align them vertically (with scale and position alignment)
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Добавлен Editor-скрипт SpriteAssetGenerator для автоматической генерации TMP Sprite Asset'ов из текстур призов.
- Сгенерированы TMP Sprite Asset'ы (Prize, Prize_Hopper, Prize_Marbles, Prize_QR, Prize_Tickets) в папке Assets/Resources/Sprite Assets.
- Настроены FaceInfo и вертикальное центрирование спрайтов относительно текста.
- Обновлен RulesPrizeListPage.cs: вместо текстовых названий призов выводятся соответствующие пиктограммы с помощью тегов <sprite>.
- Добавлены новые файлы текстур призов и их мета-файлы в коммит.
2026-08-03 16:42:43 +03:00
c5b9e8e046
Update AGENTS.md rules and remove .agents folder
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Правило для ИИ-агентов по скрытию логов терминала перенесено в корневой файл AGENTS.md.
- Удалена папка .agents
2026-07-17 10:34:47 +03:00
5092f7c107
Add .agents/AGENTS.md rule file
- Создано руководство для ИИ-агентов по сокрытию логов терминала и перенаправлению вывода команд в пустоту
2026-07-17 10:33:06 +03:00
628 changed files with 87387 additions and 1691 deletions

View File

@ -34,3 +34,12 @@
- Для запуска EditMode/PlayMode-проверок следует использовать `run_tests` и `get_test_job`, если тестовая группа `unityMCP` доступна и это уместно для задачи.
- Если нужная группа возможностей `unityMCP` отключена, агент может включить её через `manage_tools`, когда это оправдано задачей.
- Если `unityMCP` недоступен, не видит активный Unity Editor, возвращает ошибку или хуже подходит для конкретной операции, агент может использовать файловые инструменты и обычные shell-команды как запасной путь, явно учитывая риск отсутствия проверки внутри Unity.
## Git Console Output and Chat formatting
- Никогда не выводите сырой вывод консоли или логов терминала (особенно логи Git) в чат с пользователем.
- При выполнении фоновых или синхронных Git-команд перенаправляйте вывод потоков вывода и ошибок в пустоту (заглушайте вывод), чтобы избежать захламления чата системными логами и дублирования строк:
- В PowerShell используйте перенаправление в пустоту: `*>$null`
- В CMD используйте перенаправление в пустоту: `>nul 2>&1`
- Описывайте результаты работы Git понятным человеческим языком на русском языке, перечисляя только суть изменений и конечные статусы, без копирования отчетов терминала.

8
Assets/Editor.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 82f0a4f87da69ea4f86a4841fa5bf39d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,365 @@
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static class InjectToonFireToShaderGraph
{
[MenuItem("Tools/Inject Toon Fire to Shader Graph")]
public static void Inject()
{
string path = "Assets/Shaders/Fire_Shader.shadergraph";
if (!File.Exists(path))
{
Debug.LogError($"ShaderGraph not found at {path}");
return;
}
string jsonText = File.ReadAllText(path);
List<JObject> jObjects = new List<JObject>();
// Parse Multi-JSON using SupportMultipleContent = true
using (var stringReader = new StringReader(jsonText))
using (var jsonReader = new JsonTextReader(stringReader))
{
jsonReader.SupportMultipleContent = true;
while (jsonReader.Read())
{
if (jsonReader.TokenType == JsonToken.StartObject)
{
JObject obj = JObject.Load(jsonReader);
jObjects.Add(obj);
}
}
}
// Find the main GraphData object (usually the first one, but let's be safe)
JObject graphData = null;
foreach (var obj in jObjects)
{
if ((string)obj["m_Type"] == "UnityEditor.ShaderGraph.GraphData")
{
graphData = obj;
break;
}
}
if (graphData == null)
{
Debug.LogError("Could not find GraphData object in ShaderGraph file!");
return;
}
// 1. Remove connections to BaseColor and Alpha block nodes in m_Edges
// BaseColor block id: "5795a95f005b46bbbbf4c4e22a0ae9c3"
// Alpha block id: "6c0a4ae63e3c48ad9e08ffbc4cf86bba"
JArray edges = (JArray)graphData["m_Edges"];
List<JToken> edgesToRemove = new List<JToken>();
foreach (var edge in edges)
{
string inputNodeId = (string)edge["m_InputSlot"]["m_Node"]["m_Id"];
string outputNodeId = (string)edge["m_OutputSlot"]["m_Node"]["m_Id"];
if (inputNodeId == "5795a95f005b46bbbbf4c4e22a0ae9c3" ||
inputNodeId == "6c0a4ae63e3c48ad9e08ffbc4cf86bba" ||
inputNodeId == "node_cartoon_fire_custom" ||
outputNodeId == "node_cartoon_fire_custom")
{
edgesToRemove.Add(edge);
}
}
foreach (var edge in edgesToRemove)
{
edges.Remove(edge);
}
// 2. Define our Custom Function node and Slot JObjects
JObject slotInRawValue = JObject.Parse(@"{
""m_SGVersion"": 0,
""m_Type"": ""UnityEditor.ShaderGraph.Vector1MaterialSlot"",
""m_ObjectId"": ""slot_in_raw_value"",
""m_Id"": 0,
""m_DisplayName"": ""rawValue"",
""m_SlotType"": 0,
""m_Hidden"": false,
""m_ShaderOutputName"": ""rawValue"",
""m_StageCapability"": 3,
""m_Value"": 0.0,
""m_DefaultValue"": 0.0,
""m_Labels"": []
}");
JObject slotInBaseColor = JObject.Parse(@"{
""m_SGVersion"": 0,
""m_Type"": ""UnityEditor.ShaderGraph.Vector4MaterialSlot"",
""m_ObjectId"": ""slot_in_base_color"",
""m_Id"": 1,
""m_DisplayName"": ""baseColor"",
""m_SlotType"": 0,
""m_Hidden"": false,
""m_ShaderOutputName"": ""baseColor"",
""m_StageCapability"": 3,
""m_Value"": {
""x"": 1.0,
""y"": 1.0,
""z"": 1.0,
""w"": 1.0
},
""m_DefaultValue"": {
""x"": 0.0,
""y"": 0.0,
""z"": 0.0,
""w"": 0.0
},
""m_Labels"": []
}");
JObject slotOutColor = JObject.Parse(@"{
""m_SGVersion"": 0,
""m_Type"": ""UnityEditor.ShaderGraph.Vector3MaterialSlot"",
""m_ObjectId"": ""slot_out_color"",
""m_Id"": 2,
""m_DisplayName"": ""outColor"",
""m_SlotType"": 1,
""m_Hidden"": false,
""m_ShaderOutputName"": ""outColor"",
""m_StageCapability"": 3,
""m_Value"": {
""x"": 0.0,
""y"": 0.0,
""z"": 0.0
},
""m_DefaultValue"": {
""x"": 0.0,
""y"": 0.0,
""z"": 0.0
},
""m_Labels"": []
}");
JObject slotOutAlpha = JObject.Parse(@"{
""m_SGVersion"": 0,
""m_Type"": ""UnityEditor.ShaderGraph.Vector1MaterialSlot"",
""m_ObjectId"": ""slot_out_alpha"",
""m_Id"": 3,
""m_DisplayName"": ""outAlpha"",
""m_SlotType"": 1,
""m_Hidden"": false,
""m_ShaderOutputName"": ""outAlpha"",
""m_StageCapability"": 3,
""m_Value"": 0.0,
""m_DefaultValue"": 0.0,
""m_Labels"": []
}");
JObject slotInTexAlpha = JObject.Parse(@"{
""m_SGVersion"": 0,
""m_Type"": ""UnityEditor.ShaderGraph.Vector1MaterialSlot"",
""m_ObjectId"": ""slot_in_tex_alpha"",
""m_Id"": 4,
""m_DisplayName"": ""texAlpha"",
""m_SlotType"": 0,
""m_Hidden"": false,
""m_ShaderOutputName"": ""texAlpha"",
""m_StageCapability"": 3,
""m_Value"": 1.0,
""m_DefaultValue"": 1.0,
""m_Labels"": []
}");
JObject customFunctionNode = JObject.Parse(@"{
""m_SGVersion"": 1,
""m_Type"": ""UnityEditor.ShaderGraph.CustomFunctionNode"",
""m_ObjectId"": ""node_cartoon_fire_custom"",
""m_Group"": {
""m_Id"": """"
},
""m_Name"": ""ToonFireColor (Custom Function)"",
""m_DrawState"": {
""m_Expanded"": true,
""m_Position"": {
""serializedVersion"": ""2"",
""x"": -400.0,
""y"": 56.0,
""width"": 220.0,
""height"": 180.0
}
},
""m_Slots"": [
{ ""m_Id"": ""slot_in_raw_value"" },
{ ""m_Id"": ""slot_in_base_color"" },
{ ""m_Id"": ""slot_out_color"" },
{ ""m_Id"": ""slot_out_alpha"" },
{ ""m_Id"": ""slot_in_tex_alpha"" }
],
""synonyms"": [],
""m_Precision"": 0,
""m_PreviewExpanded"": false,
""m_DismissedVersion"": 0,
""m_PreviewMode"": 0,
""m_CustomColors"": {
""m_SerializableColors"": []
},
""m_SourceType"": 1,
""m_FunctionName"": ""ToonFireColor"",
""m_FunctionSource"": """",
""m_FunctionBody"": ""float3 colInner = float3(1.0, 0.95, 0.4) * baseColor.rgb * 2.2;\nfloat3 colMid = float3(1.0, 0.55, 0.0) * baseColor.rgb * 1.8;\nfloat3 colOuter = float3(1.0, 0.15, 0.1) * baseColor.rgb * 1.4;\n\nfloat stepOuter = step(1.0, rawValue);\nfloat stepMid = step(1.12, rawValue);\nfloat stepInner = step(1.25, rawValue);\n\nfloat3 finalColor = float3(0,0,0);\nif (stepInner > 0.5)\n{\n finalColor = colInner;\n}\nelse if (stepMid > 0.5)\n{\n finalColor = colMid;\n}\nelse if (stepOuter > 0.5)\n{\n finalColor = colOuter;\n}\n\noutColor = finalColor;\noutAlpha = stepOuter * baseColor.a;""
}");
// Remove previous instances of our nodes from list (if re-running script)
jObjects.RemoveAll(x => (string)x["m_ObjectId"] == "slot_in_raw_value");
jObjects.RemoveAll(x => (string)x["m_ObjectId"] == "slot_in_base_color");
jObjects.RemoveAll(x => (string)x["m_ObjectId"] == "slot_out_color");
jObjects.RemoveAll(x => (string)x["m_ObjectId"] == "slot_out_alpha");
jObjects.RemoveAll(x => (string)x["m_ObjectId"] == "slot_in_tex_alpha");
jObjects.RemoveAll(x => (string)x["m_ObjectId"] == "node_cartoon_fire_custom");
// Add them to the list of objects in the file
jObjects.Add(slotInRawValue);
jObjects.Add(slotInBaseColor);
jObjects.Add(slotOutColor);
jObjects.Add(slotOutAlpha);
jObjects.Add(slotInTexAlpha);
jObjects.Add(customFunctionNode);
// Add the node reference ID to "m_Nodes" array in GraphData
JArray nodes = (JArray)graphData["m_Nodes"];
bool hasNode = false;
foreach (var node in nodes)
{
if ((string)node["m_Id"] == "node_cartoon_fire_custom")
{
hasNode = true;
break;
}
}
if (!hasNode)
{
JObject nodeRef = new JObject();
nodeRef["m_Id"] = "node_cartoon_fire_custom";
nodes.Add(nodeRef);
}
// 3. Connect the new edges in GraphData
// Edge 1: raw fire shape (AddNode 0d74e8c23ef1428eaf8c38a4c033c563 output slot 2) -> slot_in_raw_value (CustomFunctionNode input slot 0)
JObject edge1 = JObject.Parse(@"{
""m_OutputSlot"": {
""m_Node"": {
""m_Id"": ""0d74e8c23ef1428eaf8c38a4c033c563""
},
""m_SlotId"": 2
},
""m_InputSlot"": {
""m_Node"": {
""m_Id"": ""node_cartoon_fire_custom""
},
""m_SlotId"": 0
}
}");
// Edge 2: base color representation (MultiplyNode e48170ea86fb4291b3e4198160139b3e output slot 2) -> slot_in_base_color (CustomFunctionNode input slot 1)
JObject edge2 = JObject.Parse(@"{
""m_OutputSlot"": {
""m_Node"": {
""m_Id"": ""e48170ea86fb4291b3e4198160139b3e""
},
""m_SlotId"": 2
},
""m_InputSlot"": {
""m_Node"": {
""m_Id"": ""node_cartoon_fire_custom""
},
""m_SlotId"": 1
}
}");
// Edge 3: CustomFunctionNode outColor (output slot 2) -> BaseColor block node 5795a95f005b46bbbbf4c4e22a0ae9c3 (slot 0)
JObject edge3 = JObject.Parse(@"{
""m_OutputSlot"": {
""m_Node"": {
""m_Id"": ""node_cartoon_fire_custom""
},
""m_SlotId"": 2
},
""m_InputSlot"": {
""m_Node"": {
""m_Id"": ""5795a95f005b46bbbbf4c4e22a0ae9c3""
},
""m_SlotId"": 0
}
}");
// Edge 4: CustomFunctionNode outAlpha (output slot 3) -> Alpha block node 6c0a4ae63e3c48ad9e08ffbc4cf86bba (slot 0)
JObject edge4 = JObject.Parse(@"{
""m_OutputSlot"": {
""m_Node"": {
""m_Id"": ""node_cartoon_fire_custom""
},
""m_SlotId"": 3
},
""m_InputSlot"": {
""m_Node"": {
""m_Id"": ""6c0a4ae63e3c48ad9e08ffbc4cf86bba""
},
""m_SlotId"": 0
}
}");
// Edge 5: Sample Texture 2D node (3f4b2124cb794f8e970aacc0488fc224 output slot 4 (alpha)) -> slot_in_tex_alpha (CustomFunctionNode input slot 4)
JObject edge5 = JObject.Parse(@"{
""m_OutputSlot"": {
""m_Node"": {
""m_Id"": ""3f4b2124cb794f8e970aacc0488fc224""
},
""m_SlotId"": 4
},
""m_InputSlot"": {
""m_Node"": {
""m_Id"": ""node_cartoon_fire_custom""
},
""m_SlotId"": 4
}
}");
edges.Add(edge1);
edges.Add(edge2);
edges.Add(edge3);
edges.Add(edge4);
edges.Add(edge5);
// Write all JObjects back to file separated by two newlines
StringBuilder sb = new StringBuilder();
for (int i = 0; i < jObjects.Count; i++)
{
sb.Append(jObjects[i].ToString(Newtonsoft.Json.Formatting.Indented));
if (i < jObjects.Count - 1)
{
sb.Append("\n\n");
}
}
File.WriteAllText(path, sb.ToString());
// Restore material shader to original Fire_Shader.shadergraph
string materialPath = "Assets/Materials/Fire_Material.mat";
Material mat = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
if (mat != null)
{
Shader origShader = Shader.Find("Shader Graphs/Fire_Shader");
if (origShader != null)
{
Undo.RecordObject(mat, "Restore original Fire ShaderGraph");
mat.shader = origShader;
EditorUtility.SetDirty(mat);
}
}
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
AssetDatabase.SaveAssets();
Debug.Log("Successfully injected Toon Fire Custom Function with Texture Alpha Mask into Fire_Shader.shadergraph!");
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 99fc5f3bc18a15f40b96011883199d7d

View File

@ -0,0 +1,174 @@
using UnityEngine;
using UnityEngine.VFX;
using UnityEditor;
using UnityEditor.Events;
using RR.Game2D.Entities;
public static class ReplacePlatformWithSign
{
[MenuItem("Tools/Replace Platform with Sign")]
public static string Run()
{
GameObject platformObj = GameObject.Find("Level/Props/Platforms/TriggeringPlatform_2");
if (platformObj == null)
{
platformObj = GameObject.Find("TriggeringPlatform_2");
}
if (platformObj == null)
{
Debug.LogError("Error: TriggeringPlatform_2 not found in scene!");
return "Error: TriggeringPlatform_2 not found in scene!";
}
GameObject leverObj = GameObject.Find("Level/Props/Lever");
if (leverObj == null)
{
leverObj = GameObject.Find("Lever");
}
if (leverObj == null)
{
Debug.LogError("Error: Lever not found in scene!");
return "Error: Lever not found in scene!";
}
Transform parentTransform = platformObj.transform.parent;
Vector3 position = platformObj.transform.position;
Sprite signSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Resources/Sprites/Props/Signs/Signs_Arrow_Left.png");
if (signSprite == null)
{
// Try fallback OLD folder
signSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Resources/Sprites/Props/Signs/OLD/Signs_Arrow_Left.png");
}
if (signSprite == null)
{
Debug.LogError("Error: Signs_Arrow_Left.png not found in Assets!");
return "Error: Signs_Arrow_Left.png not found in Assets!";
}
// Create sign GameObject
GameObject signObj = new GameObject("SignLeft");
Undo.RegisterCreatedObjectUndo(signObj, "Create SignLeft");
signObj.transform.parent = parentTransform;
signObj.transform.position = position;
signObj.transform.localScale = Vector3.one;
// Add SpriteRenderer
SpriteRenderer sr = signObj.AddComponent<SpriteRenderer>();
sr.sprite = signSprite;
// Copy sorting layer and order from sibling or default
SpriteRenderer[] allRenderers = parentTransform.parent.GetComponentsInChildren<SpriteRenderer>(true);
if (allRenderers != null && allRenderers.Length > 0)
{
sr.sortingLayerID = allRenderers[0].sortingLayerID;
sr.sortingLayerName = allRenderers[0].sortingLayerName;
sr.sortingOrder = allRenderers[0].sortingOrder;
}
else
{
sr.sortingLayerName = "Foreground";
sr.sortingOrder = 0;
}
// Add ShowObjectWithVFX component
ShowObjectWithVFX triggerScript = signObj.AddComponent<ShowObjectWithVFX>();
// Use reflection to set private serialized fields
var targetField = typeof(ShowObjectWithVFX).GetField("targetObject", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (targetField != null)
{
targetField.SetValue(triggerScript, signObj);
}
// Create Sparks ParticleSystem
GameObject sparksObj = new GameObject("Sparks");
sparksObj.transform.parent = signObj.transform;
sparksObj.transform.localPosition = Vector3.zero;
ParticleSystem ps = sparksObj.AddComponent<ParticleSystem>();
// Configure particles to look like sparks
var main = ps.main;
main.duration = 1.0f;
main.loop = false;
main.startLifetime = new ParticleSystem.MinMaxCurve(0.4f, 0.8f);
main.startSpeed = new ParticleSystem.MinMaxCurve(3.0f, 6.0f);
main.startSize = new ParticleSystem.MinMaxCurve(0.1f, 0.25f);
main.gravityModifier = 0.9f;
main.playOnAwake = false;
var emission = ps.emission;
emission.enabled = true;
emission.rateOverTime = 0;
var burst = new ParticleSystem.Burst(0.0f, 40);
emission.SetBursts(new ParticleSystem.Burst[] { burst });
var shape = ps.shape;
shape.enabled = true;
shape.shapeType = ParticleSystemShapeType.Cone;
shape.angle = 60f;
shape.radius = 0.3f;
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = true;
Gradient gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] { new GradientColorKey(new Color(1f, 0.9f, 0.3f), 0.0f), new GradientColorKey(new Color(1f, 0.3f, 0.0f), 1.0f) },
new GradientAlphaKey[] { new GradientAlphaKey(1.0f, 0.0f), new GradientAlphaKey(0.0f, 1.0f) }
);
colorOverLifetime.color = gradient;
var psr = sparksObj.GetComponent<ParticleSystemRenderer>();
if (psr != null)
{
psr.material = AssetDatabase.GetBuiltinExtraResource<Material>("Default-Particle.mat");
}
// Assign particle system to triggerScript
var psField = typeof(ShowObjectWithVFX).GetField("particleSystemComponent", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (psField != null)
{
psField.SetValue(triggerScript, ps);
}
// Set up Lever activation
Lever lever = leverObj.GetComponent<Lever>();
if (lever != null)
{
var onActivateField = typeof(Lever).GetField("onActivate", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
if (onActivateField != null)
{
UnityEngine.Events.UnityEvent onActivate = (UnityEngine.Events.UnityEvent)onActivateField.GetValue(lever);
if (onActivate != null)
{
// Clear any old triggers pointing to platform
int persistentEventCount = onActivate.GetPersistentEventCount();
for (int i = persistentEventCount - 1; i >= 0; i--)
{
Object target = onActivate.GetPersistentTarget(i);
if (target == platformObj || (target is Component comp && comp.gameObject == platformObj))
{
UnityEventTools.RemovePersistentListener(onActivate, i);
}
}
// Bind new activation
UnityEventTools.AddPersistentListener(onActivate, triggerScript.Activate);
EditorUtility.SetDirty(lever);
}
}
}
// Destroy old platform object
Undo.DestroyObjectImmediate(platformObj);
// Mark scene dirty and save
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(signObj.scene);
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(signObj.scene);
Debug.Log("Success! TriggeringPlatform_2 replaced with SignLeft, particle system configured, and Lever.onActivate event updated.");
return "Success!";
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 664333a25198dc74bb19121701092ce5

View File

@ -0,0 +1,162 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.TextCore;
using TMPro;
public static class SpriteAssetGenerator
{
[MenuItem("Tools/Generate Prize Sprite Assets")]
public static void Generate()
{
string sourceDir = "Assets/Resources/Sprites/UI/Labels";
string destDir = "Assets/Resources/Sprite Assets";
if (!Directory.Exists(destDir))
{
Directory.CreateDirectory(destDir);
AssetDatabase.Refresh();
}
if (!Directory.Exists(sourceDir))
{
Debug.LogError($"[SpriteAssetGenerator] Source directory not found: {sourceDir}");
return;
}
string[] pngFiles = Directory.GetFiles(sourceDir, "*.png");
if (pngFiles.Length == 0)
{
Debug.LogWarning($"[SpriteAssetGenerator] No PNG files found in {sourceDir}");
return;
}
foreach (string pngPath in pngFiles)
{
try
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(pngPath);
string assetPath = $"{destDir}/{fileNameWithoutExtension}.asset";
Texture2D sourceTex = AssetDatabase.LoadAssetAtPath<Texture2D>(pngPath);
if (sourceTex == null)
{
Debug.LogWarning($"[SpriteAssetGenerator] Failed to load Texture2D at: {pngPath}");
continue;
}
// Create new Sprite Asset
TMP_SpriteAsset spriteAsset = ScriptableObject.CreateInstance<TMP_SpriteAsset>();
spriteAsset.name = fileNameWithoutExtension;
spriteAsset.spriteInfoList = new List<TMP_Sprite>();
// Set version to 1.1.0 to avoid UpgradeSpriteAsset trigger in Awake/UpdateLookupTables
var versionField = typeof(TMP_Asset).GetField("m_Version", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (versionField != null)
{
versionField.SetValue(spriteAsset, "1.1.0");
}
spriteAsset.hashCode = TMP_TextUtilities.GetSimpleHashCode(fileNameWithoutExtension);
spriteAsset.spriteSheet = sourceTex;
// Populate tables
List<TMP_SpriteGlyph> spriteGlyphTable = new List<TMP_SpriteGlyph>();
List<TMP_SpriteCharacter> spriteCharacterTable = new List<TMP_SpriteCharacter>();
// Get all sprites contained in this texture file
Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(pngPath)
.Select(x => x as Sprite)
.Where(x => x != null)
.OrderByDescending(x => x.rect.y)
.ThenBy(x => x.rect.x)
.ToArray();
if (sprites.Length == 0)
{
Debug.LogWarning($"[SpriteAssetGenerator] No sprites sliced inside texture {pngPath}. Make sure it is sliced as Sprite (Multiple) or Sprite (Single).");
continue;
}
// Configure FaceInfo to scale the sprites properly
float baseHeight = sprites[0].rect.height;
FaceInfo faceInfo = spriteAsset.faceInfo;
faceInfo.pointSize = (int)baseHeight;
faceInfo.scale = 1.0f;
faceInfo.ascentLine = baseHeight;
faceInfo.capLine = baseHeight * 0.7f;
faceInfo.baseline = 0;
faceInfo.descentLine = 0;
spriteAsset.faceInfo = faceInfo;
for (int i = 0; i < sprites.Length; i++)
{
Sprite sprite = sprites[i];
TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
spriteGlyph.index = (uint)i;
float spriteHeight = sprite.rect.height;
// Center the sprite vertically around the cap-height (70% of font size)
float bearingY = spriteHeight * 0.85f;
float bearingX = 0f;
spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, bearingX, bearingY, sprite.rect.width);
spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
spriteGlyph.scale = 1.0f;
spriteGlyph.sprite = sprite;
spriteGlyphTable.Add(spriteGlyph);
TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
spriteCharacter.name = sprite.name;
spriteCharacter.scale = 1.0f;
spriteCharacterTable.Add(spriteCharacter);
}
spriteAsset.spriteCharacterTable.Clear();
spriteAsset.spriteCharacterTable.AddRange(spriteCharacterTable);
spriteAsset.spriteGlyphTable.Clear();
spriteAsset.spriteGlyphTable.AddRange(spriteGlyphTable);
// Add default material
Shader shader = Shader.Find("TextMeshPro/Sprite");
if (shader == null)
{
shader = Shader.Find("UI/Default");
}
if (shader == null)
{
Debug.LogError("[SpriteAssetGenerator] Shader not found!");
continue;
}
Material material = new Material(shader);
material.SetTexture(ShaderUtilities.ID_MainTex, sourceTex);
material.name = fileNameWithoutExtension + " Material";
AssetDatabase.CreateAsset(spriteAsset, assetPath);
AssetDatabase.AddObjectToAsset(material, spriteAsset);
spriteAsset.material = material;
spriteAsset.UpdateLookupTables();
EditorUtility.SetDirty(spriteAsset);
Debug.Log($"[SpriteAssetGenerator] Created Sprite Asset at: {assetPath} with {sprites.Length} sprites.");
}
catch (System.Exception ex)
{
Debug.LogException(ex);
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8a63b29edf14d7140b737239be9b3c05

View File

@ -45,11 +45,20 @@ Material:
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _DistortStrength: 0.08
- _Fade: 0.4
- _FireCellsCount: 5
- _NoiseScale: 15
- _ThresholdInner: 0.58
- _ThresholdMid: 0.38
- _ThresholdOuter: 0.15
m_Colors:
- White: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 2, g: 2, b: 2, a: 1}
- _Speed: {r: 0, g: -0.5, b: 0, a: 0}
- _ColorInner: {r: 2.2, g: 0.95, b: 0.15, a: 1}
- _ColorMid: {r: 1.8, g: 0.55, b: 0, a: 1}
- _ColorOuter: {r: 1.5, g: 0.2, b: 0.1, a: 1}
- _Speed: {r: 0, g: -1.8, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3751385830976373873

View File

@ -4937,7 +4937,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!4 &314704398255972110
Transform:
m_ObjectHideFlags: 0
@ -4989,7 +4989,7 @@ VisualEffect:
- m_Value: {x: 2, y: 0.019583605, z: 0, w: 1}
m_Name: ParticleColor
m_Overridden: 1
- m_Value: {x: 4, y: 0.20000002, z: 0, w: 1}
- m_Value: {x: 766.9961, y: 275.83508, z: 0, w: 1}
m_Name: ShaderColor
m_Overridden: 1
m_Uint:
@ -5014,7 +5014,7 @@ VFXRenderer:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4081737046539047411}
m_Enabled: 1
m_Enabled: 0
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
@ -5187,7 +5187,7 @@ VisualEffect:
- m_Value: {x: 1, y: 0, z: 0, w: 1}
m_Name: ParticleColor
m_Overridden: 0
- m_Value: {x: 2, y: 0.06666667, z: 0, w: 1}
- m_Value: {x: 32, y: 1.0666667, z: 0, w: 1}
m_Name: ShaderColor
m_Overridden: 1
m_Uint:

View File

@ -297,7 +297,7 @@ MonoBehaviour:
m_NumColorKeys: 3
m_NumAlphaKeys: 3
overrideTexture: 1
texture: {fileID: 0}
texture: {fileID: 2800000, guid: 375a132719b8f114aadcfda6bf62f8b1, type: 3}
--- !u!114 &2117915465640148810
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@ -8,6 +8,14 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: -7905401754613951787, guid: 6331eaa4049f7fc4a8107649fc474455, type: 3}
propertyPath: jumpVFX
value:
objectReference: {fileID: 8926484042661614526, guid: 2a6298d2789c9b14faf02ffd58da2493, type: 3}
- target: {fileID: -7905401754613951787, guid: 6331eaa4049f7fc4a8107649fc474455, type: 3}
propertyPath: landingVFX
value:
objectReference: {fileID: 8926484042661614526, guid: 2a6298d2789c9b14faf02ffd58da2493, type: 3}
- target: {fileID: 1088233528469808677, guid: 6331eaa4049f7fc4a8107649fc474455, type: 3}
propertyPath: m_Color.b
value: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e2aff61ec559c014bb6e119d27baf2f6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &-7402772468999753049
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Prize Material
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: e3095083c95b2da47993c18127b66b96, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _ColorMask: 15
- _CullMode: 0
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UseUIAlphaClip: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3}
m_Name: Prize
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TMP_SpriteAsset
m_Version: 1.1.0
m_FaceInfo:
m_FaceIndex: 0
m_FamilyName:
m_StyleName:
m_PointSize: 283
m_Scale: 1
m_UnitsPerEM: 0
m_LineHeight: 0
m_AscentLine: 283
m_CapLine: 198.09999
m_MeanLine: 0
m_Baseline: 0
m_DescentLine: 0
m_SuperscriptOffset: 0
m_SuperscriptSize: 0
m_SubscriptOffset: 0
m_SubscriptSize: 0
m_UnderlineOffset: 0
m_UnderlineThickness: 0
m_StrikethroughOffset: 0
m_StrikethroughThickness: 0
m_TabWidth: 0
m_Material: {fileID: -7402772468999753049}
spriteSheet: {fileID: 2800000, guid: e3095083c95b2da47993c18127b66b96, type: 3}
m_SpriteCharacterTable:
- m_ElementType: 2
m_Unicode: 65534
m_GlyphIndex: 0
m_Scale: 1
m_Name: Prize_0
m_GlyphTable:
- m_Index: 0
m_Metrics:
m_Width: 346
m_Height: 283
m_HorizontalBearingX: 0
m_HorizontalBearingY: 240.55
m_HorizontalAdvance: 346
m_GlyphRect:
m_X: 144
m_Y: 176
m_Width: 346
m_Height: 283
m_Scale: 1
m_AtlasIndex: 0
m_ClassDefinitionType: 0
sprite: {fileID: 6685656491756494725, guid: e3095083c95b2da47993c18127b66b96, type: 3}
spriteInfoList: []
fallbackSpriteAssets: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e3ce0f400e4eae24da335bfef0eaf163
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3}
m_Name: Prize_Hopper
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TMP_SpriteAsset
m_Version: 1.1.0
m_FaceInfo:
m_FaceIndex: 0
m_FamilyName:
m_StyleName:
m_PointSize: 283
m_Scale: 1
m_UnitsPerEM: 0
m_LineHeight: 0
m_AscentLine: 283
m_CapLine: 198.09999
m_MeanLine: 0
m_Baseline: 0
m_DescentLine: 0
m_SuperscriptOffset: 0
m_SuperscriptSize: 0
m_SubscriptOffset: 0
m_SubscriptSize: 0
m_UnderlineOffset: 0
m_UnderlineThickness: 0
m_StrikethroughOffset: 0
m_StrikethroughThickness: 0
m_TabWidth: 0
m_Material: {fileID: 8613041187514458185}
spriteSheet: {fileID: 2800000, guid: ac75678832a3e1b4ba5aa54e6fd12388, type: 3}
m_SpriteCharacterTable:
- m_ElementType: 2
m_Unicode: 65534
m_GlyphIndex: 0
m_Scale: 1
m_Name: Prize_Hopper_0
m_GlyphTable:
- m_Index: 0
m_Metrics:
m_Width: 277
m_Height: 283
m_HorizontalBearingX: 0
m_HorizontalBearingY: 240.55
m_HorizontalAdvance: 277
m_GlyphRect:
m_X: 178
m_Y: 175
m_Width: 277
m_Height: 283
m_Scale: 1
m_AtlasIndex: 0
m_ClassDefinitionType: 0
sprite: {fileID: -220201777628614610, guid: ac75678832a3e1b4ba5aa54e6fd12388, type: 3}
spriteInfoList: []
fallbackSpriteAssets: []
--- !u!21 &8613041187514458185
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Prize_Hopper Material
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: ac75678832a3e1b4ba5aa54e6fd12388, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _ColorMask: 15
- _CullMode: 0
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UseUIAlphaClip: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 490c014c224f28f4b9cbba78a03997ce
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &-4433844605069381581
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Prize_Marbles Material
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: a2253fc821e9c234daa8e92afdfc9e7c, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _ColorMask: 15
- _CullMode: 0
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UseUIAlphaClip: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3}
m_Name: Prize_Marbles
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TMP_SpriteAsset
m_Version: 1.1.0
m_FaceInfo:
m_FaceIndex: 0
m_FamilyName:
m_StyleName:
m_PointSize: 295
m_Scale: 1
m_UnitsPerEM: 0
m_LineHeight: 0
m_AscentLine: 295
m_CapLine: 206.5
m_MeanLine: 0
m_Baseline: 0
m_DescentLine: 0
m_SuperscriptOffset: 0
m_SuperscriptSize: 0
m_SubscriptOffset: 0
m_SubscriptSize: 0
m_UnderlineOffset: 0
m_UnderlineThickness: 0
m_StrikethroughOffset: 0
m_StrikethroughThickness: 0
m_TabWidth: 0
m_Material: {fileID: -4433844605069381581}
spriteSheet: {fileID: 2800000, guid: a2253fc821e9c234daa8e92afdfc9e7c, type: 3}
m_SpriteCharacterTable:
- m_ElementType: 2
m_Unicode: 65534
m_GlyphIndex: 0
m_Scale: 1
m_Name: Prize_Marbles_0
m_GlyphTable:
- m_Index: 0
m_Metrics:
m_Width: 295
m_Height: 295
m_HorizontalBearingX: 0
m_HorizontalBearingY: 250.75
m_HorizontalAdvance: 295
m_GlyphRect:
m_X: 169
m_Y: 170
m_Width: 295
m_Height: 295
m_Scale: 1
m_AtlasIndex: 0
m_ClassDefinitionType: 0
sprite: {fileID: -6115330580039227016, guid: a2253fc821e9c234daa8e92afdfc9e7c, type: 3}
spriteInfoList: []
fallbackSpriteAssets: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9418adc7dbab1b44299f26bbbd2fdff4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3}
m_Name: Prize_QR
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TMP_SpriteAsset
m_Version: 1.1.0
m_FaceInfo:
m_FaceIndex: 0
m_FamilyName:
m_StyleName:
m_PointSize: 271
m_Scale: 1
m_UnitsPerEM: 0
m_LineHeight: 0
m_AscentLine: 271
m_CapLine: 189.7
m_MeanLine: 0
m_Baseline: 0
m_DescentLine: 0
m_SuperscriptOffset: 0
m_SuperscriptSize: 0
m_SubscriptOffset: 0
m_SubscriptSize: 0
m_UnderlineOffset: 0
m_UnderlineThickness: 0
m_StrikethroughOffset: 0
m_StrikethroughThickness: 0
m_TabWidth: 0
m_Material: {fileID: 7580124773194365788}
spriteSheet: {fileID: 2800000, guid: 1d7aa42f6d3c2ea4bb433db905cf6e6e, type: 3}
m_SpriteCharacterTable:
- m_ElementType: 2
m_Unicode: 65534
m_GlyphIndex: 0
m_Scale: 1
m_Name: Prize_QR_0
m_GlyphTable:
- m_Index: 0
m_Metrics:
m_Width: 271
m_Height: 271
m_HorizontalBearingX: 0
m_HorizontalBearingY: 230.35
m_HorizontalAdvance: 271
m_GlyphRect:
m_X: 181
m_Y: 182
m_Width: 271
m_Height: 271
m_Scale: 1
m_AtlasIndex: 0
m_ClassDefinitionType: 0
sprite: {fileID: -1280765752914891198, guid: 1d7aa42f6d3c2ea4bb433db905cf6e6e, type: 3}
spriteInfoList: []
fallbackSpriteAssets: []
--- !u!21 &7580124773194365788
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Prize_QR Material
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: 1d7aa42f6d3c2ea4bb433db905cf6e6e, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _ColorMask: 15
- _CullMode: 0
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UseUIAlphaClip: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 08c741fa79603ea46be62f9e1daceb66
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &-1533674834702946927
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Prize_Tickets Material
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: 9ca00dbece4218c459504eab0334a06b, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _ColorMask: 15
- _CullMode: 0
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UseUIAlphaClip: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3}
m_Name: Prize_Tickets
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TMP_SpriteAsset
m_Version: 1.1.0
m_FaceInfo:
m_FaceIndex: 0
m_FamilyName:
m_StyleName:
m_PointSize: 317
m_Scale: 1
m_UnitsPerEM: 0
m_LineHeight: 0
m_AscentLine: 317
m_CapLine: 221.9
m_MeanLine: 0
m_Baseline: 0
m_DescentLine: 0
m_SuperscriptOffset: 0
m_SuperscriptSize: 0
m_SubscriptOffset: 0
m_SubscriptSize: 0
m_UnderlineOffset: 0
m_UnderlineThickness: 0
m_StrikethroughOffset: 0
m_StrikethroughThickness: 0
m_TabWidth: 0
m_Material: {fileID: -1533674834702946927}
spriteSheet: {fileID: 2800000, guid: 9ca00dbece4218c459504eab0334a06b, type: 3}
m_SpriteCharacterTable:
- m_ElementType: 2
m_Unicode: 65534
m_GlyphIndex: 0
m_Scale: 1
m_Name: Prize_Tickets_0
m_GlyphTable:
- m_Index: 0
m_Metrics:
m_Width: 295
m_Height: 317
m_HorizontalBearingX: 0
m_HorizontalBearingY: 269.45
m_HorizontalAdvance: 295
m_GlyphRect:
m_X: 169
m_Y: 159
m_Width: 295
m_Height: 317
m_Scale: 1
m_AtlasIndex: 0
m_ClassDefinitionType: 0
sprite: {fileID: -167649136679044476, guid: 9ca00dbece4218c459504eab0334a06b, type: 3}
spriteInfoList: []
fallbackSpriteAssets: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 81d03856d45a8b84f8950cec300c2e5d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -37,7 +37,7 @@ TextureImporter:
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c5bc26aa565fb740a068b5d1fdd18cb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/UI/Labels/Prize.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,169 @@
fileFormatVersion: 2
guid: e3095083c95b2da47993c18127b66b96
TextureImporter:
internalIDToNameTable:
- first:
213: 6685656491756494725
second: Prize_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: Prize_0
rect:
serializedVersion: 2
x: 144
y: 176
width: 346
height: 283
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 58f1e69382938cc50800000000000000
internalID: 6685656491756494725
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
Prize_0: 6685656491756494725
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/UI/Labels/Prize_Hopper.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,173 @@
fileFormatVersion: 2
guid: ac75678832a3e1b4ba5aa54e6fd12388
TextureImporter:
internalIDToNameTable:
- first:
213: -220201777628614610
second: Prize_Hopper_0
- first:
213: 5311543218980421935
second: Prize_Hopper_1
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: Prize_Hopper_0
rect:
serializedVersion: 2
x: 178
y: 175
width: 277
height: 283
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: e20798166afa1fcf0800000000000000
internalID: -220201777628614610
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 00c876d74e2ccd64b981881478232845
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
Prize_Hopper_0: -220201777628614610
Prize_Hopper_1: 5311543218980421935
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/UI/Labels/Prize_Marbles.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,169 @@
fileFormatVersion: 2
guid: a2253fc821e9c234daa8e92afdfc9e7c
TextureImporter:
internalIDToNameTable:
- first:
213: -6115330580039227016
second: Prize_Marbles_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: Prize_Marbles_0
rect:
serializedVersion: 2
x: 169
y: 170
width: 295
height: 295
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 879ada4ec3bf12ba0800000000000000
internalID: -6115330580039227016
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
Prize_Marbles_0: -6115330580039227016
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/UI/Labels/Prize_QR.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,169 @@
fileFormatVersion: 2
guid: 1d7aa42f6d3c2ea4bb433db905cf6e6e
TextureImporter:
internalIDToNameTable:
- first:
213: -1280765752914891198
second: Prize_QR_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: Prize_QR_0
rect:
serializedVersion: 2
x: 181
y: 182
width: 271
height: 271
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 24ea501655ec93ee0800000000000000
internalID: -1280765752914891198
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
Prize_QR_0: -1280765752914891198
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/UI/Labels/Prize_Tickets.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,169 @@
fileFormatVersion: 2
guid: 9ca00dbece4218c459504eab0334a06b
TextureImporter:
internalIDToNameTable:
- first:
213: -167649136679044476
second: Prize_Tickets_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: Prize_Tickets_0
rect:
serializedVersion: 2
x: 169
y: 159
width: 295
height: 317
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 48a38599ff36cadf0800000000000000
internalID: -167649136679044476
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
Prize_Tickets_0: -167649136679044476
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -227,16 +227,16 @@ namespace RR.Game2D.Behaviour
if (!CanSpawn())
return null;
GameObject prefab = PickPrefab();
if (prefab == null)
return null;
if (!TryPickSpawnPoint(usedSpawnPointIndices, out Transform spawnPoint))
return null;
Vector3 spawnPosition = spawnPoint != null ? spawnPoint.position : transform.position;
Quaternion spawnRotation = spawnPoint != null ? spawnPoint.rotation : transform.rotation;
GameObject prefab = PickPrefab(spawnPosition);
if (prefab == null)
return null;
float zAngle = UnityEngine.Random.Range(randomZRotation.x, randomZRotation.y);
spawnRotation *= Quaternion.Euler(0f, 0f, zAngle);
@ -261,16 +261,34 @@ namespace RR.Game2D.Behaviour
return true;
}
private GameObject PickPrefab()
private GameObject PickPrefab(Vector3 spawnPosition)
{
// Проверка, находится ли эта точка спавна внутри какой-либо активной зоны переопределения
SpawnPrefabEntry[] activePrefabs = prefabs;
EntitySpawnerZone matchedZone = null;
for (int i = 0; i < EntitySpawnerZone.ActiveZones.Count; i++)
{
EntitySpawnerZone zone = EntitySpawnerZone.ActiveZones[i];
if (zone != null && zone.MatchesSpawner(this) && zone.IsPointInside(spawnPosition))
{
activePrefabs = zone.Prefabs;
matchedZone = zone;
break;
}
}
if (activePrefabs == null || activePrefabs.Length == 0)
return null;
int totalWeight = 0;
for (int i = 0; i < prefabs.Length; i++)
for (int i = 0; i < activePrefabs.Length; i++)
{
if (prefabs[i].prefab == null)
if (activePrefabs[i].prefab == null)
continue;
totalWeight += Mathf.Max(1, prefabs[i].weight);
totalWeight += Mathf.Max(1, activePrefabs[i].weight);
}
if (totalWeight <= 0)
@ -278,18 +296,30 @@ namespace RR.Game2D.Behaviour
int randomValue = UnityEngine.Random.Range(0, totalWeight);
int currentWeight = 0;
GameObject selectedPrefab = null;
for (int i = 0; i < prefabs.Length; i++)
for (int i = 0; i < activePrefabs.Length; i++)
{
if (prefabs[i].prefab == null)
if (activePrefabs[i].prefab == null)
continue;
currentWeight += Mathf.Max(1, prefabs[i].weight);
currentWeight += Mathf.Max(1, activePrefabs[i].weight);
if (randomValue < currentWeight)
return prefabs[i].prefab;
{
selectedPrefab = activePrefabs[i].prefab;
break;
}
}
return prefabs[prefabs.Length - 1].prefab;
if (selectedPrefab == null)
selectedPrefab = activePrefabs[activePrefabs.Length - 1].prefab;
if (selectedPrefab != null && matchedZone != null)
{
Debug.Log($"[EntitySpawner] Спавнер '{gameObject.name}' переопределил префаб для точки {spawnPosition} на '{selectedPrefab.name}' по зоне '{matchedZone.gameObject.name}'.");
}
return selectedPrefab;
}
private bool TryPickSpawnPoint(HashSet<int> usedSpawnPointIndices, out Transform spawnPoint)

View File

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace RR.Game2D.Behaviour
{
[RequireComponent(typeof(Collider2D))]
[DisallowMultipleComponent]
public sealed class EntitySpawnerZone : MonoBehaviour
{
[Header("Override Prefabs")]
[Tooltip("Список префабов, которые будут спавниться вместо оригинальных префабов спавнера внутри этой зоны.")]
[SerializeField] private EntitySpawner.SpawnPrefabEntry[] prefabs;
[Header("Targeting")]
[Tooltip("Фильтр по имени GameObject спавнера. Если не пусто, зона будет переопределять только те спавнеры, чье имя содержит этот текст (без учета регистра).")]
[SerializeField] private string spawnerNameFilter = "Enemy";
private Collider2D _collider;
// Статический список всех активных зон на сцене для быстрого поиска
public static readonly List<EntitySpawnerZone> ActiveZones = new();
public EntitySpawner.SpawnPrefabEntry[] Prefabs => prefabs;
private void Awake()
{
_collider = GetComponent<Collider2D>();
// Убеждаемся, что коллайдер является триггером, чтобы не вызывать физических коллизий с персонажами
if (_collider != null)
{
_collider.isTrigger = true;
}
}
private void OnEnable()
{
ActiveZones.Add(this);
}
private void OnDisable()
{
ActiveZones.Remove(this);
}
/// <summary>
/// Проверяет, подходит ли данный спавнер под критерии фильтрации зоны.
/// </summary>
public bool MatchesSpawner(EntitySpawner spawner)
{
if (spawner == null)
return false;
if (string.IsNullOrEmpty(spawnerNameFilter))
return true;
return spawner.gameObject.name.IndexOf(spawnerNameFilter, StringComparison.OrdinalIgnoreCase) >= 0;
}
/// <summary>
/// Проверяет, находится ли мировая точка внутри коллайдера зоны.
/// </summary>
public bool IsPointInside(Vector3 point)
{
if (_collider == null)
{
_collider = GetComponent<Collider2D>();
}
if (_collider == null)
return false;
// Если это BoxCollider2D, выполняем точную математическую проверку в локальных координатах.
// Это работает надежно в любой момент (даже до первого физического обновления) и не зависит от Z-координаты.
if (_collider is BoxCollider2D box)
{
Vector3 localPoint = transform.InverseTransformPoint(point);
return Mathf.Abs(localPoint.x - box.offset.x) <= box.size.x * 0.5f &&
Mathf.Abs(localPoint.y - box.offset.y) <= box.size.y * 0.5f;
}
return _collider.OverlapPoint(point);
}
private void OnDrawGizmos()
{
if (_collider == null)
{
_collider = GetComponent<Collider2D>();
}
if (_collider == null) return;
// Рисуем оранжевую рамку зоны замены в редакторе
Gizmos.color = new Color(1f, 0.5f, 0f, 0.2f);
if (_collider is BoxCollider2D box)
{
Vector3 position = transform.TransformPoint(box.offset);
Vector3 size = Vector3.Scale(box.size, transform.lossyScale);
Gizmos.DrawCube(position, size);
Gizmos.color = new Color(1f, 0.5f, 0f, 0.8f);
Gizmos.DrawWireCube(position, size);
}
else
{
// Для других типов коллайдеров рисуем сферу в центре объекта
Gizmos.DrawSphere(transform.position, 0.5f);
}
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b5c91a3a7ea71314e9c60d5e48b397d4

View File

@ -0,0 +1,226 @@
using System.Collections.Generic;
using RR.Game2D.Core.Managers;
using UnityEngine;
using UnityEngine.U2D.Animation;
namespace RR.Game2D.Behaviour
{
/// <summary>
/// Заменяет спрайты созданного игрока спрайтами из совместимого prefab-скина.
/// Компонент действует только в собственной сцене и не изменяет prefab игрока.
/// </summary>
[DisallowMultipleComponent]
public sealed class PlayerSkinOverride : MonoBehaviour
{
[Header("Скин игрока")]
[Tooltip("Prefab с той же иерархией SpriteRenderer + SpriteSkin, что и у игрока.")]
[SerializeField] private GameObject skinPrefab;
private GameManager _subscribedManager;
private GameObject _lastAppliedPlayer;
private string _lastReportedProblem;
private void OnEnable()
{
SubscribeToPlayerSpawn();
TryApplyToCurrentPlayer();
}
private void Start()
{
// Повторная попытка нужна, если компонент включился раньше GameManager.
SubscribeToPlayerSpawn();
TryApplyToCurrentPlayer();
}
private void OnDisable()
{
UnsubscribeFromPlayerSpawn();
_lastAppliedPlayer = null;
}
private void SubscribeToPlayerSpawn()
{
GameManager manager = GameManager.Instance;
if (_subscribedManager == manager) return;
UnsubscribeFromPlayerSpawn();
if (manager == null) return;
_subscribedManager = manager;
_subscribedManager.PlayerSpawned += OnPlayerSpawned;
}
private void UnsubscribeFromPlayerSpawn()
{
if (_subscribedManager != null)
_subscribedManager.PlayerSpawned -= OnPlayerSpawned;
_subscribedManager = null;
}
private void TryApplyToCurrentPlayer()
{
GameObject player = _subscribedManager != null ? _subscribedManager.PlayerObj : null;
if (player == null || player.scene != gameObject.scene) return;
ApplySkin(player);
}
private void OnPlayerSpawned(GameObject player)
{
if (player == null || player.scene != gameObject.scene) return;
ApplySkin(player);
}
private void ApplySkin(GameObject player)
{
if (_lastAppliedPlayer == player) return;
if (HasAnotherOverrideInScene())
{
ReportProblem("В сцене найдено несколько активных PlayerSkinOverride. Скин не применён.", true);
return;
}
if (skinPrefab == null)
{
ReportProblem("Prefab скина не назначен. Используется стандартный скин игрока.", false);
return;
}
if (skinPrefab.scene.IsValid())
{
ReportProblem("В поле Skin Prefab должен быть назначен prefab-ассет, а не объект сцены.", true);
return;
}
if (!TryCollectParts(skinPrefab, "скина", true, out Dictionary<string, SpriteRenderer> skinParts))
return;
if (!TryCollectParts(player, "игрока", false, out Dictionary<string, SpriteRenderer> playerParts))
return;
if (skinParts.Count != playerParts.Count)
{
ReportProblem(
$"Структура скина несовместима с игроком: частей в скине — {skinParts.Count}, " +
$"частей у игрока — {playerParts.Count}. Скин не применён.",
true);
return;
}
foreach (KeyValuePair<string, SpriteRenderer> skinPart in skinParts)
{
if (!playerParts.ContainsKey(skinPart.Key))
{
ReportProblem(
$"У игрока отсутствует визуальная часть \"{skinPart.Key}\". Скин не применён.",
true);
return;
}
}
// Замена выполняется только после полной проверки, чтобы не получить частично применённый скин.
foreach (KeyValuePair<string, SpriteRenderer> skinPart in skinParts)
playerParts[skinPart.Key].sprite = skinPart.Value.sprite;
_lastAppliedPlayer = player;
_lastReportedProblem = null;
}
private bool TryCollectParts(
GameObject root,
string rootDescription,
bool requireSprite,
out Dictionary<string, SpriteRenderer> parts)
{
parts = new Dictionary<string, SpriteRenderer>();
SpriteSkin[] spriteSkins = root.GetComponentsInChildren<SpriteSkin>(true);
if (spriteSkins.Length == 0)
{
ReportProblem(
$"В иерархии {rootDescription} \"{root.name}\" не найдено ни одного SpriteSkin.",
true);
return false;
}
foreach (SpriteSkin spriteSkin in spriteSkins)
{
SpriteRenderer spriteRenderer = spriteSkin.GetComponent<SpriteRenderer>();
string relativePath = GetRelativePath(root.transform, spriteSkin.transform);
if (spriteRenderer == null)
{
ReportProblem(
$"На части {rootDescription} \"{relativePath}\" отсутствует SpriteRenderer.",
true);
return false;
}
if (requireSprite && spriteRenderer.sprite == null)
{
ReportProblem(
$"На части {rootDescription} \"{relativePath}\" не назначен Sprite.",
true);
return false;
}
if (!parts.TryAdd(relativePath, spriteRenderer))
{
ReportProblem(
$"В иерархии {rootDescription} повторяется путь \"{relativePath}\".",
true);
return false;
}
}
return true;
}
private bool HasAnotherOverrideInScene()
{
PlayerSkinOverride[] overrides = FindObjectsByType<PlayerSkinOverride>(
FindObjectsInactive.Exclude,
FindObjectsSortMode.None);
foreach (PlayerSkinOverride candidate in overrides)
{
if (candidate != this && candidate.gameObject.scene == gameObject.scene)
return true;
}
return false;
}
private static string GetRelativePath(Transform root, Transform child)
{
if (root == child) return string.Empty;
Stack<string> names = new();
Transform current = child;
while (current != null && current != root)
{
names.Push(current.name);
current = current.parent;
}
return string.Join("/", names);
}
private void ReportProblem(string message, bool isError)
{
if (_lastReportedProblem == message) return;
_lastReportedProblem = message;
string formattedMessage = $"[{nameof(PlayerSkinOverride)}] {message}";
if (isError)
Debug.LogError(formattedMessage, this);
else
Debug.LogWarning(formattedMessage, this);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3784dd4dcbda97c44a85ea59fdee2076

View File

@ -0,0 +1,67 @@
using UnityEngine;
using UnityEngine.VFX;
namespace RR.Game2D.Entities
{
public class ShowObjectWithVFX : MonoBehaviour
{
[Header("Target Settings")]
[SerializeField] private GameObject targetObject;
[SerializeField] private bool hideOnStart = true;
[Header("VFX Settings")]
[SerializeField] private VisualEffect visualEffect;
[SerializeField] private ParticleSystem particleSystemComponent;
[SerializeField] private GameObject vfxPrefab;
[SerializeField] private float vfxDestroyDelay = 3f;
private void Start()
{
if (hideOnStart && targetObject != null)
{
targetObject.SetActive(false);
}
if (visualEffect != null)
{
visualEffect.gameObject.SetActive(false);
}
if (particleSystemComponent != null)
{
var emission = particleSystemComponent.emission;
emission.enabled = false;
particleSystemComponent.gameObject.SetActive(false);
}
}
[ContextMenu("Activate")]
public void Activate()
{
if (targetObject != null)
{
targetObject.SetActive(true);
}
if (visualEffect != null)
{
visualEffect.gameObject.SetActive(true);
visualEffect.Play();
}
if (particleSystemComponent != null)
{
particleSystemComponent.gameObject.SetActive(true);
var emission = particleSystemComponent.emission;
emission.enabled = true;
particleSystemComponent.Play();
}
if (vfxPrefab != null)
{
GameObject vfxInstance = Instantiate(vfxPrefab, transform.position, transform.rotation);
Destroy(vfxInstance, vfxDestroyDelay);
}
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 49989a9e1bab58c42bf27b3f7eca8d5b

View File

@ -162,34 +162,35 @@ namespace RR.Common.UI
return config.type switch
{
PrizeType.QRCode => BuildQrPrizeText(manager, config.qrTier),
PrizeType.Hopper => $"хоппер, капсул: {Mathf.Max(1, config.hopperCapsuleCount)}",
PrizeType.PrizeStand => BuildPrizeStandText(manager),
PrizeType.QRCode => BuildQrPrizeSpriteText(manager, config.qrTier),
PrizeType.Hopper => $"<sprite=\"Prize_Hopper\" name=\"Prize_Hopper_0\"> x{Mathf.Max(1, config.hopperCapsuleCount)}",
PrizeType.PrizeStand => BuildPrizeStandSpriteText(manager),
_ => config.type.ToString()
};
}
private static string BuildQrPrizeText(PrizeSystemManager manager, QRTier tier)
private static string BuildQrPrizeSpriteText(PrizeSystemManager manager, QRTier tier)
{
string tierText = tier switch
{
QRTier.Bronze => "бронзовый QR-код",
QRTier.Silver => "серебряный QR-код",
QRTier.Gold => "золотой QR-код",
_ => "QR-код"
};
string prizeText = manager != null ? manager.GetQRCodeModalPrizeText(tier) : string.Empty;
bool isTicket = !string.IsNullOrEmpty(prizeText) &&
(prizeText.IndexOf("билет", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
prizeText.IndexOf("ticket", System.StringComparison.OrdinalIgnoreCase) >= 0);
string spriteTag = isTicket
? "<sprite=\"Prize_Tickets\" name=\"Prize_Tickets_0\">"
: "<sprite=\"Prize_QR\" name=\"Prize_QR_0\">";
return string.IsNullOrWhiteSpace(prizeText)
? tierText
: $"{tierText}: {prizeText.Trim()}";
? spriteTag
: $"{spriteTag} {prizeText.Trim()}";
}
private static string BuildPrizeStandText(PrizeSystemManager manager)
private static string BuildPrizeStandSpriteText(PrizeSystemManager manager)
{
string spriteTag = "<sprite=\"Prize_Marbles\" name=\"Prize_Marbles_0\">";
PrizeStandSettings settings = manager != null ? manager.GetPrizeStandSettings() : null;
int boxCount = settings?.boxes != null ? settings.boxes.Count : 0;
return boxCount > 0 ? $"призовая витрина, ячеек: {boxCount}" : "призовая витрина";
return boxCount > 0 ? $"{spriteTag} (ячеек: {boxCount})" : spriteTag;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,359 +0,0 @@
{
"m_SGVersion": 3,
"m_Type": "UnityEditor.ShaderGraph.GraphData",
"m_ObjectId": "bfd41be4768743d49abea9ce9a041ce1",
"m_Properties": [],
"m_Keywords": [],
"m_Dropdowns": [],
"m_CategoryData": [
{
"m_Id": "10f6db50c33e4ea2bb7a18d474e4bbff"
}
],
"m_Nodes": [
{
"m_Id": "94c186bebeef4d2b9f136112253d8cd5"
},
{
"m_Id": "252fbabf290644db92ce5bee37d0512a"
},
{
"m_Id": "2dcf088eb2824593aefd458b9ff69f99"
},
{
"m_Id": "cdcab024229546818a2d8b4d465e82e6"
}
],
"m_GroupDatas": [],
"m_StickyNoteDatas": [],
"m_Edges": [],
"m_VertexContext": {
"m_Position": {
"x": 0.0,
"y": 0.0
},
"m_Blocks": [
{
"m_Id": "94c186bebeef4d2b9f136112253d8cd5"
},
{
"m_Id": "252fbabf290644db92ce5bee37d0512a"
},
{
"m_Id": "2dcf088eb2824593aefd458b9ff69f99"
}
]
},
"m_FragmentContext": {
"m_Position": {
"x": 0.0,
"y": 200.0
},
"m_Blocks": [
{
"m_Id": "cdcab024229546818a2d8b4d465e82e6"
}
]
},
"m_PreviewData": {
"serializedMesh": {
"m_SerializedMesh": "{\"mesh\":{\"instanceID\":0}}",
"m_Guid": ""
},
"preventRotation": false
},
"m_Path": "Shader Graphs",
"m_GraphPrecision": 1,
"m_PreviewMode": 2,
"m_OutputNode": {
"m_Id": ""
},
"m_SubDatas": [],
"m_ActiveTargets": [
{
"m_Id": "54c208f34b824035815c4e6157e4325e"
}
]
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.CategoryData",
"m_ObjectId": "10f6db50c33e4ea2bb7a18d474e4bbff",
"m_Name": "",
"m_ChildObjectList": []
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.BlockNode",
"m_ObjectId": "252fbabf290644db92ce5bee37d0512a",
"m_Group": {
"m_Id": ""
},
"m_Name": "VertexDescription.Normal",
"m_DrawState": {
"m_Expanded": true,
"m_Position": {
"serializedVersion": "2",
"x": 0.0,
"y": 0.0,
"width": 0.0,
"height": 0.0
}
},
"m_Slots": [
{
"m_Id": "65da9c62784d4cb19d46bba53309634c"
}
],
"synonyms": [],
"m_Precision": 0,
"m_PreviewExpanded": true,
"m_DismissedVersion": 0,
"m_PreviewMode": 0,
"m_CustomColors": {
"m_SerializableColors": []
},
"m_SerializedDescriptor": "VertexDescription.Normal"
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.BlockNode",
"m_ObjectId": "2dcf088eb2824593aefd458b9ff69f99",
"m_Group": {
"m_Id": ""
},
"m_Name": "VertexDescription.Tangent",
"m_DrawState": {
"m_Expanded": true,
"m_Position": {
"serializedVersion": "2",
"x": 0.0,
"y": 0.0,
"width": 0.0,
"height": 0.0
}
},
"m_Slots": [
{
"m_Id": "81df142a9ee645e08cfc667785bf4b7e"
}
],
"synonyms": [],
"m_Precision": 0,
"m_PreviewExpanded": true,
"m_DismissedVersion": 0,
"m_PreviewMode": 0,
"m_CustomColors": {
"m_SerializableColors": []
},
"m_SerializedDescriptor": "VertexDescription.Tangent"
}
{
"m_SGVersion": 1,
"m_Type": "UnityEditor.Rendering.Universal.ShaderGraph.UniversalTarget",
"m_ObjectId": "54c208f34b824035815c4e6157e4325e",
"m_Datas": [],
"m_ActiveSubTarget": {
"m_Id": "70ee05b7f8f24361a49d9b6ebda7422a"
},
"m_AllowMaterialOverride": false,
"m_SurfaceType": 0,
"m_ZTestMode": 4,
"m_ZWriteControl": 0,
"m_AlphaMode": 0,
"m_RenderFace": 2,
"m_AlphaClip": false,
"m_CastShadows": true,
"m_ReceiveShadows": true,
"m_DisableTint": false,
"m_Sort3DAs2DCompatible": false,
"m_AdditionalMotionVectorMode": 0,
"m_AlembicMotionVectors": false,
"m_SupportsLODCrossFade": false,
"m_CustomEditorGUI": "",
"m_SupportVFX": false
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.PositionMaterialSlot",
"m_ObjectId": "5b35b4eb69ed4bcaa6689ec1d77efabe",
"m_Id": 0,
"m_DisplayName": "Position",
"m_SlotType": 0,
"m_Hidden": false,
"m_ShaderOutputName": "Position",
"m_StageCapability": 1,
"m_Value": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"m_DefaultValue": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"m_Labels": [],
"m_Space": 0
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.NormalMaterialSlot",
"m_ObjectId": "65da9c62784d4cb19d46bba53309634c",
"m_Id": 0,
"m_DisplayName": "Normal",
"m_SlotType": 0,
"m_Hidden": false,
"m_ShaderOutputName": "Normal",
"m_StageCapability": 1,
"m_Value": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"m_DefaultValue": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"m_Labels": [],
"m_Space": 0
}
{
"m_SGVersion": 2,
"m_Type": "UnityEditor.Rendering.Universal.ShaderGraph.UniversalUnlitSubTarget",
"m_ObjectId": "70ee05b7f8f24361a49d9b6ebda7422a",
"m_KeepLightingVariants": false,
"m_DefaultDecalBlending": true,
"m_DefaultSSAO": true
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.TangentMaterialSlot",
"m_ObjectId": "81df142a9ee645e08cfc667785bf4b7e",
"m_Id": 0,
"m_DisplayName": "Tangent",
"m_SlotType": 0,
"m_Hidden": false,
"m_ShaderOutputName": "Tangent",
"m_StageCapability": 1,
"m_Value": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"m_DefaultValue": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"m_Labels": [],
"m_Space": 0
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.BlockNode",
"m_ObjectId": "94c186bebeef4d2b9f136112253d8cd5",
"m_Group": {
"m_Id": ""
},
"m_Name": "VertexDescription.Position",
"m_DrawState": {
"m_Expanded": true,
"m_Position": {
"serializedVersion": "2",
"x": 0.0,
"y": 0.0,
"width": 0.0,
"height": 0.0
}
},
"m_Slots": [
{
"m_Id": "5b35b4eb69ed4bcaa6689ec1d77efabe"
}
],
"synonyms": [],
"m_Precision": 0,
"m_PreviewExpanded": true,
"m_DismissedVersion": 0,
"m_PreviewMode": 0,
"m_CustomColors": {
"m_SerializableColors": []
},
"m_SerializedDescriptor": "VertexDescription.Position"
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.BlockNode",
"m_ObjectId": "cdcab024229546818a2d8b4d465e82e6",
"m_Group": {
"m_Id": ""
},
"m_Name": "SurfaceDescription.BaseColor",
"m_DrawState": {
"m_Expanded": true,
"m_Position": {
"serializedVersion": "2",
"x": 0.0,
"y": 0.0,
"width": 0.0,
"height": 0.0
}
},
"m_Slots": [
{
"m_Id": "eb00736542bf494b858ac57f9e4a3d72"
}
],
"synonyms": [],
"m_Precision": 0,
"m_PreviewExpanded": true,
"m_DismissedVersion": 0,
"m_PreviewMode": 0,
"m_CustomColors": {
"m_SerializableColors": []
},
"m_SerializedDescriptor": "SurfaceDescription.BaseColor"
}
{
"m_SGVersion": 0,
"m_Type": "UnityEditor.ShaderGraph.ColorRGBMaterialSlot",
"m_ObjectId": "eb00736542bf494b858ac57f9e4a3d72",
"m_Id": 0,
"m_DisplayName": "Base Color",
"m_SlotType": 0,
"m_Hidden": false,
"m_ShaderOutputName": "BaseColor",
"m_StageCapability": 2,
"m_Value": {
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"m_DefaultValue": {
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"m_Labels": [],
"m_ColorMode": 0,
"m_DefaultColor": {
"r": 0.5,
"g": 0.5,
"b": 0.5,
"a": 1.0
}
}

View File

@ -1,18 +0,0 @@
fileFormatVersion: 2
guid: ea1ea5e43c81c7b4285ed77336f302bf
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 625f186215c104763be7675aa2d941aa, type: 3}
useAsTemplate: 0
exposeTemplateAsShader: 0
template:
name:
category:
description:
icon: {instanceID: 0}
thumbnail: {instanceID: 0}

View File

@ -1,6 +1,8 @@
<Solution>
<Project Path="MCPForUnity.Runtime.csproj" />
<Project Path="VFXGraph.OutputEventHandlers.csproj" />
<Project Path="Assembly-CSharp.csproj" />
<Project Path="MCPForUnity.Editor.csproj" />
<Project Path="Assembly-CSharp-Editor.csproj" />
<Project Path="VFXGraph.OutputEventHandlers.Editor.csproj" />
</Solution>

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 31e7fac5858840340a75cc6df0ad3d9e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MCPForUnityTests.EditMode")]

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be61633e00d934610ac1ff8192ffbe3d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c9d47f01d06964ee7843765d1bd71205
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 59ff83375c2c74c8385c4a22549778dd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Models;
using UnityEditor;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class AntigravityConfigurator : JsonFileMcpConfigurator
{
public AntigravityConfigurator() : base(new McpClient
{
name = "Antigravity",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gemini", "antigravity", "mcp_config.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gemini", "antigravity", "mcp_config.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gemini", "antigravity", "mcp_config.json"),
HttpUrlProperty = "serverUrl",
DefaultUnityFields = { { "disabled", false } },
StripEnvWhenNotRequired = true
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Antigravity",
"Click the more_horiz menu in the Agent pane > MCP Servers",
"Select 'Install' for Unity MCP or use the Configure button above",
"Restart Antigravity if necessary"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 331b33961513042e3945d0a1d06615b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Models;
using MCPForUnity.Editor.Services;
using UnityEditor;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class CherryStudioConfigurator : JsonFileMcpConfigurator
{
public const string ClientName = "Cherry Studio";
public CherryStudioConfigurator() : base(new McpClient
{
name = ClientName,
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Cherry Studio", "config"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Cherry Studio", "config"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Cherry Studio", "config"),
SupportsHttpTransport = false
})
{ }
public override bool SupportsAutoConfigure => false;
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Cherry Studio",
"Go to Settings (⚙️) → MCP Server",
"Click 'Add Server' button",
"For STDIO mode (recommended):",
" - Name: unity-mcp",
" - Type: STDIO",
" - Command: uvx",
" - Arguments: Copy from the Manual Configuration JSON below",
"Click Save and restart Cherry Studio",
"",
"Note: Cherry Studio uses UI-based configuration.",
"Use the manual snippet below as reference for the values to enter."
};
public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
{
client.SetStatus(McpStatus.NotConfigured, "Cherry Studio requires manual UI configuration");
return client.status;
}
public override void Configure()
{
throw new InvalidOperationException(
"Cherry Studio uses UI-based configuration. " +
"Please use the Manual Configuration snippet and Installation Steps to configure manually."
);
}
public override string GetManualSnippet()
{
bool useHttp = EditorConfigurationCache.Instance.UseHttpTransport;
if (useHttp)
{
return "# Cherry Studio does not support WebSocket transport.\n" +
"# Cherry Studio supports STDIO and SSE transports.\n" +
"# \n" +
"# To use Cherry Studio:\n" +
"# 1. Switch transport to 'Stdio' in Advanced Settings below\n" +
"# 2. Return to this configuration screen\n" +
"# 3. Copy the STDIO configuration snippet that will appear\n" +
"# \n" +
"# OPTION 2: SSE mode (future support)\n" +
"# Note: Unity MCP does not currently have an SSE endpoint.\n" +
"# This may be added in a future update.";
}
return base.GetManualSnippet() + "\n\n" +
"# Cherry Studio Configuration Instructions:\n" +
"# Cherry Studio uses UI-based configuration, not a JSON file.\n" +
"# \n" +
"# To configure:\n" +
"# 1. Open Cherry Studio\n" +
"# 2. Go to Settings (⚙️) → MCP Server\n" +
"# 3. Click 'Add Server'\n" +
"# 4. Enter the following values from the JSON above:\n" +
"# - Name: unity-mcp\n" +
"# - Type: STDIO\n" +
"# - Command: (copy 'command' value from JSON)\n" +
"# - Arguments: (copy 'args' array values, space-separated or as individual entries)\n" +
"# - Active: true\n" +
"# 5. Click Save\n" +
"# 6. Restart Cherry Studio";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6de06c6bb0399154d840a1e4c84be869
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
/// <summary>
/// Claude Code configurator using the CLI-based registration (claude mcp add/remove).
/// This integrates with Claude Code's native MCP management.
/// </summary>
public class ClaudeCodeConfigurator : ClaudeCliMcpConfigurator
{
public ClaudeCodeConfigurator() : base(new McpClient
{
name = "Claude Code",
SupportsHttpTransport = true,
})
{ }
public override bool SupportsSkills => true;
public override string GetSkillInstallPath()
{
var userHome = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
return Path.Combine(userHome, ".claude", "skills", "unity-mcp-skill");
}
public override IList<string> GetInstallationSteps() => new List<string>
{
"Ensure Claude CLI is installed (comes with Claude Code)",
"Click Configure to add UnityMCP via 'claude mcp add'",
"The server will be automatically available in Claude Code",
"Use Unregister to remove via 'claude mcp remove'"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0d22681fc594475db1c189f2d9abdf7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Models;
using MCPForUnity.Editor.Services;
using UnityEditor;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class ClaudeDesktopConfigurator : JsonFileMcpConfigurator
{
public const string ClientName = "Claude Desktop";
public ClaudeDesktopConfigurator() : base(new McpClient
{
name = ClientName,
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Claude", "claude_desktop_config.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Claude", "claude_desktop_config.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Claude", "claude_desktop_config.json"),
SupportsHttpTransport = false,
StripEnvWhenNotRequired = true
})
{ }
public override bool SupportsSkills => true;
public override string GetSkillInstallPath()
{
var userHome = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
return Path.Combine(userHome, ".claude", "skills", "unity-mcp-skill");
}
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Claude Desktop",
"Go to Settings > Developer > Edit Config\nOR open the config path",
"Paste the configuration JSON",
"Save and restart Claude Desktop"
};
public override void Configure()
{
bool useHttp = EditorConfigurationCache.Instance.UseHttpTransport;
if (useHttp)
{
throw new InvalidOperationException("Claude Desktop does not support HTTP transport. Switch to stdio in settings before configuring.");
}
base.Configure();
}
public override string GetManualSnippet()
{
bool useHttp = EditorConfigurationCache.Instance.UseHttpTransport;
if (useHttp)
{
return "# Claude Desktop does not support HTTP transport.\n" +
"# In Connect tab, change the Transport option from HTTP to stdio, then regenerate.";
}
return base.GetManualSnippet();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d5e5d87c9db57495f842dc366f1ebd65
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class ClineConfigurator : JsonFileMcpConfigurator
{
public ClineConfigurator() : base(new McpClient
{
name = "Cline",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
DefaultUnityFields = { { "disabled", false }, { "autoApprove", new object[] { } } }
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Cline in VS Code",
"Click the MCP Servers icon in the Cline pane",
"Go to Configure tab and click 'Configure MCP Servers'\nOR open the config file at the path above",
"Paste the configuration JSON into the mcpServers object",
"Save and restart VS Code"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b8abf0951c7413d9ff97a053b0adf2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
/// <summary>
/// Configures the CodeBuddy CLI (~/.codebuddy.json) MCP settings.
/// </summary>
public class CodeBuddyCliConfigurator : JsonFileMcpConfigurator
{
public CodeBuddyCliConfigurator() : base(new McpClient
{
name = "CodeBuddy CLI",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codebuddy.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codebuddy.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codebuddy.json"),
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install CodeBuddy CLI and ensure '~/.codebuddy.json' exists",
"Click Configure to add the UnityMCP entry (or manually edit the file above)",
"Restart your CLI session if needed"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 923728a98c8c74cfaa6e9203c408f34e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class CodexConfigurator : CodexMcpConfigurator
{
public CodexConfigurator() : base(new McpClient
{
name = "Codex",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codex", "config.toml"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codex", "config.toml"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codex", "config.toml")
})
{ }
public override bool SupportsSkills => true;
public override string GetSkillInstallPath()
{
var userHome = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
return Path.Combine(userHome, ".codex", "skills", "unity-mcp-skill");
}
public override IList<string> GetInstallationSteps() => new List<string>
{
"Run 'codex config edit' in a terminal\nOR open the config file at the path above",
"Paste the configuration TOML",
"Save and restart Codex"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7037ef8b168e49f79247cb31c3be75a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class CopilotCliConfigurator : JsonFileMcpConfigurator
{
public CopilotCliConfigurator() : base(new McpClient
{
name = "GitHub Copilot CLI",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".copilot", "mcp-config.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".copilot", "mcp-config.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".copilot", "mcp-config.json")
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install GitHub Copilot CLI (https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli)",
"Open or create mcp-config.json at the path above",
"Paste the configuration JSON (or use /mcp add in the CLI)",
"Restart your Copilot CLI session"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 14a4b9a7f749248d496466c2a3a53e56
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class CursorConfigurator : JsonFileMcpConfigurator
{
public CursorConfigurator() : base(new McpClient
{
name = "Cursor",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cursor", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cursor", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cursor", "mcp.json")
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Cursor",
"Go to File > Preferences > Cursor Settings > MCP > Add new global MCP server\nOR open the config file at the path above",
"Paste the configuration JSON",
"Save and restart Cursor"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b708eda314746481fb8f4a1fb0652b03
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Models;
using UnityEditor;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class GeminiCliConfigurator : JsonFileMcpConfigurator
{
public GeminiCliConfigurator() : base(new McpClient
{
name = "Gemini CLI",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gemini", "settings.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gemini", "settings.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gemini", "settings.json"),
HttpUrlProperty = "httpUrl",
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Ensure Gemini CLI is installed (see https://geminicli.com/docs/get-started/installation/)",
"Click Register to add UnityMCP via 'gemini mcp add'",
"The server will be automatically available in Gemini CLI",
"Use Unregister to remove via 'gemini mcp remove'"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c5e9bbb45e552453ab5cb557a22d43e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class KiloCodeConfigurator : JsonFileMcpConfigurator
{
public KiloCodeConfigurator() : base(new McpClient
{
name = "Kilo Code",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Code", "User", "globalStorage", "kilocode.kilo-code", "settings", "mcp_settings.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Code", "User", "globalStorage", "kilocode.kilo-code", "settings", "mcp_settings.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Code", "User", "globalStorage", "kilocode.kilo-code", "settings", "mcp_settings.json"),
IsVsCodeLayout = false
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install Kilo Code extension in VS Code",
"Open Kilo Code settings (gear icon in sidebar)",
"Navigate to MCP Servers section and click 'Edit Global MCP Settings'\nOR open the config file at the path above",
"Paste the configuration JSON into the mcpServers object",
"Save and restart VS Code"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3286d62ffe5644f5ea60488fd7e6513d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class KiroConfigurator : JsonFileMcpConfigurator
{
public KiroConfigurator() : base(new McpClient
{
name = "Kiro",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".kiro", "settings", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".kiro", "settings", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".kiro", "settings", "mcp.json"),
EnsureEnvObject = true,
DefaultUnityFields = { { "disabled", false } }
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Kiro",
"Go to File > Settings > Settings > Search for \"MCP\" > Open Workspace MCP Config\nOR open the config file at the path above",
"Paste the configuration JSON",
"Save and restart Kiro"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e9b73ff071a6043dda1f2ec7d682ef71
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,398 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Models;
using MCPForUnity.Editor.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
namespace MCPForUnity.Editor.Clients.Configurators
{
/// <summary>
/// Configurator for OpenClaw via the openclaw-mcp-bridge plugin.
/// OpenClaw stores config at ~/.openclaw/openclaw.json.
/// </summary>
public class OpenClawConfigurator : McpClientConfiguratorBase
{
private const string PluginName = "openclaw-mcp-bridge";
private const string ServerName = "unityMCP";
private const string HttpTransportName = "http";
private const string StdioTransportName = "stdio";
private const string StdioUrl = "stdio://local";
public OpenClawConfigurator() : base(new McpClient
{
name = "OpenClaw",
windowsConfigPath = BuildConfigPath(),
macConfigPath = BuildConfigPath(),
linuxConfigPath = BuildConfigPath()
})
{ }
private static string BuildConfigPath()
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".openclaw",
"openclaw.json");
}
public override string GetConfigPath() => CurrentOsPath();
public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
{
try
{
string path = GetConfigPath();
if (!File.Exists(path))
{
client.SetStatus(McpStatus.NotConfigured);
client.configuredTransport = ConfiguredTransport.Unknown;
return client.status;
}
JObject root = LoadConfig(path);
JObject pluginEntry = root["plugins"]?["entries"]?[PluginName] as JObject;
JObject unityServer = FindUnityServer(pluginEntry?["config"]?["servers"]);
if (pluginEntry == null || unityServer == null)
{
client.SetStatus(McpStatus.MissingConfig);
client.configuredTransport = ConfiguredTransport.Unknown;
return client.status;
}
if (!IsEnabled(pluginEntry) || !IsEnabled(unityServer))
{
client.SetStatus(McpStatus.NotConfigured);
client.configuredTransport = ConfiguredTransport.Unknown;
return client.status;
}
bool matches = ServerMatchesCurrentEndpoint(unityServer);
if (matches)
{
client.SetStatus(McpStatus.Configured);
client.configuredTransport = ResolveTransport(unityServer);
return client.status;
}
if (attemptAutoRewrite)
{
Configure();
}
else
{
client.SetStatus(McpStatus.IncorrectPath);
client.configuredTransport = ConfiguredTransport.Unknown;
}
}
catch (Exception ex)
{
client.SetStatus(McpStatus.Error, ex.Message);
client.configuredTransport = ConfiguredTransport.Unknown;
}
return client.status;
}
public override void Configure()
{
if (EditorPrefs.GetBool(EditorPrefKeys.LockCursorConfig, false))
return;
string path = GetConfigPath();
McpConfigurationHelper.EnsureConfigDirectoryExists(path);
JObject root = File.Exists(path) ? LoadConfig(path) : new JObject();
JObject plugins = root["plugins"] as JObject ?? new JObject();
root["plugins"] = plugins;
JObject entries = plugins["entries"] as JObject ?? new JObject();
plugins["entries"] = entries;
JObject pluginEntry = entries[PluginName] as JObject ?? new JObject();
entries[PluginName] = pluginEntry;
pluginEntry["enabled"] = true;
JObject pluginConfig = pluginEntry["config"] as JObject ?? new JObject();
pluginEntry["config"] = pluginConfig;
pluginConfig.Remove("timeout"); // removed in openclaw-mcp-bridge v2+
pluginConfig.Remove("retries"); // removed in openclaw-mcp-bridge v2+
pluginConfig["servers"] = UpsertUnityServer(pluginConfig["servers"]);
McpConfigurationHelper.WriteAtomicFile(path, root.ToString(Formatting.Indented));
client.SetStatus(McpStatus.Configured);
client.configuredTransport = HttpEndpointUtility.GetCurrentServerTransport();
}
public override string GetManualSnippet()
{
JObject snippet = new JObject
{
["plugins"] = new JObject
{
["entries"] = new JObject
{
[PluginName] = new JObject
{
["enabled"] = true,
["config"] = new JObject
{
["servers"] = new JObject
{
[ServerName] = BuildUnityServerEntry()
}
}
}
}
}
};
return snippet.ToString(Formatting.Indented);
}
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install OpenClaw",
"Install the bridge plugin: npm install -g openclaw-mcp-bridge (or pnpm add -g openclaw-mcp-bridge)",
"In MCP for Unity, choose OpenClaw and click Configure",
"OpenClaw uses the currently selected MCP for Unity transport (HTTP or stdio)",
"OpenClaw exposes a proxy tool such as unityMCP__call for Unity MCP access",
"Restart OpenClaw if the plugin does not hot-reload the new config"
};
private JObject LoadConfig(string path)
{
string text = File.ReadAllText(path);
if (string.IsNullOrWhiteSpace(text))
{
return new JObject();
}
try
{
return JsonConvert.DeserializeObject<JObject>(text) ?? new JObject();
}
catch (JsonException ex)
{
throw new InvalidOperationException(
$"OpenClaw config contains non-JSON content and cannot be safely auto-edited: {ex.Message}");
}
}
private JObject FindUnityServer(JToken serversToken)
{
if (serversToken is JObject serverMap)
{
return serverMap[ServerName] as JObject;
}
if (serversToken is JArray legacyServers)
{
foreach (JToken token in legacyServers)
{
JObject server = token as JObject;
if (server == null)
{
continue;
}
string name = server["name"]?.ToString();
if (string.Equals(name, ServerName, StringComparison.OrdinalIgnoreCase))
{
return server;
}
}
}
return null;
}
private JObject UpsertUnityServer(JToken serversToken)
{
JObject servers = NormalizeServers(serversToken);
JObject entry = servers[ServerName] as JObject ?? new JObject();
JObject desiredEntry = BuildUnityServerEntry();
entry.Remove("name");
entry.Remove("prefix");
entry.Remove("healthCheck");
entry.Remove("command");
entry.Remove("args");
entry.Remove("env");
entry.Remove("connectTimeoutMs");
foreach (var property in desiredEntry.Properties())
{
entry[property.Name] = property.Value.DeepClone();
}
servers[ServerName] = entry;
return servers;
}
private static JObject NormalizeServers(JToken serversToken)
{
if (serversToken is JObject serverMap)
{
return serverMap;
}
var normalized = new JObject();
if (!(serversToken is JArray legacyServers))
{
return normalized;
}
foreach (JToken token in legacyServers)
{
if (!(token is JObject legacyServer))
{
continue;
}
string name = legacyServer["name"]?.ToString();
if (string.IsNullOrWhiteSpace(name))
{
continue;
}
normalized[name] = legacyServer;
}
return normalized;
}
private static JObject BuildUnityServerEntry()
{
ConfiguredTransport transport = HttpEndpointUtility.GetCurrentServerTransport();
if (transport == ConfiguredTransport.Stdio)
{
var (uvxPath, _, packageName) = AssetPathUtility.GetUvxCommandParts();
if (string.IsNullOrWhiteSpace(uvxPath))
{
throw new InvalidOperationException("uvx not found. Install uv/uvx or set the override in Advanced Settings.");
}
var args = new JArray();
foreach (string value in AssetPathUtility.GetUvxDevFlagsList())
{
args.Add(value);
}
foreach (string value in AssetPathUtility.GetBetaServerFromArgsList())
{
args.Add(value);
}
args.Add(packageName);
args.Add("--transport");
args.Add("stdio");
return new JObject
{
["enabled"] = true,
["url"] = StdioUrl,
["transport"] = StdioTransportName,
["command"] = uvxPath,
["args"] = args,
["toolPrefix"] = ServerName,
["requestTimeoutMs"] = 60000,
["connectTimeoutMs"] = 15000
};
}
return new JObject
{
["enabled"] = true,
["url"] = HttpEndpointUtility.GetMcpRpcUrl(),
["transport"] = HttpTransportName,
["toolPrefix"] = ServerName,
["requestTimeoutMs"] = 30000
};
}
private bool ServerMatchesCurrentEndpoint(JObject server)
{
if (server == null)
{
return false;
}
ConfiguredTransport expectedTransport = HttpEndpointUtility.GetCurrentServerTransport();
ConfiguredTransport configuredTransport = ResolveTransport(server);
if (configuredTransport != expectedTransport)
{
return false;
}
if (configuredTransport == ConfiguredTransport.Stdio)
{
string configuredUrl = server["url"]?.ToString();
string command = server["command"]?.ToString();
if (!UrlsEqual(configuredUrl, StdioUrl) || string.IsNullOrWhiteSpace(command))
{
return false;
}
// Validate the --from package source hasn't drifted (e.g. stable vs prerelease switch)
string[] args = (server["args"] as JArray)?.ToObject<string[]>();
string configuredSource = McpConfigurationHelper.ExtractUvxUrl(args);
string expectedSource = GetExpectedPackageSourceForValidation();
if (!string.IsNullOrEmpty(configuredSource) && !string.IsNullOrEmpty(expectedSource) &&
!McpConfigurationHelper.PathsEqual(configuredSource, expectedSource))
{
return false;
}
}
else
{
string configuredUrl = server["url"]?.ToString();
if (string.IsNullOrWhiteSpace(configuredUrl) ||
(!UrlsEqual(configuredUrl, HttpEndpointUtility.GetLocalMcpRpcUrl()) &&
!UrlsEqual(configuredUrl, HttpEndpointUtility.GetRemoteMcpRpcUrl())))
{
return false;
}
}
string toolPrefix = server["toolPrefix"]?.ToString();
return string.IsNullOrWhiteSpace(toolPrefix) ||
string.Equals(toolPrefix, ServerName, StringComparison.OrdinalIgnoreCase);
}
private static bool IsEnabled(JObject entry)
{
JToken enabledToken = entry["enabled"];
return enabledToken == null || enabledToken.Type != JTokenType.Boolean || enabledToken.Value<bool>();
}
private ConfiguredTransport ResolveTransport(JObject server)
{
string configuredTransport = server?["transport"]?.ToString();
string configuredUrl = server?["url"]?.ToString();
if (string.Equals(configuredTransport, StdioTransportName, StringComparison.OrdinalIgnoreCase) ||
UrlsEqual(configuredUrl, StdioUrl))
{
return ConfiguredTransport.Stdio;
}
if (UrlsEqual(configuredUrl, HttpEndpointUtility.GetRemoteMcpRpcUrl()))
{
return ConfiguredTransport.HttpRemote;
}
if (UrlsEqual(configuredUrl, HttpEndpointUtility.GetLocalMcpRpcUrl()))
{
return ConfiguredTransport.Http;
}
return ConfiguredTransport.Unknown;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19f29c88761345158fc766d24e7c18f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,224 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace MCPForUnity.Editor.Clients.Configurators
{
/// <summary>
/// Configurator for OpenCode (opencode.ai) - a Go-based terminal AI coding assistant.
/// OpenCode uses ~/.config/opencode/opencode.json with a custom "mcp" format.
/// </summary>
public class OpenCodeConfigurator : McpClientConfiguratorBase
{
private const string ServerName = "unityMCP";
private const string SchemaUrl = "https://opencode.ai/config.json";
private const string RemoteType = "remote";
private const string LocalType = "local";
public OpenCodeConfigurator() : base(new McpClient
{
name = "OpenCode",
windowsConfigPath = BuildConfigPath(),
macConfigPath = BuildConfigPath(),
linuxConfigPath = BuildConfigPath()
})
{ }
private static string BuildConfigPath()
{
string xdgConfigHome = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME");
string configBase = !string.IsNullOrEmpty(xdgConfigHome)
? xdgConfigHome
: Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config");
return Path.Combine(configBase, "opencode", "opencode.json");
}
public override string GetConfigPath() => CurrentOsPath();
/// <summary>
/// Attempts to load and parse the config file.
/// Returns null if file doesn't exist or cannot be read.
/// Returns parsed JObject if valid JSON found.
/// Logs warning if file exists but contains malformed JSON.
/// </summary>
private JObject TryLoadConfig(string path)
{
if (!File.Exists(path))
return null;
string content;
try
{
content = File.ReadAllText(path);
}
catch (Exception ex)
{
UnityEngine.Debug.LogWarning($"[OpenCodeConfigurator] Failed to read config file {path}: {ex.Message}");
return null;
}
try
{
return JsonConvert.DeserializeObject<JObject>(content) ?? new JObject();
}
catch (JsonException ex)
{
// Malformed JSON - log warning and return null.
// When Configure() receives null, it will do: TryLoadConfig(path) ?? new JObject()
// This creates a fresh empty JObject, which replaces the entire file with only the unityMCP section.
// Existing config sections are lost. To preserve sections, a different recovery strategy
// (e.g., line-by-line parsing, JSON repair, or manual user intervention) would be needed.
UnityEngine.Debug.LogWarning($"[OpenCodeConfigurator] Malformed JSON in {path}: {ex.Message}");
return null;
}
}
public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
{
try
{
string path = GetConfigPath();
var config = TryLoadConfig(path);
if (config == null)
{
client.SetStatus(McpStatus.NotConfigured);
return client.status;
}
var unityMcp = config["mcp"]?[ServerName] as JObject;
if (unityMcp == null)
{
client.SetStatus(McpStatus.NotConfigured);
return client.status;
}
if (EntryMatchesCurrentTransport(unityMcp))
{
client.SetStatus(McpStatus.Configured);
}
else if (attemptAutoRewrite)
{
Configure();
}
else
{
client.SetStatus(McpStatus.IncorrectPath);
}
}
catch (Exception ex)
{
client.SetStatus(McpStatus.Error, ex.Message);
}
return client.status;
}
public override void Configure()
{
try
{
string path = GetConfigPath();
McpConfigurationHelper.EnsureConfigDirectoryExists(path);
// Load existing config or start fresh, preserving all other properties and MCP servers
var config = TryLoadConfig(path) ?? new JObject();
// Only add $schema if creating a new file
if (!File.Exists(path))
{
config["$schema"] = SchemaUrl;
}
// Preserve existing mcp section and only update our server entry
var mcpSection = config["mcp"] as JObject ?? new JObject();
config["mcp"] = mcpSection;
mcpSection[ServerName] = BuildServerEntry();
McpConfigurationHelper.WriteAtomicFile(path, JsonConvert.SerializeObject(config, Formatting.Indented));
client.SetStatus(McpStatus.Configured);
}
catch (Exception ex)
{
client.SetStatus(McpStatus.Error, ex.Message);
}
}
public override string GetManualSnippet()
{
var snippet = new JObject
{
["mcp"] = new JObject { [ServerName] = BuildServerEntry() }
};
return JsonConvert.SerializeObject(snippet, Formatting.Indented);
}
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install OpenCode (https://opencode.ai)",
"Click Configure to add Unity MCP to ~/.config/opencode/opencode.json",
"Restart OpenCode",
"The Unity MCP server should be detected automatically"
};
private static JObject BuildServerEntry()
{
if (HttpEndpointUtility.GetCurrentServerTransport() == ConfiguredTransport.Stdio)
{
var (uvxPath, _, packageName) = AssetPathUtility.GetUvxCommandParts();
if (string.IsNullOrWhiteSpace(uvxPath))
{
throw new InvalidOperationException("uvx not found. Install uv/uvx or set the override in Advanced Settings.");
}
var command = new JArray { uvxPath };
foreach (string value in AssetPathUtility.GetUvxDevFlagsList())
{
command.Add(value);
}
foreach (string value in AssetPathUtility.GetBetaServerFromArgsList())
{
command.Add(value);
}
command.Add(packageName);
command.Add("--transport");
command.Add("stdio");
return new JObject
{
["type"] = LocalType,
["command"] = command,
["enabled"] = true
};
}
return new JObject
{
["type"] = RemoteType,
["url"] = HttpEndpointUtility.GetMcpRpcUrl(),
["enabled"] = true
};
}
private bool EntryMatchesCurrentTransport(JObject entry)
{
string entryType = entry["type"]?.ToString();
ConfiguredTransport expected = HttpEndpointUtility.GetCurrentServerTransport();
if (expected == ConfiguredTransport.Stdio)
{
return string.Equals(entryType, LocalType, StringComparison.OrdinalIgnoreCase)
&& entry["command"] is JArray;
}
return string.Equals(entryType, RemoteType, StringComparison.OrdinalIgnoreCase)
&& UrlsEqual(entry["url"]?.ToString(), HttpEndpointUtility.GetMcpRpcUrl());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 489f99ffb7e6743e88e3203552c8b37b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
/// <summary>
/// Qwen Code MCP client configurator.
/// Qwen Code uses a JSON-based configuration file with mcpServers section.
/// Config path: ~/.qwen/settings.json
///
/// Qwen Code supports both stdio (uvx) and HTTP transport modes.
/// Default: stdio mode (works without Unity Editor for basic operations)
/// HTTP mode: requires Unity Editor running with MCP HTTP server started
/// </summary>
public class QwenCodeConfigurator : JsonFileMcpConfigurator
{
public QwenCodeConfigurator() : base(new McpClient
{
name = "Qwen Code",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".qwen", "settings.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".qwen", "settings.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".qwen", "settings.json"),
SupportsHttpTransport = true,
// Default to stdio transport for Qwen Code (like Cursor)
// User can switch to HTTP in Unity: Window > MCP for Unity > Settings
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Ensure Qwen Code is installed (npm install -g @qwen-code/qwen-code or download from https://github.com/QwenLM/qwen-code)",
"Open Qwen Code",
"Click 'Auto Configure' to automatically add UnityMCP to settings.json",
"OR click 'Manual Setup' to copy the configuration JSON",
"Open ~/.qwen/settings.json and paste the configuration",
"Save and restart Qwen Code",
"Use /mcp command in Qwen Code to verify Unity MCP is connected",
"Note: For full functionality, open Unity Editor and start HTTP server"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46891bcdb00e468cbd04afbfb8f3095e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class RiderConfigurator : JsonFileMcpConfigurator
{
public RiderConfigurator() : base(new McpClient
{
name = "Rider GitHub Copilot",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "github-copilot", "intellij", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "github-copilot", "intellij", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "github-copilot", "intellij", "mcp.json"),
IsVsCodeLayout = true
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install GitHub Copilot plugin in Rider",
"Open or create mcp.json at the path above",
"Paste the configuration JSON",
"Save and restart Rider"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2511b0d05271d486bb61f8cc9fd11363
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class TraeConfigurator : JsonFileMcpConfigurator
{
public TraeConfigurator() : base(new McpClient
{
name = "Trae",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Trae", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Trae", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Trae", "mcp.json"),
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Trae and go to Settings > MCP",
"Select Add Server > Add Manually",
"Paste the JSON or point to the mcp.json file\n"+
"Windows: %AppData%\\Trae\\mcp.json\n" +
"macOS: ~/Library/Application Support/Trae/mcp.json\n" +
"Linux: ~/.config/Trae/mcp.json\n",
"Save and restart Trae"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3ab39e22ae0948ab94beae307f9902e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class VSCodeConfigurator : JsonFileMcpConfigurator
{
public VSCodeConfigurator() : base(new McpClient
{
name = "VSCode GitHub Copilot",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Code", "User", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Code", "User", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Code", "User", "mcp.json"),
IsVsCodeLayout = true
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install GitHub Copilot extension",
"Open or create mcp.json at the path above",
"Paste the configuration JSON",
"Save and restart VSCode"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bcc7ead475a4d4ea2978151c217757b8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class VSCodeInsidersConfigurator : JsonFileMcpConfigurator
{
public VSCodeInsidersConfigurator() : base(new McpClient
{
name = "VSCode Insiders GitHub Copilot",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Code - Insiders", "User", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Code - Insiders", "User", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Code - Insiders", "User", "mcp.json"),
IsVsCodeLayout = true
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Install GitHub Copilot extension in VS Code Insiders",
"Open or create mcp.json at the path above",
"Paste the configuration JSON",
"Save and restart VS Code Insiders"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c4a1b0d3b34489cbf0f8c40c49c4f3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients.Configurators
{
public class WindsurfConfigurator : JsonFileMcpConfigurator
{
public WindsurfConfigurator() : base(new McpClient
{
name = "Windsurf",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codeium", "windsurf", "mcp_config.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codeium", "windsurf", "mcp_config.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codeium", "windsurf", "mcp_config.json"),
HttpUrlProperty = "serverUrl",
DefaultUnityFields = { { "disabled", false } },
StripEnvWhenNotRequired = true
})
{ }
public override IList<string> GetInstallationSteps() => new List<string>
{
"Open Windsurf",
"Go to File > Preferences > Windsurf Settings > MCP > Manage MCPs > View raw config\nOR open the config file at the path above",
"Paste the configuration JSON",
"Save and restart Windsurf"
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b528971e189f141d38db577f155bd222
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Clients
{
/// <summary>
/// Contract for MCP client configurators. Each client is responsible for
/// status detection, auto-configure, and manual snippet/steps.
/// </summary>
public interface IMcpClientConfigurator
{
/// <summary>Stable identifier (e.g., "cursor").</summary>
string Id { get; }
/// <summary>Display name shown in the UI.</summary>
string DisplayName { get; }
/// <summary>Current status cached by the configurator.</summary>
McpStatus Status { get; }
/// <summary>
/// The transport type the client is currently configured for.
/// Returns Unknown if the client is not configured or the transport cannot be determined.
/// </summary>
ConfiguredTransport ConfiguredTransport { get; }
/// <summary>True if this client supports auto-configure.</summary>
bool SupportsAutoConfigure { get; }
/// <summary>Label to show on the configure button for the current state.</summary>
string GetConfigureActionLabel();
/// <summary>Returns the platform-specific config path (or message for CLI-managed clients).</summary>
string GetConfigPath();
/// <summary>Checks and updates status; returns current status.</summary>
McpStatus CheckStatus(bool attemptAutoRewrite = true);
/// <summary>Runs auto-configuration (register/write file/CLI etc.).</summary>
void Configure();
/// <summary>Returns the manual configuration snippet (JSON/TOML/commands).</summary>
string GetManualSnippet();
/// <summary>Returns ordered human-readable installation steps.</summary>
System.Collections.Generic.IList<string> GetInstallationSteps();
/// <summary>True if this client supports skill installation/sync.</summary>
bool SupportsSkills { get; }
/// <summary>Returns the absolute path where skills should be installed, or null if unsupported.</summary>
string GetSkillInstallPath();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5a5078d9e6e14027a1abfebf4018634
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d408fd7733cb4a1eb80f785307db2ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MCPForUnity.Editor.Helpers;
using UnityEditor;
using UnityEngine;
namespace MCPForUnity.Editor.Clients
{
/// <summary>
/// Central registry that auto-discovers configurators via TypeCache.
/// </summary>
public static class McpClientRegistry
{
private static List<IMcpClientConfigurator> cached;
public static IReadOnlyList<IMcpClientConfigurator> All
{
get
{
if (cached == null)
{
cached = BuildRegistry();
}
return cached;
}
}
private static List<IMcpClientConfigurator> BuildRegistry()
{
var configurators = new List<IMcpClientConfigurator>();
foreach (var type in TypeCache.GetTypesDerivedFrom<IMcpClientConfigurator>())
{
if (type.IsAbstract || !type.IsClass || !type.IsPublic)
continue;
// Require a public parameterless constructor
if (type.GetConstructor(Type.EmptyTypes) == null)
continue;
try
{
if (Activator.CreateInstance(type) is IMcpClientConfigurator instance)
{
configurators.Add(instance);
}
}
catch (Exception ex)
{
McpLog.Warn($"UnityMCP: Failed to instantiate configurator {type.Name}: {ex.Message}");
}
}
// Alphabetical order by display name
configurators = configurators.OrderBy(c => c.DisplayName, StringComparer.OrdinalIgnoreCase).ToList();
return configurators;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ce08555f995e4e848a826c63f18cb35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f7e009cbf3e74f6c987331c2b438ec59
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More