Skip to content

Commit

Permalink
Merge branch 'release/v4.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
VaLiuM09 committed Nov 5, 2024
2 parents a5b4c85 + 78ba251 commit 16a34f6
Show file tree
Hide file tree
Showing 47 changed files with 1,083 additions and 380 deletions.
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# Changelog - VR Builder

**v4.3.0 (2024/09/12 - Current)**
**v4.4.0 (2024/10/31 - Current)**

*[Added]*
- The `Object in Collider` condition now supports multiple object references and lets the user specify a number of objects that needs to be in the collider.
- Every text-to-speech provider implementation can now have custom configuration fields. This makes it easier to implement custom TTS providers.
- `UserSceneObject` now has a `Base` property corresponding to the position of the user's feet.


*[Changed]*
- Standardized component configuration sections in Project Settings (like the SnapZone settings) to make it easier to create more.

*[Fixed]*
- Fixed automatically focusing the Step Inspector window when recompiling.
- Play Audio behavior now stops playing before aborting.
- If a condition is a lockable property provider, lockable properties are retrieved using the condition's API instead of a helper method. This makes it easier to implement custom conditions that return a specific set of lockable properties.

**v4.3.0 (2024/09/12)**

*[Added]*
- The Parallel Execution node now lets you create optional paths. You can make a path optional by clicking the new button next to its name. An optional path will be interrupted when all non-optional paths have completed, and execution will immediately proceed to the next node. This can be useful to create background looping animations, recurring hints and so on. An optional path can even be an endless loop and it will still be interrupted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace VRBuilder.Editor.UI.Conditions
public class ObjectInColliderMenuItem : MenuItem<ICondition>
{
/// <inheritdoc />
public override string DisplayedName { get; } = "Environment/Move Object in Collider";
public override string DisplayedName { get; } = "Environment/Move Objects in Collider";

/// <inheritdoc />
public override ICondition GetNewItem()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,28 @@ public override void FastForward()
}
}

private class AbortingProcess : InstantProcess<PlayAudioBehavior.EntityData>
{
public AbortingProcess(EntityData data) : base(data)
{
}

public override void Start()
{
Debug.Log("Aborting");
if (Data.AudioPlayer != null)
{
Data.AudioPlayer.Stop();
}
else
{
IProcessAudioPlayer audioPlayer = RuntimeConfigurator.Configuration.ProcessAudioPlayer;
audioPlayer.Stop();
audioPlayer.Reset();
}
}
}

