diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade
index 443d4a87..585582e4 100644
--- a/Assets/Scenes/M2Scene.shade
+++ b/Assets/Scenes/M2Scene.shade
@@ -256,4 +256,7 @@
Color: {x: 1, y: 1, z: 1, w: 1}
Layer: 4294967295
Strength: 0.25
- Scripts: ~
\ No newline at end of file
+ Scripts:
+ - Type: PickAndThrow
+ throwForce: [100, 200, 100]
+ item: 1
\ No newline at end of file
diff --git a/SHADE_Engine/src/Editor/SHEditorUI.cpp b/SHADE_Engine/src/Editor/SHEditorUI.cpp
index cc63c565..49cfbfd6 100644
--- a/SHADE_Engine/src/Editor/SHEditorUI.cpp
+++ b/SHADE_Engine/src/Editor/SHEditorUI.cpp
@@ -288,7 +288,7 @@ namespace SHADE
return CHANGED;
}
- bool SHEditorUI::InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered)
+ bool SHEditorUI::InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered, bool alwaysNull)
{
ImGui::Text(label.c_str());
if (isHovered)
@@ -296,7 +296,7 @@ namespace SHADE
ImGui::SameLine();
SHEntity* entity = SHEntityManager::GetEntityByID(value);
std::ostringstream oss;
- if (entity)
+ if (!alwaysNull && entity)
{
oss << value << ": " << entity->name;
}
@@ -314,6 +314,13 @@ namespace SHADE
SHDragDrop::EndTarget();
}
}
+ ImGui::SameLine();
+ if (ImGui::Button("Clear"))
+ {
+ value = MAX_EID;
+ changed = true;
+ }
+
return changed;
}
diff --git a/SHADE_Engine/src/Editor/SHEditorUI.h b/SHADE_Engine/src/Editor/SHEditorUI.h
index d4104639..4e8f4400 100644
--- a/SHADE_Engine/src/Editor/SHEditorUI.h
+++ b/SHADE_Engine/src/Editor/SHEditorUI.h
@@ -313,8 +313,12 @@ namespace SHADE
/// Label used to identify this widget.
/// Reference to the variable to store the result.
///
+ /// If set, the field displayed will always be blank regardless of specified
+ /// GameObject.
+ ///
/// True if the value was changed.
- static bool InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered = nullptr);
+ static bool InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered = nullptr, bool alwaysNull = false);
///
/// Creates a combo box for enumeration input.
///
diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx
index 55fe91da..2afe9697 100644
--- a/SHADE_Managed/src/Editor/Editor.cxx
+++ b/SHADE_Managed/src/Editor/Editor.cxx
@@ -290,9 +290,14 @@ namespace SHADE
{
GameObject gameObj = safe_cast(field->GetValue(object));
uint32_t entityId = gameObj.GetEntity();
- if (SHEditorUI::InputGameObjectField(Convert::ToNative(field->Name), entityId, &isHovered))
+ if (SHEditorUI::InputGameObjectField(Convert::ToNative(field->Name), entityId, &isHovered, !gameObj))
{
GameObject newVal = GameObject(entityId);
+ if (entityId != MAX_EID)
+ {
+ // Null GameObject set
+ newVal = GameObject(entityId);
+ }
field->SetValue(object, newVal);
registerUndoAction(object, field, newVal, gameObj);
}
diff --git a/SHADE_Managed/src/Engine/GameObject.cxx b/SHADE_Managed/src/Engine/GameObject.cxx
index 7ceabfed..9f15c6c9 100644
--- a/SHADE_Managed/src/Engine/GameObject.cxx
+++ b/SHADE_Managed/src/Engine/GameObject.cxx
@@ -6,9 +6,9 @@
\brief Contains the definition of the functions for the GameObject managed class.
Note: This file is written in C++17/CLI.
-
+
Copyright (C) 2021 DigiPen Institute of Technology.
-Reproduction or disclosure of this file or its contents without the prior written consent
+Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// Precompiled Headers
@@ -36,10 +36,12 @@ namespace SHADE
void GameObject::Destroy(GameObject obj)
{
+ if (!obj.valid)
+ throw gcnew System::NullReferenceException("Attempted to destroy a null GameObject.");
SHEntityManager::DestroyEntity(static_cast(obj.GetEntity()));
}
- System::Nullable GameObject::Find(System::String ^ name)
+ System::Nullable GameObject::Find(System::String^ name)
{
// Search the GameObjectLibrary for an Entity with the specified name
const auto ENTITY_ID = SHEntityManager::GetEntityByName(Convert::ToNative(name));
@@ -50,21 +52,27 @@ namespace SHADE
return GameObject(ENTITY_ID);
}
-
+
/*---------------------------------------------------------------------------------*/
/* Properties */
/*---------------------------------------------------------------------------------*/
System::String^ GameObject::Name::get()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return Convert::ToCLI(GetNativeEntity().name);
-
- }
+
+ }
bool GameObject::IsActiveSelf::get()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return GetNativeEntity().GetActive();
}
bool GameObject::IsActiveInHierarchy::get()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
auto node = SHSceneManager::GetCurrentSceneGraph().GetNode(GetEntity());
if (!node)
{
@@ -75,39 +83,49 @@ namespace SHADE
}
Entity GameObject::EntityId::get()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return entity;
}
GameObject^ GameObject::Parent::get()
{
- const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph();
- const auto* ROOT = SCENE_GRAPH.GetRoot();
+ if (!valid)
+ throw gcnew System::NullReferenceException();
+ const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph();
+ const auto* ROOT = SCENE_GRAPH.GetRoot();
- const auto* NODE = SCENE_GRAPH.GetNode(entity);
- if (NODE == nullptr)
- throw gcnew System::InvalidOperationException("Unable to retrieve SceneGraphNode for Entity " + entity.ToString());
+ const auto* NODE = SCENE_GRAPH.GetNode(entity);
+ if (NODE == nullptr)
+ throw gcnew System::InvalidOperationException("Unable to retrieve SceneGraphNode for Entity " + entity.ToString());
- const auto* PARENT = NODE->GetParent();
- return PARENT != ROOT ? gcnew GameObject(PARENT->GetEntityID()) : nullptr;
+ const auto* PARENT = NODE->GetParent();
+ return PARENT != ROOT ? gcnew GameObject(PARENT->GetEntityID()) : nullptr;
}
void GameObject::Parent::set(GameObject^ newParent)
{
- const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph();
+ if (!valid)
+ throw gcnew System::NullReferenceException();
+ const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph();
- if (newParent == nullptr)
- SCENE_GRAPH.SetParent(entity, nullptr);
- else
- SCENE_GRAPH.SetParent(entity, newParent->EntityId);
+ if (newParent == nullptr)
+ SCENE_GRAPH.SetParent(entity, nullptr);
+ else
+ SCENE_GRAPH.SetParent(entity, newParent->EntityId);
}
-
+
/*---------------------------------------------------------------------------------*/
/* GameObject Property Functions */
/*---------------------------------------------------------------------------------*/
void GameObject::SetName(System::String^ name)
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
GetNativeEntity().name = Convert::ToNative(name);
}
void GameObject::SetActive(bool active)
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
GetNativeEntity().SetActive(active);
}
@@ -117,63 +135,83 @@ namespace SHADE
generic
T GameObject::AddComponent()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ECS::AddComponent(entity);
}
generic
T GameObject::GetComponent()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ECS::GetComponent(entity);
}
generic
T GameObject::GetComponentInChildren()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ECS::GetComponentInChildren(entity);
}
generic
T GameObject::EnsureComponent()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ECS::EnsureComponent(entity);
}
generic
void GameObject::RemoveComponent()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
ECS::RemoveComponent(entity);
}
-
+
/*---------------------------------------------------------------------------------*/
/* Script Access Functions */
/*---------------------------------------------------------------------------------*/
generic
T GameObject::AddScript()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ScriptStore::AddScript(entity);
}
generic
T GameObject::GetScript()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ScriptStore::GetScript(entity);
}
generic
T GameObject::GetScriptInChildren()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ScriptStore::GetScriptInChildren(entity);
}
generic
System::Collections::Generic::IEnumerable^ GameObject::GetScripts()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
return ScriptStore::GetScripts(entity);
}
generic
void GameObject::RemoveScript()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
ScriptStore::RemoveScript(entity);
}
@@ -181,20 +219,24 @@ namespace SHADE
/* Constructors */
/*---------------------------------------------------------------------------------*/
GameObject::GameObject(const SHEntity& entity)
- : entity { entity.GetEID() }
- , children{ gcnew System::Collections::ArrayList }
+ : entity{ entity.GetEID() }
+ , children{ gcnew System::Collections::ArrayList }
+ , valid{ true }
{}
GameObject::GameObject(Entity entity)
- : entity { entity }
- , children{ gcnew System::Collections::ArrayList }
+ : entity{ entity }
+ , children{ gcnew System::Collections::ArrayList }
+ , valid{ true }
{}
-
+
/*---------------------------------------------------------------------------------*/
/* Getters */
/*---------------------------------------------------------------------------------*/
SHEntity& GameObject::GetNativeEntity()
{
+ if (!valid)
+ throw gcnew System::NullReferenceException();
SHEntity* nativeEntity = SHEntityManager::GetEntityByID(entity);
if (nativeEntity == nullptr)
throw gcnew System::InvalidOperationException("[GameObject] Unable to obtain native Entity for GameObject.");
@@ -202,14 +244,22 @@ namespace SHADE
return *nativeEntity;
}
+ /*---------------------------------------------------------------------------------*/
+ /* Operator Overloads */
+ /*---------------------------------------------------------------------------------*/
+ GameObject::operator bool(GameObject gameObj)
+ {
+ return gameObj.valid;
+ }
+
/*---------------------------------------------------------------------------------*/
/* IEquatable */
/*---------------------------------------------------------------------------------*/
bool GameObject::Equals(GameObject other)
{
- return entity == other.entity;
+ return (!valid && !other.valid) || entity == other.entity;
}
-
+
/*---------------------------------------------------------------------------------*/
/* Object */
/*---------------------------------------------------------------------------------*/
diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx
index 99296a91..030b917c 100644
--- a/SHADE_Managed/src/Engine/GameObject.hxx
+++ b/SHADE_Managed/src/Engine/GameObject.hxx
@@ -29,8 +29,9 @@ namespace SHADE
/* Class Definitions */
/*---------------------------------------------------------------------------------*/
///
- /// Lightweight object for an PlushieEngine Entity that allows for easy access
- /// to Component and Script operations.
+ /// Lightweight object for an Entity that allows for easy access to Component and
+ /// Script operations.
+ /// Can be set to a invalid/null GameObject by default construction.
///
public value class GameObject : public System::IEquatable
{
@@ -98,8 +99,8 @@ namespace SHADE
///
property GameObject^ Parent
{
- GameObject^ get();
- void set(GameObject^);
+ GameObject^ get();
+ void set(GameObject^);
}
/*-----------------------------------------------------------------------------*/
@@ -247,12 +248,22 @@ namespace SHADE
/// Native Entity object that this GameObject represents.
SHEntity& GetNativeEntity();
+ /*-----------------------------------------------------------------------------*/
+ /* Operator Overloads */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Implicit conversion operator to enable checking if a GameObject is valid.
+ ///
+ /// GameObjects to check.
+ static operator bool(GameObject gameObj);
+
private:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
Entity entity;
System::Collections::ArrayList^ children;
+ bool valid;
public:
/*-----------------------------------------------------------------------------*/
diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx
index 4b4e44fc..651afb73 100644
--- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx
+++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx
@@ -171,7 +171,7 @@ namespace SHADE
else if (fieldInfo->FieldType == GameObject::typeid)
{
GameObject gameObj = safe_cast(fieldInfo->GetValue(object));
- fieldNode = gameObj.GetEntity();
+ fieldNode = gameObj ? gameObj.GetEntity() : MAX_EID;
}
else // Not any of the supported types
{
@@ -250,7 +250,8 @@ namespace SHADE
}
else if (fieldInfo->FieldType == GameObject::typeid)
{
- fieldInfo->SetValue(object, GameObject(node.as()));
+ const uint32_t EID = node.as();
+ fieldInfo->SetValue(object, EID == MAX_EID ? GameObject() : GameObject(EID));
}
else // Not any of the supported types
{