Files
sbox-public/engine/Sandbox.Engine/Scene/GameObject/GameObject.Reference.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

108 lines
3.8 KiB
C#

using System.Text.Json.Serialization;
namespace Sandbox;
#nullable enable
/// <summary>
/// A serialized reference to a <see cref="GameObject"/> that can be resolved at runtime.
/// Can either have a <see cref="GameObjectId"/> for objects in a scene, or a <see cref="PrefabPath"/>
/// if we're referencing a prefab.
/// </summary>
[ActionGraphExposeWhenCached]
internal readonly struct GameObjectReference
{
private const string ExpectedReferenceType = "gameobject";
/// <summary>
/// Reference a <see cref="GameObject"/> in a scene by its <see cref="GameObject.Id"/>.
/// </summary>
public static GameObjectReference FromId( Guid id ) => new( ExpectedReferenceType, gameObjectId: id );
/// <summary>
/// Reference a prefab by its <see cref="Resource.ResourcePath"/>.
/// </summary>
public static GameObjectReference FromPrefabPath( string prefabPath ) => new( ExpectedReferenceType, prefabPath: prefabPath );
/// <summary>
/// Reference a given <see cref="GameObject"/>.
/// </summary>
public static GameObjectReference FromInstance( GameObject go )
{
// If we reference a prefab scene, use the prefab path instead of the ID.
// But only if the prefab scene is not the active scene.
// If the prefab is the active scene, we can use the ID, as users may want to reference the prefab root from within the prefab scene.
if ( go is PrefabScene prefabRoot && go.Scene != Game.ActiveScene )
{
Assert.NotNull( prefabRoot.Source, "Prefab root should have a source" );
return FromPrefabPath( prefabRoot.Source.ResourcePath );
}
return FromId( go.Id );
}
/// <summary>
/// Expected to be <c>"gameobject"</c> for a game object reference.
/// </summary>
[JsonPropertyName( "_type" )]
public string ReferenceType { get; }
/// <summary>
/// If we're referencing an object in a scene, this is its <see cref="GameObject.Id"/>.
/// </summary>
[JsonPropertyName( "go" ), JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingDefault )]
public Guid GameObjectId { get; }
/// <summary>
/// If we're referencing a prefab, this is its <see cref="Resource.ResourcePath"/>.
/// </summary>
[JsonPropertyName( "prefab" ), JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
public string? PrefabPath { get; }
[JsonConstructor]
private GameObjectReference( string referenceType, Guid gameObjectId = default, string? prefabPath = null )
{
ReferenceType = referenceType;
GameObjectId = gameObjectId;
PrefabPath = prefabPath;
}
/// <summary>
/// Attempt to resolve this reference in the current <see cref="Game.ActiveScene"/>. Returns <see langword="null"/> if
/// the reference couldn't be resolved, and logs a warning.
/// </summary>
public GameObject? Resolve() => Resolve( Game.ActiveScene );
/// <summary>
/// Attempt to resolve this reference in the given <paramref name="scene"/>. Returns <see langword="null"/> if
/// the reference couldn't be resolved.
/// </summary>
/// <param name="scene">Scene to attempt to resolve the reference in.</param>
/// <param name="warn">If true, log a warning to the console if the reference couldn't be resolved.</param>
public GameObject? Resolve( Scene scene, bool warn = false )
{
if ( ReferenceType != ExpectedReferenceType )
throw new( $"Tried to deserialize unknown type '{ReferenceType ?? "null"}' as GameObject" );
if ( !string.IsNullOrWhiteSpace( PrefabPath ) )
{
var prefabFile = (PrefabFile)GameResource.GetPromise( typeof( PrefabFile ), PrefabPath );
return SceneUtility.GetPrefabScene( prefabFile );
}
var go = scene.Directory.FindByGuid( GameObjectId );
if ( !go.IsValid() )
{
// Conna: this is okay - we may be deserializing over the network and this is a property
// referring to a GameObject that we don't know about. Let's just make the reference null.
if ( warn ) Log.Warning( $"Unknown GameObject {GameObjectId}" );
return null;
}
return go;
}
}