Merge branch '1.5' into dotnet7
# Conflicts: # Source/Editor/Managed/ManagedEditor.Internal.cpp # Source/Engine/Core/Config/LayersAndTagsSettings.cs
This commit is contained in:
@@ -73,13 +73,12 @@ Actor::Actor(const SpawnParams& params)
|
||||
, _isPrefabRoot(false)
|
||||
, _isEnabled(false)
|
||||
, _layer(0)
|
||||
, _tag(ACTOR_TAG_INVALID)
|
||||
, _scene(nullptr)
|
||||
, _staticFlags(StaticFlags::FullyStatic)
|
||||
, _localTransform(Transform::Identity)
|
||||
, _transform(Transform::Identity)
|
||||
, _sphere(BoundingSphere::Empty)
|
||||
, _box(BoundingBox::Zero)
|
||||
, _scene(nullptr)
|
||||
, _physicsScene(nullptr)
|
||||
, HideFlags(HideFlags::None)
|
||||
{
|
||||
@@ -439,23 +438,24 @@ void Actor::DestroyChildren(float timeLeft)
|
||||
}
|
||||
}
|
||||
|
||||
bool Actor::HasTag(const StringView& tag) const
|
||||
{
|
||||
return HasTag() && tag == Level::Tags[_tag];
|
||||
}
|
||||
|
||||
const String& Actor::GetLayerName() const
|
||||
{
|
||||
return Level::Layers[_layer];
|
||||
}
|
||||
|
||||
const String& Actor::GetTag() const
|
||||
bool Actor::HasTag() const
|
||||
{
|
||||
if (HasTag())
|
||||
{
|
||||
return Level::Tags[_tag];
|
||||
}
|
||||
return String::Empty;
|
||||
return Tags.Count() != 0;
|
||||
}
|
||||
|
||||
bool Actor::HasTag(const Tag& tag) const
|
||||
{
|
||||
return Tags.Contains(tag);
|
||||
}
|
||||
|
||||
bool Actor::HasTag(const StringView& tag) const
|
||||
{
|
||||
return Tags.Contains(tag);
|
||||
}
|
||||
|
||||
void Actor::SetLayer(int32 layerIndex)
|
||||
@@ -463,51 +463,10 @@ void Actor::SetLayer(int32 layerIndex)
|
||||
layerIndex = Math::Clamp(layerIndex, 0, 31);
|
||||
if (layerIndex == _layer)
|
||||
return;
|
||||
|
||||
_layer = layerIndex;
|
||||
OnLayerChanged();
|
||||
}
|
||||
|
||||
void Actor::SetTagIndex(int32 tagIndex)
|
||||
{
|
||||
if (tagIndex == ACTOR_TAG_INVALID)
|
||||
{
|
||||
}
|
||||
else if (Level::Tags.IsEmpty())
|
||||
{
|
||||
tagIndex = ACTOR_TAG_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagIndex = tagIndex < 0 ? ACTOR_TAG_INVALID : Math::Min(tagIndex, Level::Tags.Count() - 1);
|
||||
}
|
||||
if (tagIndex == _tag)
|
||||
return;
|
||||
|
||||
_tag = tagIndex;
|
||||
OnTagChanged();
|
||||
}
|
||||
|
||||
void Actor::SetTag(const StringView& tagName)
|
||||
{
|
||||
int32 tagIndex;
|
||||
if (tagName.IsEmpty())
|
||||
{
|
||||
tagIndex = ACTOR_TAG_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagIndex = Level::Tags.Find(tagName);
|
||||
if (tagIndex == -1)
|
||||
{
|
||||
LOG(Error, "Cannot change actor tag. Given value is invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetTagIndex(tagIndex);
|
||||
}
|
||||
|
||||
void Actor::SetName(const StringView& value)
|
||||
{
|
||||
if (_name == value)
|
||||
@@ -897,10 +856,21 @@ void Actor::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
SERIALIZE_MEMBER(StaticFlags, _staticFlags);
|
||||
SERIALIZE(HideFlags);
|
||||
SERIALIZE_MEMBER(Layer, _layer);
|
||||
if (!other || _tag != other->_tag)
|
||||
if (!other || Tags != other->Tags)
|
||||
{
|
||||
stream.JKEY("Tag");
|
||||
stream.String(GetTag());
|
||||
if (Tags.Count() == 1)
|
||||
{
|
||||
stream.JKEY("Tag");
|
||||
stream.String(Tags.Get()->ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.JKEY("Tags");
|
||||
stream.StartArray();
|
||||
for (auto& tag : Tags)
|
||||
stream.String(tag.ToString());
|
||||
stream.EndArray();
|
||||
}
|
||||
}
|
||||
|
||||
if (isPrefabDiff)
|
||||
@@ -996,14 +966,27 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
_staticFlags |= StaticFlags::Navigation;
|
||||
}
|
||||
|
||||
// Resolve tag (it may be missing in the current configuration
|
||||
const auto tag = stream.FindMember("Tag");
|
||||
if (tag != stream.MemberEnd())
|
||||
{
|
||||
if (tag->value.IsString() && tag->value.GetStringLength())
|
||||
{
|
||||
const String tagName = tag->value.GetText();
|
||||
_tag = Level::GetOrAddTag(tagName);
|
||||
Tags.Clear();
|
||||
Tags.Add(Tags::Get(tag->value.GetText()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto tags = stream.FindMember("Tags");
|
||||
if (tags != stream.MemberEnd() && tags->value.IsArray())
|
||||
{
|
||||
Tags.Clear();
|
||||
for (rapidjson::SizeType i = 0; i < tags->value.Size(); i++)
|
||||
{
|
||||
auto& e = tags->value[i];
|
||||
if (e.IsString() && e.GetStringLength())
|
||||
Tags.Add(Tags::Get(e.GetText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,9 @@ namespace FlaxEngine
|
||||
/// <returns>The child actor.</returns>
|
||||
public Actor AddChild(Type type)
|
||||
{
|
||||
if (type.IsAbstract)
|
||||
return null;
|
||||
|
||||
var result = (Actor)New(type);
|
||||
result.SetParent(this, false, false);
|
||||
return result;
|
||||
@@ -135,6 +138,9 @@ namespace FlaxEngine
|
||||
/// <returns>The child actor.</returns>
|
||||
public T AddChild<T>() where T : Actor
|
||||
{
|
||||
if (typeof(T).IsAbstract)
|
||||
return null;
|
||||
|
||||
var result = New<T>();
|
||||
result.SetParent(this, false, false);
|
||||
return result;
|
||||
@@ -172,6 +178,9 @@ namespace FlaxEngine
|
||||
var result = GetChild<T>();
|
||||
if (result == null)
|
||||
{
|
||||
if (typeof(T).IsAbstract)
|
||||
return null;
|
||||
|
||||
result = New<T>();
|
||||
result.SetParent(this, false, false);
|
||||
}
|
||||
@@ -185,6 +194,9 @@ namespace FlaxEngine
|
||||
/// <returns>The created script instance, null otherwise.</returns>
|
||||
public Script AddScript(Type type)
|
||||
{
|
||||
if (type.IsAbstract)
|
||||
return null;
|
||||
|
||||
var script = (Script)New(type);
|
||||
script.Parent = this;
|
||||
return script;
|
||||
@@ -197,6 +209,9 @@ namespace FlaxEngine
|
||||
/// <returns>The created script instance, null otherwise.</returns>
|
||||
public T AddScript<T>() where T : Script
|
||||
{
|
||||
if (typeof(T).IsAbstract)
|
||||
return null;
|
||||
|
||||
var script = New<T>();
|
||||
script.Parent = this;
|
||||
return script;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "SceneObject.h"
|
||||
#include "Tags.h"
|
||||
#include "Engine/Core/Types/Span.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
@@ -19,9 +20,6 @@ class PhysicsScene;
|
||||
class SceneRendering;
|
||||
class SceneRenderTask;
|
||||
|
||||
// Maximum tag index is used as an invalid value
|
||||
#define ACTOR_TAG_INVALID 255
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all actor objects on the scene.
|
||||
/// </summary>
|
||||
@@ -34,7 +32,6 @@ API_CLASS(Abstract) class FLAXENGINE_API Actor : public SceneObject
|
||||
friend SceneRendering;
|
||||
friend Prefab;
|
||||
friend PrefabInstanceData;
|
||||
|
||||
protected:
|
||||
int16 _isActive : 1;
|
||||
int16 _isActiveInHierarchy : 1;
|
||||
@@ -43,7 +40,6 @@ protected:
|
||||
int16 _drawNoCulling : 1;
|
||||
int16 _drawCategory : 4;
|
||||
byte _layer;
|
||||
byte _tag;
|
||||
StaticFlags _staticFlags;
|
||||
Transform _localTransform;
|
||||
Transform _transform;
|
||||
@@ -77,6 +73,11 @@ public:
|
||||
API_FIELD(Attributes="HideInEditor, NoSerialize")
|
||||
HideFlags HideFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Actor tags collection.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-68)") Array<Tag> Tags;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the object layer (index). Can be used for selective rendering or ignoring raycasts.
|
||||
@@ -96,26 +97,10 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor tag (index). Can be used to identify the objects.
|
||||
/// Sets the layer.
|
||||
/// </summary>
|
||||
FORCE_INLINE int32 GetTagIndex() const
|
||||
{
|
||||
return _tag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this actor has tag assigned.
|
||||
/// </summary>
|
||||
API_FUNCTION() FORCE_INLINE bool HasTag() const
|
||||
{
|
||||
return _tag != ACTOR_TAG_INVALID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this actor has given tag assigned.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
API_FUNCTION() bool HasTag(const StringView& tag) const;
|
||||
/// <param name="layerIndex">The index of the layer.</param>
|
||||
API_PROPERTY() void SetLayer(int32 layerIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the layer.
|
||||
@@ -123,28 +108,21 @@ public:
|
||||
API_PROPERTY() const String& GetLayerName() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the tag.
|
||||
/// Determines whether this actor has any tag assigned.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-68), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTagEditor\")")
|
||||
const String& GetTag() const;
|
||||
API_FUNCTION() bool HasTag() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the layer.
|
||||
/// Determines whether this actor has given tag assigned (exact match).
|
||||
/// </summary>
|
||||
/// <param name="layerIndex">The index of the layer.</param>
|
||||
API_PROPERTY() void SetLayer(int32 layerIndex);
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
API_FUNCTION() bool HasTag(const Tag& tag) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the tag.
|
||||
/// Determines whether this actor has given tag assigned (exact match).
|
||||
/// </summary>
|
||||
/// <param name="tagIndex">The index of the tag.</param>
|
||||
void SetTagIndex(int32 tagIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the tag.
|
||||
/// </summary>
|
||||
/// <param name="tagName">Name of the tag.</param>
|
||||
API_PROPERTY() void SetTag(const StringView& tagName);
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
API_FUNCTION() bool HasTag(const StringView& tag) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor name.
|
||||
@@ -229,6 +207,9 @@ public:
|
||||
T* result = (T*)GetChild(T::GetStaticClass());
|
||||
if (!result)
|
||||
{
|
||||
if (T::GetStaticClass()->IsAbstract())
|
||||
return nullptr;
|
||||
|
||||
result = New<T>();
|
||||
result->SetParent(this, false, false);
|
||||
}
|
||||
@@ -955,13 +936,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when tag gets changed.
|
||||
/// </summary>
|
||||
virtual void OnTagChanged()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when adding object to the game.
|
||||
/// </summary>
|
||||
|
||||
@@ -166,7 +166,6 @@ Action Level::ScriptsReload;
|
||||
Action Level::ScriptsReloaded;
|
||||
Action Level::ScriptsReloadEnd;
|
||||
#endif
|
||||
Array<String> Level::Tags;
|
||||
String Level::Layers[32];
|
||||
|
||||
bool LevelImpl::spawnActor(Actor* actor, Actor* parent)
|
||||
@@ -177,6 +176,12 @@ bool LevelImpl::spawnActor(Actor* actor, Actor* parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actor->GetType().ManagedClass->IsAbstract())
|
||||
{
|
||||
Log::Exception(TEXT("Cannot spawn abstract actor type."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actor->Is<Scene>())
|
||||
{
|
||||
// Spawn scene
|
||||
@@ -226,8 +231,7 @@ void LayersAndTagsSettings::Apply()
|
||||
// Tags/Layers are stored as index in actors so collection change would break the linkage
|
||||
for (auto& tag : Tags)
|
||||
{
|
||||
if (!Level::Tags.Contains(tag))
|
||||
Level::Tags.Add(tag);
|
||||
Tags::Get(tag);
|
||||
}
|
||||
for (int32 i = 0; i < ARRAY_COUNT(Level::Layers); i++)
|
||||
{
|
||||
@@ -735,17 +739,6 @@ void LevelImpl::CallSceneEvent(SceneEventType eventType, Scene* scene, Guid scen
|
||||
}
|
||||
}
|
||||
|
||||
int32 Level::GetOrAddTag(const StringView& tag)
|
||||
{
|
||||
int32 index = Tags.Find(tag);
|
||||
if (index == INVALID_INDEX)
|
||||
{
|
||||
index = Tags.Count();
|
||||
Tags.AddOne() = tag;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int32 Level::GetNonEmptyLayerNamesCount()
|
||||
{
|
||||
int32 result = 31;
|
||||
@@ -768,6 +761,59 @@ int32 Level::GetLayerIndex(const StringView& layer)
|
||||
return result;
|
||||
}
|
||||
|
||||
Actor* FindActorRecursive(Actor* node, const Tag& tag)
|
||||
{
|
||||
if (node->HasTag(tag))
|
||||
return node;
|
||||
Actor* result = nullptr;
|
||||
for (Actor* child : node->Children)
|
||||
{
|
||||
result = FindActorRecursive(child, tag);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Actor* Level::FindActor(const Tag& tag, Actor* root)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
if (root)
|
||||
return FindActorRecursive(root, tag);
|
||||
Actor* result = nullptr;
|
||||
for (Scene* scene : Scenes)
|
||||
{
|
||||
result = FindActorRecursive(scene, tag);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FindActorRecursive(Actor* node, const Tag& tag, Array<Actor*>& result)
|
||||
{
|
||||
if (node->HasTag(tag))
|
||||
result.Add(node);
|
||||
for (Actor* child : node->Children)
|
||||
FindActorRecursive(child, tag, result);
|
||||
}
|
||||
|
||||
Array<Actor*> Level::FindActors(const Tag& tag, Actor* root)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
Array<Actor*> result;
|
||||
if (root)
|
||||
{
|
||||
FindActorRecursive(root, tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Scene* scene : Scenes)
|
||||
FindActorRecursive(scene, tag);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
@@ -16,6 +16,7 @@ class Engine;
|
||||
struct RenderView;
|
||||
struct RenderContext;
|
||||
struct RenderContextBatch;
|
||||
struct Tag;
|
||||
|
||||
/// <summary>
|
||||
/// The scene manager that contains the loaded scenes collection and spawns/deleted actors.
|
||||
@@ -445,23 +446,11 @@ public:
|
||||
static void ConstructParentActorsTreeList(const Array<Actor*>& input, Array<Actor*>& output);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The tags names.
|
||||
/// </summary>
|
||||
static Array<String> Tags;
|
||||
|
||||
/// <summary>
|
||||
/// The layers names.
|
||||
/// </summary>
|
||||
static String Layers[32];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or adds the tag (returns the tag index).
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag.</param>
|
||||
/// <returns>The tag index.</returns>
|
||||
static int32 GetOrAddTag(const StringView& tag);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of non empty layer names (from the beginning, trims the last ones).
|
||||
/// </summary>
|
||||
@@ -473,6 +462,23 @@ public:
|
||||
/// </summary>
|
||||
API_FUNCTION() static int32 GetLayerIndex(const StringView& layer);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Tries to find the actor with the given tag (returns the first one found).
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag of the actor to search for.</param>
|
||||
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||
/// <returns>Found actor or null.</returns>
|
||||
API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the actors with the given tag (returns all found).
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag of the actor to search for.</param>
|
||||
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||
/// <returns>Found actors or empty if none.</returns>
|
||||
API_FUNCTION() static Array<Actor*> FindActors(const Tag& tag, Actor* root = nullptr);
|
||||
|
||||
private:
|
||||
// Actor API
|
||||
enum class ActorEventType
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "Engine/Core/Math/Triangle.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Physics/CollisionData.h"
|
||||
#include "Engine/Serialization/ISerializable.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Content/Assets/RawDataAsset.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "Engine/Core/Object.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Serialization/ISerializable.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Renderer/Lightmaps.h"
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Serialization/ISerializable.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
|
||||
class SceneTicking;
|
||||
|
||||
125
Source/Engine/Level/Tags.cpp
Normal file
125
Source/Engine/Level/Tags.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Tags.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
#include "Engine/Serialization/SerializationFwd.h"
|
||||
|
||||
Array<String> Tags::List;
|
||||
#if !BUILD_RELEASE
|
||||
FLAXENGINE_API String* TagsListDebug = nullptr;
|
||||
#endif
|
||||
|
||||
const String& Tag::ToString() const
|
||||
{
|
||||
return Index >= 0 && Index < Tags::List.Count() ? Tags::List.Get()[Index] : String::Empty;
|
||||
}
|
||||
|
||||
bool Tag::operator==(const StringView& other) const
|
||||
{
|
||||
return ToString() == other;
|
||||
}
|
||||
|
||||
bool Tag::operator!=(const StringView& other) const
|
||||
{
|
||||
return ToString() != other;
|
||||
}
|
||||
|
||||
void FLAXENGINE_API Serialization::Serialize(ISerializable::SerializeStream& stream, const Tag& v, const void* otherObj)
|
||||
{
|
||||
if (v.Index != -1)
|
||||
stream.String(v.ToString());
|
||||
else
|
||||
stream.String("", 0);
|
||||
}
|
||||
|
||||
void FLAXENGINE_API Serialization::Deserialize(ISerializable::DeserializeStream& stream, Tag& v, ISerializeModifier* modifier)
|
||||
{
|
||||
v = Tags::Get(stream.GetText());
|
||||
}
|
||||
|
||||
Tag Tags::Get(const StringView& tagName)
|
||||
{
|
||||
if (tagName.IsEmpty())
|
||||
return Tag();
|
||||
Tag tag = List.Find(tagName);
|
||||
if (tag.Index == -1 && tagName.HasChars())
|
||||
{
|
||||
tag.Index = List.Count();
|
||||
List.AddOne() = tagName;
|
||||
#if !BUILD_RELEASE
|
||||
TagsListDebug = List.Get();
|
||||
#endif
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
bool Tags::HasTag(const Array<Tag>& list, const Tag& tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
return false;
|
||||
const String& tagName = tag.ToString();
|
||||
for (const Tag& e : list)
|
||||
{
|
||||
const String& eName = e.ToString();
|
||||
if (e == tag || (eName.Length() > tagName.Length() + 1 && eName.StartsWith(tagName) && eName[tagName.Length()] == '.'))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tags::HasTagExact(const Array<Tag>& list, const Tag& tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
return false;
|
||||
return list.Contains(tag);
|
||||
}
|
||||
|
||||
bool Tags::HasAny(const Array<Tag>& list, const Array<Tag>& tags)
|
||||
{
|
||||
for (const Tag& tag : tags)
|
||||
{
|
||||
if (HasTag(list, tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tags::HasAnyExact(const Array<Tag>& list, const Array<Tag>& tags)
|
||||
{
|
||||
for (const Tag& tag : tags)
|
||||
{
|
||||
if (HasTagExact(list, tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tags::HasAll(const Array<Tag>& list, const Array<Tag>& tags)
|
||||
{
|
||||
if (tags.IsEmpty())
|
||||
return true;
|
||||
for (const Tag& tag : tags)
|
||||
{
|
||||
if (!HasTag(list, tag))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Tags::HasAllExact(const Array<Tag>& list, const Array<Tag>& tags)
|
||||
{
|
||||
if (tags.IsEmpty())
|
||||
return true;
|
||||
for (const Tag& tag : tags)
|
||||
{
|
||||
if (!HasTagExact(list, tag))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const String& Tags::GetTagName(int32 tag)
|
||||
{
|
||||
return Tag(tag).ToString();
|
||||
}
|
||||
235
Source/Engine/Level/Tags.cs
Normal file
235
Source/Engine/Level/Tags.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
partial struct Tag : IEquatable<Tag>, IEquatable<string>, IComparable, IComparable<Tag>, IComparable<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// The default <see cref="Tag"/>.
|
||||
/// </summary>
|
||||
public static Tag Default => new Tag(-1);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Tag" /> struct.
|
||||
/// </summary>
|
||||
/// <param name="index">The tag index.</param>
|
||||
public Tag(int index)
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
|
||||
[System.Runtime.Serialization.OnDeserializing]
|
||||
internal void OnDeserializing(System.Runtime.Serialization.StreamingContext context)
|
||||
{
|
||||
// Initialize structure with default values to replicate C++ deserialization behavior
|
||||
Index = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two tags.
|
||||
/// </summary>
|
||||
/// <param name="left">The lft tag.</param>
|
||||
/// <param name="right">The right tag.</param>
|
||||
/// <returns>True if both values are equal, otherwise false.</returns>
|
||||
public static bool operator ==(Tag left, Tag right)
|
||||
{
|
||||
return left.Index == right.Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two tags.
|
||||
/// </summary>
|
||||
/// <param name="left">The lft tag.</param>
|
||||
/// <param name="right">The right tag.</param>
|
||||
/// <returns>True if both values are not equal, otherwise false.</returns>
|
||||
public static bool operator !=(Tag left, Tag right)
|
||||
{
|
||||
return left.Index == right.Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if tag is valid.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
/// <returns>True if tag is valid, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator bool(Tag tag)
|
||||
{
|
||||
return tag.Index != -1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is string asString)
|
||||
return CompareTo(asString);
|
||||
if (obj is Tag asTag)
|
||||
return CompareTo(asTag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(Tag other)
|
||||
{
|
||||
return Index == other.Index;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(string other)
|
||||
{
|
||||
return string.Equals(ToString(), other, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(Tag other)
|
||||
{
|
||||
return string.Compare(ToString(), ToString(), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(string other)
|
||||
{
|
||||
return string.Compare(ToString(), other, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Tag other && Index == other.Index;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Tags.Internal_GetTagName(Index);
|
||||
}
|
||||
}
|
||||
|
||||
partial class Tags
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||
public static bool HasTag(this Tag[] list, Tag tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
return false;
|
||||
string tagName = tag.ToString();
|
||||
foreach (Tag e in list)
|
||||
{
|
||||
string eName = e.ToString();
|
||||
if (e == tag || (eName.Length > tagName.Length + 1 && eName.StartsWith(tagName) && eName[tagName.Length] == '.'))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||
public static bool HasTagExact(this Tag[] list, Tag tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
return false;
|
||||
if (list == null)
|
||||
return false;
|
||||
foreach (Tag e in list)
|
||||
{
|
||||
if (e == tag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if any of of the given tags is contained by the list of tags.</returns>
|
||||
public static bool HasAny(this Tag[] list, Tag[] tags)
|
||||
{
|
||||
if (list == null)
|
||||
return false;
|
||||
foreach (Tag tag in tags)
|
||||
{
|
||||
if (HasTag(list, tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains any of the given tags (exact match). For example, HasAnyExact({"A.B", "C"}, {"A"}) returns false, for parents check use HasAny.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if any of the given tags is contained by the list of tags.</returns>
|
||||
public static bool HasAnyExact(this Tag[] list, Tag[] tags)
|
||||
{
|
||||
if (list == null)
|
||||
return false;
|
||||
foreach (Tag tag in tags)
|
||||
{
|
||||
if (HasTagExact(list, tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains all of the given tags (including parent tags check). For example, HasAll({"A.B", "C"}, {"A", "C"}) returns true, for exact check use HasAllExact.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
|
||||
public static bool HasAll(this Tag[] list, Tag[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length == 0)
|
||||
return true;
|
||||
if (list == null || list.Length == 0)
|
||||
return false;
|
||||
foreach (Tag tag in tags)
|
||||
{
|
||||
if (!HasTag(list, tag))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains all of the given tags (exact match). For example, HasAllExact({"A.B", "C"}, {"A", "C"}) returns false, for parents check use HasAll.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
|
||||
public static bool HasAllExact(this Tag[] list, Tag[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length == 0)
|
||||
return true;
|
||||
if (list == null || list.Length == 0)
|
||||
return false;
|
||||
foreach (Tag tag in tags)
|
||||
{
|
||||
if (!HasTagExact(list, tag))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Source/Engine/Level/Tags.h
Normal file
150
Source/Engine/Level/Tags.h
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
|
||||
/// <summary>
|
||||
/// Gameplay tag that represents a hierarchical name of the form 'X.Y.Z' (namespaces separated with a dot). Tags are defined in project LayersAndTagsSettings asset but can be also created from code.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API Tag
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(Tag);
|
||||
|
||||
/// <summary>
|
||||
/// Index of the tag (in global Level.Tags list).
|
||||
/// </summary>
|
||||
API_FIELD() int32 Index = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag name.
|
||||
/// </summary>
|
||||
const String& ToString() const;
|
||||
|
||||
public:
|
||||
Tag() = default;
|
||||
|
||||
FORCE_INLINE Tag(int32 index)
|
||||
: Index(index)
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return Index != -1;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(const Tag& other) const
|
||||
{
|
||||
return Index == other.Index;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator!=(const Tag& other) const
|
||||
{
|
||||
return Index != other.Index;
|
||||
}
|
||||
|
||||
bool operator==(const StringView& other) const;
|
||||
bool operator!=(const StringView& other) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TIsPODType<Tag>
|
||||
{
|
||||
enum { Value = true };
|
||||
};
|
||||
|
||||
inline uint32 GetHash(const Tag& key)
|
||||
{
|
||||
return (uint32)key.Index;
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
namespace Serialization
|
||||
{
|
||||
inline bool ShouldSerialize(const Tag& v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || v != *(Tag*)otherObj;
|
||||
}
|
||||
void FLAXENGINE_API Serialize(ISerializable::SerializeStream& stream, const Tag& v, const void* otherObj);
|
||||
void FLAXENGINE_API Deserialize(ISerializable::DeserializeStream& stream, Tag& v, ISerializeModifier* modifier);
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
/// <summary>
|
||||
/// Gameplay tags utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API Tags
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(Tags);
|
||||
|
||||
/// <summary>
|
||||
/// List of all tags.
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) static Array<String> List;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or adds the tag.
|
||||
/// </summary>
|
||||
/// <param name="tagName">The tag name.</param>
|
||||
/// <returns>The tag.</returns>
|
||||
API_FUNCTION() static Tag Get(const StringView& tagName);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||
static bool HasTag(const Array<Tag>& list, const Tag& tag);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tag">The tag to check.</param>
|
||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||
static bool HasTagExact(const Array<Tag>& list, const Tag& tag);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if any of of the given tags is contained by the list of tags.</returns>
|
||||
static bool HasAny(const Array<Tag>& list, const Array<Tag>& tags);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains any of the given tags (exact match). For example, HasAnyExact({"A.B", "C"}, {"A"}) returns false, for parents check use HasAny.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if any of the given tags is contained by the list of tags.</returns>
|
||||
static bool HasAnyExact(const Array<Tag>& list, const Array<Tag>& tags);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains all of the given tags (including parent tags check). For example, HasAll({"A.B", "C"}, {"A", "C"}) returns true, for exact check use HasAllExact.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
|
||||
static bool HasAll(const Array<Tag>& list, const Array<Tag>& tags);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the list of tags contains all of the given tags (exact match). For example, HasAllExact({"A.B", "C"}, {"A", "C"}) returns false, for parents check use HasAll.
|
||||
/// </summary>
|
||||
/// <param name="list">The tags list to use.</param>
|
||||
/// <param name="tags">The tags to check.</param>
|
||||
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
|
||||
static bool HasAllExact(const Array<Tag>& list, const Array<Tag>& tags);
|
||||
|
||||
private:
|
||||
API_FUNCTION(NoProxy) static const String& GetTagName(int32 tag);
|
||||
};
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
extern FLAXENGINE_API String* TagsListDebug; // Used by flax.natvis
|
||||
#endif
|
||||
Reference in New Issue
Block a user