[JsonConstructor, Preserve]
protected PlayAudioBehavior() : this(null, BehaviorExecutionStages.None)
{
Expand Down Expand Up @@ -169,5 +191,10 @@ public override IStageProcess GetDeactivatingProcess()
{
return new PlayAudioProcess(BehaviorExecutionStages.Deactivation, Data);
}

public override IStageProcess GetAbortingProcess()
{
return new AbortingProcess(Data);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using UnityEngine;
using UnityEngine.Scripting;
using VRBuilder.Core.Attributes;
using VRBuilder.Core.Properties;
using VRBuilder.Core.SceneObjects;
using VRBuilder.Core.Settings;
using VRBuilder.Core.Utils;

namespace VRBuilder.Core.Conditions
Expand All @@ -25,10 +28,10 @@ public class ObjectInColliderCondition : Condition<ObjectInColliderCondition.Ent
public class EntityData : IObjectInTargetData
{
/// <summary>
/// The object that has to enter the collider.
/// The objects that have to enter the collider.
/// </summary>
[DataMember]
[DisplayName("Object")]
[DisplayName("Objects")]
public MultipleSceneObjectReference TargetObjects { get; set; }

[DataMember]
Expand All @@ -50,19 +53,34 @@ public class EntityData : IObjectInTargetData
[Obsolete("Use TriggerObject instead.")]
[LegacyProperty(nameof(TriggerObject))]
public ScenePropertyReference<ColliderWithTriggerProperty> TriggerProperty { get; set; }

/// <inheritdoc />
public bool IsCompleted { get; set; }

/// <inheritdoc />
[HideInProcessInspector]
[IgnoreDataMember]
public string Name => $"Move {TargetObjects} in collider {TriggerObject}";
public string Name
{
get
{
if (ObjectsRequiredInTrigger > 1 && TargetObjects.Values.Count() > 1)
{
return $"Move {ObjectsRequiredInTrigger} of {TargetObjects.Values.Count()} '{SceneObjectGroups.Instance.GetLabel(TargetObjects.Guids.First())}' in collider {TriggerObject}";
}

return $"Move {TargetObjects} in collider {TriggerObject}";
}
}

/// <inheritdoc />
[DataMember]
[DisplayName("Required seconds inside")]
public float RequiredTimeInside { get; set; }

[DataMember]
[DisplayName("Required Object count")]
public float ObjectsRequiredInTrigger { get; set; }

/// <inheritdoc />
public Metadata Metadata { get; set; }
Expand All @@ -73,14 +91,19 @@ public ObjectInColliderCondition() : this(Guid.Empty, Guid.Empty)
{
}

public ObjectInColliderCondition(ColliderWithTriggerProperty targetPosition, IReadOnlyList<Guid> multipleObjectsGuids, int requiredTimeInTarget, int objectsRequiredInTrigger)
: this(ProcessReferenceUtils.GetUniqueIdFrom(targetPosition), multipleObjectsGuids, requiredTimeInTarget, objectsRequiredInTrigger)
{
}

// ReSharper disable once SuggestBaseTypeForParameter
public ObjectInColliderCondition(ColliderWithTriggerProperty targetPosition, ISceneObject targetObject, float requiredTimeInTarget = 0)
: this(ProcessReferenceUtils.GetUniqueIdFrom(targetPosition), ProcessReferenceUtils.GetUniqueIdFrom(targetObject), requiredTimeInTarget)
public ObjectInColliderCondition(ColliderWithTriggerProperty targetPosition, ISceneObject targetObject, float requiredTimeInTarget = 0, float objectsRequiredInTrigger = 1)
: this(ProcessReferenceUtils.GetUniqueIdFrom(targetPosition), ProcessReferenceUtils.GetUniqueIdFrom(targetObject), requiredTimeInTarget, objectsRequiredInTrigger)
{
}

[Obsolete("This constructor will be removed in the next major version.")]
public ObjectInColliderCondition(string targetPosition, string targetObject, float requiredTimeInTarget = 0)
public ObjectInColliderCondition(string targetPosition, string targetObject, float requiredTimeInTarget = 0, float objectsRequiredInTrigger = 1)
{
Guid triggerGuid = Guid.Empty;
Guid targetGuid = Guid.Empty;
Expand All @@ -89,13 +112,23 @@ public ObjectInColliderCondition(string targetPosition, string targetObject, flo
Data.TriggerObject = new SingleScenePropertyReference<ColliderWithTriggerProperty>(triggerGuid);
Data.TargetObjects = new MultipleSceneObjectReference(targetGuid);
Data.RequiredTimeInside = requiredTimeInTarget;
Data.ObjectsRequiredInTrigger = objectsRequiredInTrigger;
}

public ObjectInColliderCondition(Guid targetPosition, Guid targetObject, float requiredTimeInTarget = 0)
public ObjectInColliderCondition(Guid targetPosition, Guid targetObject, float requiredTimeInTarget = 0, float objectsRequiredInTrigger = 1)
{
Data.TriggerObject = new SingleScenePropertyReference<ColliderWithTriggerProperty>(targetPosition);
Data.TargetObjects = new MultipleSceneObjectReference(targetObject);
Data.RequiredTimeInside = requiredTimeInTarget;
Data.ObjectsRequiredInTrigger = objectsRequiredInTrigger;
}

private ObjectInColliderCondition(Guid targetPosition, IReadOnlyList<Guid> targetObject, int requiredTimeInTarget, int objectsRequiredInTrigger)
{
Data.TriggerObject = new SingleScenePropertyReference<ColliderWithTriggerProperty>(targetPosition);
Data.TargetObjects = new MultipleSceneObjectReference(targetObject);
Data.RequiredTimeInside = requiredTimeInTarget;
Data.ObjectsRequiredInTrigger = objectsRequiredInTrigger;
}

private class ActiveProcess : ObjectInTargetActiveProcess<EntityData>
Expand All @@ -104,17 +137,30 @@ public ActiveProcess(EntityData data) : base(data)
{
}

public override void Start()
{
if (Data.ObjectsRequiredInTrigger > Data.TargetObjects.Values.Count())
{
Debug.LogWarning($"The required object count {Data.ObjectsRequiredInTrigger} is bigger then the target objects count of {Data.TargetObjects.Values.Count()} and not completable.");
}
if (Data.ObjectsRequiredInTrigger < 1)
{
Debug.LogWarning($"The required object count {Data.ObjectsRequiredInTrigger} is below 1 and always completed.");
}
base.Start();
}

/// <inheritdoc />
protected override bool IsInside()
{
bool isTransformInsideTrigger = false;
int counter = 0;

foreach (ISceneObject sceneObject in Data.TargetObjects.Values)
{
isTransformInsideTrigger |= Data.TriggerObject.Value.IsTransformInsideTrigger(sceneObject.GameObject.transform);
counter += Data.TriggerObject.Value.IsTransformInsideTrigger(sceneObject.GameObject.transform)? 1 : 0;
}

return isTransformInsideTrigger;
return counter >= Data.ObjectsRequiredInTrigger;
}
}

Expand All @@ -127,10 +173,22 @@ public EntityAutocompleter(EntityData data) : base(data)
/// <inheritdoc />
public override void Complete()
{
ISceneObject sceneObject = Data.TargetObjects.Values.FirstOrDefault();

if (sceneObject != null)
if (Data.ObjectsRequiredInTrigger > 1 && Data.TargetObjects.Values.Any())
{
int counter = 0;
foreach (var objs in Data.TargetObjects.Values)
{
Data.TriggerObject.Value.FastForwardEnter(objs);
counter++;
if (counter >= Data.ObjectsRequiredInTrigger)
{
break;
}
}
}
else
{
ISceneObject sceneObject = Data.TargetObjects.Values.FirstOrDefault();
Data.TriggerObject.Value.FastForwardEnter(sceneObject);
}
}
Expand Down
27 changes: 27 additions & 0 deletions Source/Core/Editor/UI/ProjectSettings/ComponentSettingsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using UnityEditor;

namespace VRBuilder.Editor.UI
{
/// <summary>
/// Settings provider that groups global settings for scene components in separate sections.
/// </summary>
public class ComponentSettingsProvider : BaseSettingsProvider
{
private const string Path = "Project/VR Builder/Component Settings";

private UnityEditor.Editor editor;

public ComponentSettingsProvider() : base(Path, SettingsScope.Project) { }

[SettingsProvider]
public static SettingsProvider Provider()
{
SettingsProvider provider = new ComponentSettingsProvider();
return provider;
}

protected override void InternalDraw(string searchContext)
{
}
}
}
80 changes: 80 additions & 0 deletions Source/Core/Editor/UI/ProjectSettings/ComponentSettingsSection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using VRBuilder.Core.Runtime.Utils;

namespace VRBuilder.Editor.UI
{
/// <summary>
/// Settings section holding global settings for a specified scene component.
/// </summary>
/// <typeparam name="TObject">The component that can be configured by this section.</typeparam>
/// <typeparam name="TSettings">The scriptable object holding the global settings.</typeparam>
public abstract class ComponentSettingsSection<TObject, TSettings> : IProjectSettingsSection where TObject : UnityEngine.Object where TSettings : ComponentSettings<TObject, TSettings>, new()
{
/// <inheritdoc/>
public abstract string Title { get; }

/// <summary>
/// Description of this settings section. Will be displayed below the title, before the settings in the scriptable object.
/// </summary>
public abstract string Description { get; }

/// <inheritdoc/>
public Type TargetPageProvider => typeof(ComponentSettingsProvider);

/// <inheritdoc/>
public abstract int Priority { get; }

private UnityEditor.Editor editor;
private TSettings settings;

/// <inheritdoc/>
public void OnGUI(string searchContext)
{
EditorGUILayout.Space();
GUIStyle labelStyle = BuilderEditorStyles.ApplyPadding(BuilderEditorStyles.Paragraph, 0);
GUILayout.Label(Description, labelStyle);
EditorGUILayout.Space();

editor.OnInspectorGUI();

if (GUILayout.Button("Apply settings in current scene"))
{
TObject[] objects = Resources.FindObjectsOfTypeAll<TObject>();

foreach (TObject snapZone in objects)
{
GetSettingsInstance().ApplySettings(snapZone);
}
}

EditorGUILayout.Space(20f);
}

public ComponentSettingsSection()
{
editor = UnityEditor.Editor.CreateEditor(GetSettingsInstance());
}

private TSettings GetSettingsInstance()
{
if (settings != null)
{
return settings;
}

Type closedGenericType = typeof(SettingsObject<>).MakeGenericType(typeof(TSettings));

PropertyInfo instanceProperty = closedGenericType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);

if (instanceProperty != null)
{
return instanceProperty.GetValue(null) as TSettings;
}

return null;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 16a34f6

Please sign in to comment.