Initialer Upload neues Unity-Projekt

This commit is contained in:
Daniel Ocks
2025-07-03 11:02:29 +02:00
commit 27d6b94b7c
8167 changed files with 1116569 additions and 0 deletions

View File

@ -0,0 +1,367 @@
#if READY_PLAYER_ME
using ReadyPlayerMe.Core;
using ReadyPlayerMe.Core.Editor;
#endif
using System;
using System.IO;
using System.Net;
using System.Text;
using Convai.Scripts.Runtime.Utils;
using Convai.Scripts.Utils;
using Convai.Scripts.Utils.LipSync;
using Newtonsoft.Json;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using Random = UnityEngine.Random;
namespace Convai.Scripts.Editor.Character
{
public class ConvaiCharacterImporter : EditorWindow
{
/// <summary>
/// The color palette used for the character text.
/// </summary>
private static readonly Color[] ColorPalette =
{
new(1f, 0f, 0f), new(0f, 1f, 0f), new(0f, 0f, 1f),
new(1f, 1f, 0f), new(0f, 1f, 1f), new(1f, 0f, 1f),
new(1f, 0.5f, 0f), new(0.5f, 0f, 0.5f), new(0f, 0.5f, 0f),
new(0.5f, 0.5f, 0.5f), new(1f, 0.8f, 0.6f), new(0.6f, 0.8f, 1f),
new(0.8f, 0.6f, 1f), new(1f, 0.6f, 0.8f), new(0.7f, 0.4f, 0f),
new(0f, 0.7f, 0.7f), new(0.7f, 0.7f, 0f), new(0f, 0.7f, 0.4f),
new(0.7f, 0f, 0.2f), new(0.9f, 0.9f, 0.9f)
};
/// <summary>
/// Creates the GUI for the Character Importer window.
/// </summary>
public void CreateGUI()
{
VisualElement root = rootVisualElement;
ScrollView page2 = new();
root.Add(new Label(""));
Image convaiLogoImage = new()
{
image = AssetDatabase.LoadAssetAtPath<Texture>(ConvaiImagesDirectory.CONVAI_LOGO_PATH),
style =
{
height = 100,
paddingBottom = 10,
paddingTop = 10,
paddingRight = 10,
paddingLeft = 10
}
};
root.Add(convaiLogoImage);
Label convaiCharacterIDLabel = new("Enter your Character ID: ")
{
style = { fontSize = 16 }
};
TextField characterIDTextField = new();
Button downloadButton = new(() => DownloadCharacter(characterIDTextField.text))
{
text = "Import!",
style =
{
fontSize = 16,
unityFontStyleAndWeight = FontStyle.Bold,
alignSelf = Align.Center,
paddingBottom = 10,
paddingLeft = 30,
paddingRight = 30,
paddingTop = 10
}
};
Button docsLink = new(() => Application.OpenURL(
"https://docs.convai.com/api-docs/plugins-and-integrations/unity-plugin/importing-a-character-from-convai-playground"))
{
text = "How do I create a character?",
style =
{
alignSelf = Align.Center,
paddingBottom = 5,
paddingLeft = 50,
paddingRight = 50,
paddingTop = 5
}
};
page2.Add(convaiCharacterIDLabel);
page2.Add(new Label(""));
page2.Add(characterIDTextField);
page2.Add(new Label(""));
page2.Add(downloadButton);
page2.Add(new Label(""));
page2.Add(docsLink);
page2.style.marginBottom = 20;
page2.style.marginLeft = 20;
page2.style.marginRight = 20;
page2.style.marginTop = 20;
root.Add(page2);
}
/// <summary>
/// Opens the Character Importer window.
/// </summary>
[MenuItem("Convai/Character Importer", false, 5)]
public static void CharacterImporter()
{
ConvaiCharacterImporter wnd = GetWindow<ConvaiCharacterImporter>();
wnd.titleContent = new GUIContent("Character Importer");
}
/// <summary>
/// Downloads the character from the Convai API and sets up the character in the scene.
/// </summary>
/// <param name="characterID"> The characterID of the character to download.</param>
private async void DownloadCharacter(string characterID)
{
#if READY_PLAYER_ME
if (!ConvaiAPIKeySetup.GetAPIKey(out string apiKey)) return;
GetRequest getRequest = new(characterID);
string stringGetRequest = JsonConvert.SerializeObject(getRequest);
WebRequest request = WebRequest.Create("https://api.convai.com/character/get");
EditorUtility.DisplayProgressBar("Connecting", "Collecting resources...", 0f);
request.Method = "post";
request.ContentType = "application/json";
request.Headers.Add("CONVAI-API-KEY", apiKey);
byte[] jsonBytes = Encoding.UTF8.GetBytes(stringGetRequest);
await using Stream requestStream = await request.GetRequestStreamAsync();
await requestStream.WriteAsync(jsonBytes, 0, jsonBytes.Length);
try
{
using HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
await using Stream streamResponse = response.GetResponseStream();
if (streamResponse == null) return;
using StreamReader reader = new(streamResponse);
string responseContent = await reader.ReadToEndAsync();
GetResponse getResponseContent = JsonConvert.DeserializeObject<GetResponse>(responseContent);
string modelLink = getResponseContent.ModelDetail.ModelLink;
string characterName = getResponseContent.CharacterName.Trim();
AvatarObjectLoader avatarLoader = new()
{
AvatarConfig = Resources.Load<AvatarConfig>("ConvaiRPMAvatarConfig")
};
DirectoryUtility.DefaultAvatarFolder = $"Convai/Characters/Mesh Data/{characterName}";
EditorUtility.DisplayProgressBar("Downloading Character", "Initializing download...", 0f);
avatarLoader.OnProgressChanged += (_, progressArgs) =>
EditorUtility.DisplayProgressBar("Downloading Character", $"Downloading character model {characterName}: {progressArgs.Progress * 100f}%",
progressArgs.Progress);
avatarLoader.OnCompleted += (_, args) =>
{
EditorUtility.ClearProgressBar();
AvatarLoaderSettings avatarLoaderSettings = Resources.Load<AvatarLoaderSettings>("ConvaiAvatarLoaderSettings");
string path =
$"{DirectoryUtility.GetRelativeProjectPath(args.Avatar.name, AvatarCache.GetAvatarConfigurationHash(avatarLoaderSettings.AvatarConfig))}/{args.Avatar.name}";
GameObject avatar = PrefabHelper.CreateAvatarPrefab(args.Metadata, path, avatarConfig: avatarLoaderSettings.AvatarConfig);
SetupCharacter(characterID, characterName, avatar, args);
Debug.Log($"Character '{characterName}' downloaded and set up successfully.");
};
avatarLoader.OnFailed += (_, error) =>
{
EditorUtility.ClearProgressBar();
Debug.LogError($"Failed to download character: {error}");
};
avatarLoader.LoadAvatar(modelLink);
}
catch (WebException e)
{
EditorUtility.ClearProgressBar();
Debug.LogError(e.Message + "\nPlease check if Character ID is correct.");
}
catch (Exception e)
{
EditorUtility.ClearProgressBar();
Debug.LogError(e);
}
#endif
}
#if READY_PLAYER_ME
/// <summary>
/// Sets up the character in the scene with the downloaded character model.
/// </summary>
/// <param name="characterID"> The character ID.</param>
/// <param name="characterName"> The name of the character.</param>
/// <param name="avatar"> The avatar GameObject to set up.</param>
/// <param name="args"> The completion event arguments.</param>
private void SetupCharacter(string characterID, string characterName, GameObject avatar, CompletionEventArgs args)
{
SetupCharacterMetadata(characterName, avatar);
SetupCollision(avatar);
avatar.AddComponent<AudioSource>();
SetupAnimator(args, avatar);
ConvaiNPC convaiNPCComponent = SetupConvaiComponents(characterID, characterName, avatar);
SetupLipsync(avatar);
SetupChatUI(convaiNPCComponent);
PrefabUtility.SaveAsPrefabAsset(avatar, $"Assets/Convai/Characters/Prefabs/{avatar.name}.prefab");
DestroyImmediate(args.Avatar, true);
Selection.activeObject = avatar;
}
#endif
/// <summary>Setups the lipsync.</summary>
/// <param name="avatar">The avatar.</param>
private static void SetupLipsync(GameObject avatar)
{
ConvaiLipSync convaiLipSync = avatar.AddComponent<ConvaiLipSync>();
convaiLipSync.BlendshapeType = ConvaiLipSync.LipSyncBlendshapeType.ARKit;
}
/// <summary>
/// Sets up the metadata for the character.
/// </summary>
/// <param name="characterName">The name of the character.</param>
/// <param name="avatar">The avatar GameObject.</param>
private static void SetupCharacterMetadata(string characterName, GameObject avatar)
{
avatar.tag = "Character";
avatar.name = $"Convai NPC {characterName}";
}
/// <summary>
/// Sets up the chat UI for the character.
/// </summary>
/// <param name="convaiNPCComponent">The ConvaiNPC component.</param>
private void SetupChatUI(ConvaiNPC convaiNPCComponent)
{
ConvaiChatUIHandler chatUIHandler = FindObjectOfType<ConvaiChatUIHandler>();
if (chatUIHandler != null && convaiNPCComponent.characterName != null &&
!chatUIHandler.HasCharacter(convaiNPCComponent.characterName))
{
Utils.Character newCharacter = new()
{
characterGameObject = convaiNPCComponent,
characterName = convaiNPCComponent.characterName,
CharacterTextColor = GetRandomColor()
};
chatUIHandler.AddCharacter(newCharacter);
EditorUtility.SetDirty(chatUIHandler);
}
}
/// <summary>
/// Sets up the Convai components for the character.
/// </summary>
/// <param name="characterID">The character ID.</param>
/// <param name="characterName">The name of the character.</param>
/// <param name="avatar">The avatar GameObject.</param>
/// <returns>The ConvaiNPC component.</returns>
private static ConvaiNPC SetupConvaiComponents(string characterID, string characterName, GameObject avatar)
{
ConvaiNPC convaiNPCComponent = avatar.AddComponent<ConvaiNPC>();
convaiNPCComponent.sessionID = "-1";
convaiNPCComponent.characterID = characterID;
convaiNPCComponent.characterName = characterName;
avatar.AddComponent<ConvaiHeadTracking>();
return convaiNPCComponent;
}
#if READY_PLAYER_ME
/// <summary>
/// Sets up the animator for the character.
/// </summary>
/// <param name="args">The completion event arguments.</param>
/// <param name="avatar">The avatar GameObject.</param>
private static void SetupAnimator(CompletionEventArgs args, GameObject avatar)
{
AvatarAnimationHelper.SetupAnimator(args.Metadata, avatar);
Animator animator = avatar.GetComponent<Animator>();
// Determine avatar type based on Avatar field in Animator component
bool isMasculine = animator.avatar.name.Contains("Masculine");
// Set the appropriate animator controller
string animatorPath = isMasculine ? "Masculine NPC Animator" : "Feminine NPC Animator";
animator.runtimeAnimatorController = Resources.Load<RuntimeAnimatorController>(animatorPath);
}
#endif
/// <summary>
/// Sets up the collision for the character.
/// </summary>
/// <param name="avatar">The avatar GameObject.</param>
private static void SetupCollision(GameObject avatar)
{
CapsuleCollider capsuleColliderComponent = avatar.AddComponent<CapsuleCollider>();
capsuleColliderComponent.center = new Vector3(0, 0.9f, 0);
capsuleColliderComponent.radius = 0.3f;
capsuleColliderComponent.height = 1.8f;
capsuleColliderComponent.isTrigger = true;
}
/// <summary>
/// Returns a random color from the predefined palette.
/// </summary>
/// <returns>A random color from the predefined palette.</returns>
private Color GetRandomColor()
{
return ColorPalette[Random.Range(0, ColorPalette.Length)];
}
private class GetRequest
{
[JsonProperty("charID")] public string CharID;
public GetRequest(string charID)
{
CharID = charID;
}
}
private class GetResponse
{
[JsonProperty("backstory")] public string Backstory;
[JsonProperty("character_actions")] public string[] CharacterActions;
[JsonProperty("character_emotions")] public string[] CharacterEmotions;
[JsonProperty("character_id")] public string CharacterID;
[JsonProperty("character_name")] public string CharacterName;
[JsonProperty("model_details")] public ModelDetails ModelDetail;
[JsonProperty("timestamp")] public string Timestamp;
[JsonProperty("user_id")] public string UserID;
[JsonProperty("voice_type")] public string VoiceType;
#region Nested type: ModelDetails
internal class ModelDetails
{
[JsonProperty("modelLink")] public string ModelLink;
[JsonProperty("modelPlaceholder")] public string ModelPlaceholder;
[JsonProperty("modelType")] public string ModelType;
}
#endregion
}
}
}

View File

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