private static void ValidateLocationBounds(GameObject gameObject) { var locationBoundsList = gameObject.GetComponentsInChildren<VCILocationBounds>(); if (locationBoundsList == null || locationBoundsList.Length == 0) { return; } // Check 1: LocationBounds が一つのみ存在する if (locationBoundsList.Length >= 2) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.LocationBoundsCountLimitOver}"); throw new VCIValidatorException(ValidationErrorType.LocationBoundsCountLimitOver, errorText); } var locationBounds = locationBoundsList[0]; var min = locationBounds.Bounds.min; var max = locationBounds.Bounds.max; // Check 2: x, y, z の制限範囲が ±10000 に収まる if (Mathf.Abs(min.x) > 10000f || Mathf.Abs(min.y) > 10000f || Mathf.Abs(min.z) > 10000f || Mathf.Abs(max.x) > 10000f || Mathf.Abs(max.y) > 10000f || Mathf.Abs(max.z) > 10000f) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.LocationBoundsValueExceeded}"); throw new VCIValidatorException(ValidationErrorType.LocationBoundsValueExceeded, errorText); } }
private static void ValidateVCIObjectComponentRestrictions(GameObject gameObject) { // Check 1: RootのGameObjectにVCIObjectがアタッチされている var vciObject = gameObject.GetComponent<VCIObject>(); if (vciObject == null) { if (vciObject == null) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.VCIObjectNotAttached}"); throw new VCIValidatorException(ValidationErrorType.VCIObjectNotAttached, errorText); } } // Check 2:「VCIObject」コンポーネントがVCIの中で一つのみ存在する var vciObjectCount = 0; foreach (var transform in vciObject.transform.Traverse()) { if (transform.GetComponent<VCIObject>() == null) { continue; } vciObjectCount++; if (vciObjectCount > 1) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.MultipleVCIObject}"); throw new VCIValidatorException(ValidationErrorType.MultipleVCIObject, errorText); } } }
private static void ValidatePlayerSpawnPoints(GameObject gameObject) { var playerSpawnPoints = gameObject.GetComponentsInChildren<VCIPlayerSpawnPoint>(); if (playerSpawnPoints == null) { return; } foreach (var playerSpawnPoint in playerSpawnPoints) { var spawnPointTransform = playerSpawnPoint.gameObject.transform; // Check 1: SpawnPoint の向きが水平である if (Math.Abs(spawnPointTransform.rotation.x) > 0.001f || Math.Abs(spawnPointTransform.rotation.z) > 0.001f) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.SpawnPointNotHorizontal}"); throw new VCIValidatorException(ValidationErrorType.SpawnPointNotHorizontal, errorText); } var spawnPointRestriction = playerSpawnPoint.GetComponent<VCIPlayerSpawnPointRestriction>(); if (spawnPointRestriction == null) continue; // Check 2: SpawnPoint が PlayerSpawnPointRestriction で指定した制限範囲内に存在する if (spawnPointRestriction.LimitRectLeft > 0 || spawnPointRestriction.LimitRectRight < 0 || spawnPointRestriction.LimitRectForward < 0 || spawnPointRestriction.LimitRectBackward > 0) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.SpawnPointOriginNotInRange}"); throw new VCIValidatorException(ValidationErrorType.SpawnPointOriginNotInRange, errorText); } } }
private static void CheckInvalidComponent<T>(GameObject target) { var c = target.GetComponentsInChildren<T>(true); if (c == null || c.Length == 0) return; var errorText = string.Format( VCIConfig.GetText($"error{(int) ValidationErrorType.InvalidComponent}"), typeof(T).Name); throw new VCIValidatorException(ValidationErrorType.InvalidComponent, errorText); }
public static bool Validate() { // Validation try { var selectedGameObjects = Selection.gameObjects; if (selectedGameObjects.Length == 0) { throw new VCIValidatorException(ValidationErrorType.GameObjectNotSelected); } if (2 <= selectedGameObjects.Length) { throw new VCIValidatorException(ValidationErrorType.MultipleSelection); } var vciObject = selectedGameObjects[0].GetComponent <VCIObject>(); if (vciObject == null) { throw new VCIValidatorException(ValidationErrorType.VCIObjectNotAttached); } VCIValidator.ValidateVCIObject(vciObject); } catch (VCIValidatorException e) { var title = $"Error{(int)e.ErrorType}"; var text = ""; if (string.IsNullOrEmpty(e.Message)) { text = VCIConfig.GetText($"error{(int)e.ErrorType}"); } else { text = e.Message; } text = text.Replace("\\n", Environment.NewLine); if (e.ErrorType == ValidationErrorType.InvalidCharacter) { EditorGUILayout.HelpBox(e.Message, MessageType.Warning); } EditorUtility.DisplayDialog(title, text, "OK"); GUIUtility.ExitGUI(); return(false); } return(true); }
private static string ValidateField(string fieldName, string text) { var validationRule = ValidationRules[fieldName]; if (validationRule.IsRequired && string.IsNullOrEmpty(text)) { return(string.Format(VCIConfig.GetText("input"), fieldName)); } if (text != null && validationRule.MaxLength < text.Length) { return(string.Format(VCIConfig.GetText("input_less_than"), fieldName, validationRule.MaxLength)); } return(""); }
private static void ValidateVCIScripts(VCIObject vciObject) { if (!vciObject.Scripts.Any()) { return; } // Check 1: 一つ目のスクリプトの名前が「main」である if (vciObject.Scripts[0].name != "main") { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.FirstScriptNameNotValid}"); throw new VCIValidatorException(ValidationErrorType.FirstScriptNameNotValid, errorText); } // Check 2: 名前が空のスクリプトが存在しない var empties = vciObject.Scripts.Where(x => string.IsNullOrEmpty(x.name)); if (empties.Any()) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.NoScriptName}"); throw new VCIValidatorException(ValidationErrorType.NoScriptName, errorText); } // Check 3: 同一の名前のスクリプトが複数存在しない var duplicates = vciObject.Scripts.GroupBy(script => script.name) .Where(name => name.Count() > 1) .Select(group => @group.Key).ToList(); if (duplicates.Any()) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.ScriptNameConfliction}"); throw new VCIValidatorException(ValidationErrorType.ScriptNameConfliction, errorText); } // Check 4: スクリプト名に無効な文字列が含まれていない // - 無効な文字列 : ファイル名に含めることのできない文字 + '.' var invalidChars = Path.GetInvalidFileNameChars().Concat(new[] {'.'}).ToArray(); foreach (var script in vciObject.Scripts) { if (script.name.IndexOfAny(invalidChars) >= 0) { var errorText = string.Format( VCIConfig.GetText($"error{(int) ValidationErrorType.InvalidCharacter}"), script.name); throw new VCIValidatorException(ValidationErrorType.InvalidCharacter, errorText); } } }
// TODO: VCIのValidationに関するエラーではないので、別途エラーを定義して投げる // NOTE: VCIExporterMenu.Validateの処理の一部をここに切り出したため、VCIValidatorExceptionを投げる形になっている // 切り出したのは、この処理は厳密にはVCIのValidationに関するものではないと思ったため /// <summary> /// Editor上で選択されている唯一のGameObjectを返す /// GameObjectが選択されていない/複数選択されている場合はexceptionをthrowする /// </summary> /// <returns></returns> /// <exception cref="VCIValidatorException"></exception> public static GameObject GetSingleSelectedObject() { var selectedGameObjects = Selection.gameObjects; if (selectedGameObjects.Length == 0) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.GameObjectNotSelected}"); throw new VCIValidatorException(ValidationErrorType.GameObjectNotSelected, errorText); } if (2 <= selectedGameObjects.Length) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.MultipleSelection}"); throw new VCIValidatorException(ValidationErrorType.MultipleSelection, errorText); } return(selectedGameObjects[0]); }
private static void ValidateAnimation(GameObject gameObject) { // NOTE: Editorコードを含んでいるため、ランタイムでValidateすることができない // TODO: Runtime 時にどうするかを考える #if UNITY_EDITOR // Check 1: root の Animator/Animation で自身を animate していない // NOTE: root についてる animation の export は UniGLTF 側で行われる // その時、Animator か Animation どちらか片方が一つのみアタッチされていること前提で export される var animationClips = new List<AnimationClip>(); var animator = gameObject.GetComponent<Animator>(); var animation = gameObject.GetComponent<Animation>(); if (animator != null) { animationClips.AddRange(AnimationExporter.GetAnimationClips(animator)); } else if (animation != null) { animationClips.AddRange(AnimationExporter.GetAnimationClips(animation)); } if (!animationClips.Any()) { return; } foreach (var animationClip in animationClips) { foreach (var animationCurveBindings in AnimationUtility.GetCurveBindings(animationClip)) { if (string.IsNullOrEmpty(animationCurveBindings.path)) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootIsAnimated}"); throw new VCIValidatorException(ValidationErrorType.RootIsAnimated, errorText); } } } #endif }
public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(_scriptProp); // Version EditorGUI.BeginDisabledGroup(true); if (string.IsNullOrEmpty(_target.Meta.exporterVersion)) { _target.Meta.exporterVersion = VCIVersion.VERSION; } SetMetaPropertyField(_metaProp, "exporterVersion"); EditorGUI.EndDisabledGroup(); EditorGUILayout.Space(); // Information EditorGUILayout.LabelField("Information", EditorStyles.boldLabel); SetMetaPropertyField(_metaProp, "title"); SetMetaPropertyField(_metaProp, "version"); SetMetaPropertyField(_metaProp, "author"); SetMetaPropertyField(_metaProp, "contactInformation"); SetMetaPropertyField(_metaProp, "reference"); // Thumbnail SetMetaPropertyField(_metaProp, "thumbnail"); EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); _thumbnailProp.objectReferenceValue = (Texture2D)EditorGUILayout.ObjectField( _thumbnailProp.objectReferenceValue, typeof(Texture2D), false, GUILayout.Width(100), GUILayout.Height(100)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); // Description SetMetaPropertyField(_metaProp, "description"); EditorGUILayout.Space(); // License SetMetaPropertyField(_metaProp, "modelDataLicenseType"); SetMetaPropertyField(_metaProp, "modelDataOtherLicenseUrl"); EditorGUILayout.Space(); SetMetaPropertyField(_metaProp, "scriptLicenseType"); SetMetaPropertyField(_metaProp, "scriptOtherLicenseUrl"); EditorGUILayout.Space(); // Script settings SetMetaPropertyField(_metaProp, "scriptWriteProtected"); SetMetaPropertyField(_metaProp, "scriptEnableDebugging"); EditorGUILayout.Space(); if (_target.Scripts.Any()) { if (_target.Scripts[0].name != "main") { EditorGUILayout.HelpBox("The first script must be named \"main\".", MessageType.Warning); } var empties = _target.Scripts.Where(x => string.IsNullOrEmpty(x.name)); if (empties.Any()) { EditorGUILayout.HelpBox("Some have no script name.", MessageType.Warning); } var duplicates = _target.Scripts.GroupBy(script => script.name) .Where(n => n.Count() > 1) .Select(group => group.Key).ToList(); if (duplicates.Any()) { EditorGUILayout.HelpBox("Duplicate script name.", MessageType.Warning); } var invalidChars = Path.GetInvalidFileNameChars().Concat(new[] { '.' }).ToArray(); foreach (var script in _target.Scripts) { if (script.name.IndexOfAny(invalidChars) >= 0) { EditorGUILayout.HelpBox("Contains characters that can not be used as scriptName. " + script.name, MessageType.Warning); } } } // vci scripts EditorGUILayout.PropertyField(_vciScriptProp, true); serializedObject.ApplyModifiedProperties(); EditorGUILayout.Space(); // Export Button if (GUILayout.Button(VCIConfig.GetText("validate_button"), GUILayout.MinHeight(32))) { try { var rootGameObject = GameObjectSelectionService.GetSingleSelectedObject(); VCIValidator.ValidateVCIRequirements(rootGameObject); EditorUtility.DisplayDialog("Result", VCIConfig.GetText("no_error"), "OK"); } catch (VCIValidatorException e) { VCIValidationErrorDialog.ShowErrorDialog(e); GUIUtility.ExitGUI(); } } EditorGUILayout.Space(); // Export Button if (GUILayout.Button(VCIConfig.GetText("export_button"), GUILayout.MinHeight(32))) { #if UNITY_EDITOR_WIN VCIObjectExporterMenu.ExportObject(); #endif } }
public static void ValidateVCIObject(VCIObject vo) { // VCIObject var vciObjectCount = 0; var gameObjectCount = 0; foreach (var t in vo.transform.Traverse()) { if (t.GetComponent <VCIObject>() != null) { vciObjectCount++; } gameObjectCount++; } if (vciObjectCount > 1) { throw new VCIValidatorException(ValidationErrorType.MultipleVCIObject); } // Scripts if (vo.Scripts.Any()) { if (vo.Scripts[0].name != "main") { throw new VCIValidatorException(ValidationErrorType.FirstScriptNameNotValid); } var empties = vo.Scripts.Where(x => string.IsNullOrEmpty(x.name)); if (empties.Any()) { throw new VCIValidatorException(ValidationErrorType.NoScriptName); } var duplicates = vo.Scripts.GroupBy(script => script.name) .Where(name => name.Count() > 1) .Select(group => group.Key).ToList(); if (duplicates.Any()) { throw new VCIValidatorException(ValidationErrorType.ScriptNameConfliction); } var invalidChars = Path.GetInvalidFileNameChars().Concat(new [] { '.' }).ToArray(); foreach (var script in vo.Scripts) { if (script.name.IndexOfAny(invalidChars) >= 0) { throw new VCIValidatorException(ValidationErrorType.InvalidCharacter, string.Format(VCIConfig.GetText($"error{(int)ValidationErrorType.InvalidCharacter}"), script.name)); } } ; } VCIMetaValidator.Validate(vo); // Invalid Components CheckInvalidComponent <MeshCollider>(vo.gameObject); // Spring Bone var springBones = vo.GetComponents <VCISpringBone>(); if (springBones != null && springBones.Length > 0) { ValidateSpringBones(springBones); } }
public static void ValidateVCIObject(VCIObject vo) { // VCIObject var vciObjectCount = 0; var gameObjectCount = 0; var playerSpawnPointsList = new List <VCIPlayerSpawnPoint>(); var locationBoundsList = new List <VCILocationBounds>(); foreach (var t in vo.transform.Traverse()) { if (t.GetComponent <VCIObject>() != null) { vciObjectCount++; } var psp = t.GetComponent <VCIPlayerSpawnPoint>(); if (psp != null) { playerSpawnPointsList.Add(psp); } var locationBounds = t.GetComponent <VCILocationBounds>(); if (locationBounds != null) { locationBoundsList.Add(locationBounds); } gameObjectCount++; } if (vciObjectCount > 1) { throw new VCIValidatorException(ValidationErrorType.MultipleVCIObject); } // Scripts if (vo.Scripts.Any()) { if (vo.Scripts[0].name != "main") { throw new VCIValidatorException(ValidationErrorType.FirstScriptNameNotValid); } var empties = vo.Scripts.Where(x => string.IsNullOrEmpty(x.name)); if (empties.Any()) { throw new VCIValidatorException(ValidationErrorType.NoScriptName); } var duplicates = vo.Scripts.GroupBy(script => script.name) .Where(name => name.Count() > 1) .Select(group => group.Key).ToList(); if (duplicates.Any()) { throw new VCIValidatorException(ValidationErrorType.ScriptNameConfliction); } var invalidChars = Path.GetInvalidFileNameChars().Concat(new [] { '.' }).ToArray(); foreach (var script in vo.Scripts) { if (script.name.IndexOfAny(invalidChars) >= 0) { throw new VCIValidatorException(ValidationErrorType.InvalidCharacter, string.Format(VCIConfig.GetText($"error{(int)ValidationErrorType.InvalidCharacter}"), script.name)); } } ; } VCIMetaValidator.Validate(vo); // Invalid Components CheckInvalidComponent <MeshCollider>(vo.gameObject); // Spring Bone var springBones = vo.GetComponents <VCISpringBone>(); if (springBones != null && springBones.Length > 0) { ValidateSpringBones(springBones); } // PlayerSpawnPoint foreach (var psp in playerSpawnPointsList) { var pspT = psp.gameObject.transform; if (Math.Abs(pspT.position.y) > SpawnPointAllowedHeightRange) { throw new VCIValidatorException(ValidationErrorType.SpawnPointHeightRangeNotAllowed, VCIConfig.GetText($"error{(int)ValidationErrorType.SpawnPointHeightRangeNotAllowed}")); } if (Math.Abs(pspT.rotation.x) > 0.001f || Math.Abs(pspT.rotation.z) > 0.001f) { throw new VCIValidatorException(ValidationErrorType.SpawnPointNotHorizontal, VCIConfig.GetText($"error{(int)ValidationErrorType.SpawnPointNotHorizontal}")); } var pspR = psp.GetComponent <VCIPlayerSpawnPointRestriction>(); if (pspR == null) { continue; } if (pspR.LimitRectLeft > 0 || pspR.LimitRectRight < 0 || pspR.LimitRectForward < 0 || pspR.LimitRectBackward > 0) { throw new VCIValidatorException(ValidationErrorType.SpawnPointOriginNotInRange, VCIConfig.GetText($"error{(int)ValidationErrorType.SpawnPointOriginNotInRange}")); } } // LocationBounds if (locationBoundsList.Count >= 2) { throw new VCIValidatorException(ValidationErrorType.LocationBoundsCountLimitOver, VCIConfig.GetText($"error{(int)ValidationErrorType.LocationBoundsCountLimitOver}")); } if (locationBoundsList.Count > 0) { var locationBounds = locationBoundsList[0]; var min = locationBounds.Bounds.min; var max = locationBounds.Bounds.max; // 10000fを超えてないかだけチェック if (Mathf.Abs(min.x) > 10000f || Mathf.Abs(min.y) > 10000f || Mathf.Abs(min.z) > 10000f || Mathf.Abs(max.x) > 10000f || Mathf.Abs(max.y) > 10000f || Mathf.Abs(max.z) > 10000f) { throw new VCIValidatorException(ValidationErrorType.LocationBoundsValueExceeded, VCIConfig.GetText($"error{(int)ValidationErrorType.LocationBoundsValueExceeded}")); } } }
private static void ValidateSpringBones(GameObject gameObject) { // NOTE: どういう歴史的経緯があったのか分からないけど、現在これらの使われていない // const int maxSpringBoneColliderCount = 10; // const int maxSphereColliderCount = 10; var springBones = gameObject.GetComponents<VCISpringBone>(); if (springBones == null) { return; } // Check 1: アタッチされている SpringBone コンポーネントが maxSpringBoneCount以下である const int maxSpringBoneCount = 1; if (springBones.Length > maxSpringBoneCount) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.TooManySpringBone}"); throw new VCIValidatorException(ValidationErrorType.TooManySpringBone, errorText); } const int maxRootBoneCount = 10; const int maxChildBoneCount = 10; foreach (var springBone in springBones) { var rootBones = springBone.RootBones; // Check 2: RootBone が存在する if (rootBones == null || rootBones.Count == 0) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootBoneNotFound}"); throw new VCIValidatorException(ValidationErrorType.RootBoneNotFound, errorText); } // Check 3: Root の Bone の数が MaxRootBoneCount 以下である if (rootBones.Count > maxRootBoneCount) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.TooManyRootBone}"); throw new VCIValidatorException(ValidationErrorType.TooManyRootBone, errorText); } for (var i = 0; i < rootBones.Count; i++) { if (rootBones[i] == null) { continue; } var rootBone = rootBones[i]; var childCount = 0; foreach (var childBone in rootBone.Traverse()) { // Check 4: SpringBone の中に SubItem が存在しない if (childBone.GetComponent<VCISubItem>() != null) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootBoneContainsSubItem}"); throw new VCIValidatorException(ValidationErrorType.RootBoneContainsSubItem, errorText); } // Check 5: RootBone の持つ ChildBone が MaxChildBoneCount 以下である childCount++; if (childCount > maxChildBoneCount) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.TooManyRootBoneChild}"); throw new VCIValidatorException(ValidationErrorType.TooManyRootBoneChild, errorText); } // Check 6: RootBone が入れ子になっていない for (var j = 0; j < rootBones.Count; j++) { if (j == i) { continue; } if (rootBones[j] == childBone) { var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootBoneNested}"); throw new VCIValidatorException(ValidationErrorType.RootBoneNested, errorText); } } } } } }