123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- using System;
- using System.IO;
- using JetBrains.Annotations;
- using LLM.Editor.Core;
- using LLM.Editor.Helper;
- using UnityEditor;
- using UnityEngine;
- using Object = UnityEngine.Object;
- namespace LLM.Editor.Commands
- {
- public enum AssetType
- {
- Folder,
- Text,
- Script,
- Material,
- AssemblyDefinition,
- Prefab
- }
- [Serializable]
- public class CreateAssetParams
- {
- public AssetType assetType;
- public string assetName;
- public string logicalName; // A unique name for the LLM to reference this object later
- public string path; // Relative to Assets folder
- public string content;
- public string sourceAssetGuid; // Optional, for creating prefabs from models
- public string sourceGameObjectIdentifier; // Optional, for creating prefabs from scene objects
- public string shaderName; // Optional, for creating materials
- }
- [UsedImplicitly]
- public class CreateAssetCommand : ICommand
- {
- private readonly CreateAssetParams _params;
- public CreateAssetCommand(string jsonParams)
- {
- _params = jsonParams?.FromJson<CreateAssetParams>();
- }
- public CommandOutcome Execute(Data.CommandContext context)
- {
- if (_params == null || string.IsNullOrEmpty(_params.assetName))
- {
- context.ErrorMessage = "Invalid parameters: assetName is required.";
- return CommandOutcome.Error;
- }
-
- var relativePath = _params.path?.Replace("Assets/", string.Empty) ?? "";
- var fullPath = Path.Combine(Application.dataPath, relativePath, _params.assetName);
- var assetPathForDb = $"Assets/{Path.Combine(relativePath, _params.assetName)}";
- var dirName = Path.GetDirectoryName(fullPath);
- if (!string.IsNullOrEmpty(dirName) && !Directory.Exists(dirName))
- {
- Directory.CreateDirectory(dirName);
- }
- try
- {
- switch (_params.assetType)
- {
- case AssetType.Folder:
- Directory.CreateDirectory(fullPath);
- assetPathForDb += "/";
- break;
- case AssetType.Text:
- File.WriteAllText(fullPath, _params.content ?? "");
- break;
- case AssetType.Script:
- assetPathForDb += ".cs";
- fullPath += ".cs";
- File.WriteAllText(fullPath, _params.content ?? "using UnityEngine;\n\npublic class NewScript : MonoBehaviour\n{\n}\n");
- break;
- case AssetType.Material:
- assetPathForDb += ".mat";
- var shader = string.IsNullOrEmpty(_params.shaderName) ? Shader.Find("Standard") : Shader.Find(_params.shaderName);
- if (shader == null)
- {
- Debug.LogWarning($"[CreateAssetCommand] Shader '{_params.shaderName}' not found. Falling back to Standard.");
- shader = Shader.Find("Standard");
- }
- var material = new Material(shader);
- AssetDatabase.CreateAsset(material, assetPathForDb);
- break;
- case AssetType.AssemblyDefinition:
- assetPathForDb += ".asmdef";
- var asmdefContent = _params.content ?? $"{{\n \"name\": \"{_params.assetName}\"\n}}";
- File.WriteAllText(assetPathForDb, asmdefContent);
- break;
- case AssetType.Prefab:
- assetPathForDb += ".prefab";
- CreatePrefab(context, assetPathForDb);
- break;
- default:
- context.ErrorMessage = $"Unsupported asset type: {_params.assetType}";
- return CommandOutcome.Error;
- }
-
- AssetDatabase.Refresh();
- // Folders don't have GUIDs in the same way assets do, so we handle them differently.
- // Their path is their identifier.
- if (_params.assetType == AssetType.Folder)
- {
- var logicalNameForFolder = !string.IsNullOrEmpty(_params.logicalName) ? _params.logicalName : $"{_params.assetName}_{Guid.NewGuid():N}";
- context.IdentifierMap[logicalNameForFolder] = assetPathForDb;
- SessionManager.SaveIdentifierMap(context.IdentifierMap);
- context.CurrentSubject = logicalNameForFolder;
- Debug.Log($"[CreateAssetCommand] Successfully created folder '{_params.assetName}' and mapped it to logical name '{logicalNameForFolder}'.");
- return CommandOutcome.Success;
- }
-
- // For all other assets, we require a valid GUID.
- var assetGuid = AssetDatabase.AssetPathToGUID(assetPathForDb);
- if (string.IsNullOrEmpty(assetGuid))
- {
- AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
- assetGuid = AssetDatabase.AssetPathToGUID(assetPathForDb);
- if (string.IsNullOrEmpty(assetGuid))
- {
- throw new Exception($"Could not get GUID for newly created asset at path: {assetPathForDb}");
- }
- }
-
- var logicalName = !string.IsNullOrEmpty(_params.logicalName) ? _params.logicalName : $"{_params.assetName}_{Guid.NewGuid():N}";
- context.IdentifierMap[logicalName] = assetGuid;
- SessionManager.SaveIdentifierMap(context.IdentifierMap);
-
- context.CurrentSubject = logicalName;
- Debug.Log($"[CreateAssetCommand] Successfully created asset '{_params.assetName}' and mapped it to logical name '{logicalName}'.");
- return CommandOutcome.Success;
- }
- catch (Exception e)
- {
- context.ErrorMessage = $"Failed to create asset '{_params.assetName}'. Exception: {e.Message}";
- Debug.LogException(e);
- return CommandOutcome.Error;
- }
- }
- private void CreatePrefab(Data.CommandContext context, string finalPrefabPath)
- {
- GameObject sourceObject = null;
- if (!string.IsNullOrEmpty(_params.sourceGameObjectIdentifier))
- {
- var resolvedObject = CommandUtility.ResolveIdentifier(context, _params.sourceGameObjectIdentifier);
- if (resolvedObject is GameObject go)
- {
- sourceObject = go;
- }
- else
- {
- throw new Exception($"Could not resolve scene object with logical name: '{_params.sourceGameObjectIdentifier}'");
- }
- }
- else if (!string.IsNullOrEmpty(_params.sourceAssetGuid))
- {
- var sourcePath = AssetDatabase.GUIDToAssetPath(_params.sourceAssetGuid);
- sourceObject = AssetDatabase.LoadAssetAtPath<GameObject>(sourcePath);
- if (sourceObject == null)
- {
- throw new Exception($"Could not find source asset with GUID: {_params.sourceAssetGuid}");
- }
- }
- else
- {
- sourceObject = new GameObject(_params.assetName);
- // Save the new empty object as a prefab, then immediately destroy the instance.
- PrefabUtility.SaveAsPrefabAsset(sourceObject, finalPrefabPath);
- Object.DestroyImmediate(sourceObject);
- return;
- }
-
- // For existing scene objects or assets, save as a prefab.
- PrefabUtility.SaveAsPrefabAssetAndConnect(sourceObject, finalPrefabPath, InteractionMode.UserAction);
- }
- }
- }
|