Added prefab instancing through C# and support for mid-game rigidbody and skeletal animation

This commit is contained in:
Antoine Pilote
2024-04-23 23:22:19 -04:00
parent f854a2d783
commit 39025d8b06
7 changed files with 173 additions and 62 deletions

View File

@@ -13,6 +13,8 @@ namespace Nuake {
std::string ScriptPath;
std::string Class;
bool Initialized = false;
json Serialize()
{
BEGIN_SERIALIZE();

View File

@@ -43,6 +43,10 @@ namespace Nuake
return;
}
InitializeShapes();
InitializeRigidbodies();
InitializeCharacterControllers();
ApplyForces();
PhysicsManager::Get().Step(ts);
@@ -74,8 +78,6 @@ namespace Nuake
if (!Engine::IsPlayMode())
return;
InitializeRigidbodies();
auto view = m_Scene->m_Registry.view<TransformComponent, RigidBodyComponent>();
for (auto e : view)
{
@@ -143,32 +145,44 @@ namespace Nuake
for (auto entity : boxView)
{
auto& boxComponent = boxView.get<BoxColliderComponent>(entity);
boxComponent.Box = CreateRef<Physics::Box>(boxComponent.Size);
if (!boxComponent.Box)
{
boxComponent.Box = CreateRef<Physics::Box>(boxComponent.Size);
}
}
const auto capsuleView = m_Scene->m_Registry.view<CapsuleColliderComponent>();
for (auto entity : capsuleView)
{
auto& capsuleComponent = capsuleView.get<CapsuleColliderComponent>(entity);
float radius = capsuleComponent.Radius;
float height = capsuleComponent.Height;
capsuleComponent.Capsule = CreateRef<Physics::Capsule>(radius, height);
if (!capsuleComponent.Capsule)
{
float radius = capsuleComponent.Radius;
float height = capsuleComponent.Height;
capsuleComponent.Capsule = CreateRef<Physics::Capsule>(radius, height);
}
}
const auto cylinderView = m_Scene->m_Registry.view<CylinderColliderComponent>();
for (auto entity : cylinderView)
{
auto& cylinderComponent = cylinderView.get<CylinderColliderComponent>(entity);
float radius = cylinderComponent.Radius;
float height = cylinderComponent.Height;
cylinderComponent.Cylinder = CreateRef<Physics::Cylinder>(radius, height);
if (!cylinderComponent.Cylinder)
{
float radius = cylinderComponent.Radius;
float height = cylinderComponent.Height;
cylinderComponent.Cylinder = CreateRef<Physics::Cylinder>(radius, height);
}
}
const auto sphereView = m_Scene->m_Registry.view<SphereColliderComponent>();
for (auto entity : sphereView)
{
auto& sphereComponent = sphereView.get<SphereColliderComponent>(entity);
sphereComponent.Sphere = CreateRef<Physics::Sphere>(sphereComponent.Radius);
if (!sphereComponent.Sphere)
{
sphereComponent.Sphere = CreateRef<Physics::Sphere>(sphereComponent.Radius);
}
}
const auto meshColliderView = m_Scene->m_Registry.view<MeshColliderComponent>();
@@ -181,19 +195,22 @@ namespace Nuake
}
auto& meshColliderComponent = meshColliderView.get<MeshColliderComponent>(e);
const auto& modelComponent = entity.GetComponent<ModelComponent>();
if (modelComponent.ModelResource)
if (!meshColliderComponent.Shape)
{
uint32_t subMeshId = meshColliderComponent.SubMesh;
const std::vector<Ref<Mesh>>& submeshes = modelComponent.ModelResource->GetMeshes();
if (subMeshId >= submeshes.size())
{
Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING);
}
const auto& modelComponent = entity.GetComponent<ModelComponent>();
Ref<Mesh> mesh = submeshes[subMeshId];
meshColliderComponent.Shape = CreateRef<Physics::MeshShape>(mesh);
if (modelComponent.ModelResource)
{
uint32_t subMeshId = meshColliderComponent.SubMesh;
const std::vector<Ref<Mesh>>& submeshes = modelComponent.ModelResource->GetMeshes();
if (subMeshId >= submeshes.size())
{
Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING);
}
Ref<Mesh> mesh = submeshes[subMeshId];
meshColliderComponent.Shape = CreateRef<Physics::MeshShape>(mesh);
}
}
}
}
@@ -298,6 +315,11 @@ namespace Nuake
Entity entity = Entity({ e, m_Scene });
auto [transformComponent, characterControllerComponent] = characterControllerView.get<TransformComponent, CharacterControllerComponent>(e);
if (characterControllerComponent.GetCharacterController())
{
continue;
}
Ref<Physics::PhysicShape> shape;
if (entity.HasComponent<CapsuleColliderComponent>())
{
@@ -328,6 +350,7 @@ namespace Nuake
characterController->Position = transformComponent.GetGlobalPosition();
characterController->Rotation = transformComponent.GetGlobalRotation();
characterControllerComponent.SetCharacterController(characterController);
PhysicsManager::Get().RegisterCharacterController(characterController);
}
}

View File

@@ -56,6 +56,8 @@ namespace Nuake
// Creates an instance of the entity script in C#
scriptingEngineNet.RegisterEntityScript(entity);
netScriptComponent.Initialized = true;
}
for (auto& e : netEntities)
@@ -89,6 +91,40 @@ namespace Nuake
auto& scriptingEngineNet = ScriptingEngineNet::Get();
auto netEntities = m_Scene->m_Registry.view<NetScriptComponent>();
// Instance all the new entities
std::vector<Entity> entityJustInstanced;
for (auto& e : netEntities)
{
auto& netScriptComponent = netEntities.get<NetScriptComponent>(e);
if (!netScriptComponent.Initialized)
{
if (netScriptComponent.ScriptPath.empty())
continue;
auto entity = Entity{ e, m_Scene };
// Creates an instance of the entity script in C#
scriptingEngineNet.RegisterEntityScript(entity);
netScriptComponent.Initialized = true;
entityJustInstanced.push_back(entity);
}
}
// Call init on the newly created instance
for (auto& e : entityJustInstanced)
{
auto& netScriptComponent = e.GetComponent<NetScriptComponent>();
if (e.IsValid() && scriptingEngineNet.HasEntityScriptInstance(e))
{
// We can now call on init on it.
auto scriptInstance = scriptingEngineNet.GetEntityScript(e);
scriptInstance.InvokeMethod("OnInit");
}
}
// Then call update on all the other scripts
for (auto& e : netEntities)
{
NetScriptComponent& netScriptComponent = netEntities.get<NetScriptComponent>(e);

View File

@@ -75,6 +75,17 @@ namespace Nuake {
}
}
int InstancePrefab(Coral::String path)
{
if (!FileSystem::FileExists(path))
{
return -1;
}
const auto& prefab = Prefab::New(path);
return prefab->Root.GetHandle();
}
static enum ComponentTypes
{
Unknown = -1,
@@ -291,11 +302,12 @@ namespace Nuake {
RegisterMethod("Entity.EntityHasComponentIcall", &EntityHasComponent);
RegisterMethod("Entity.EntityHasManagedInstanceIcall", &EntityHasManagedInstance);
RegisterMethod("Entity.EntityGetEntityIcall", &EntityGetEntity);
// Scene
RegisterMethod("Scene.GetEntityIcall", &GetEntity);
RegisterMethod("Scene.GetEntityScriptFromNameIcall", &GetEntityScriptFromName);
RegisterMethod("Scene.GetEntityScriptFromHandleIcall", &GetEntityScriptFromHandle);
RegisterMethod("Scene.InstancePrefabIcall", &InstancePrefab);
// Components
RegisterMethod("TransformComponent.SetPositionIcall", &TransformSetPosition);
RegisterMethod("TransformComponent.GetPositionIcall", &TransformGetPosition);

View File

@@ -181,47 +181,9 @@ namespace Nuake
auto& component = entity.GetComponent<NetScriptComponent>();
const auto& filePath = component.ScriptPath;
if (filePath.empty())
{
Logger::Log("Skipped .net entity script since it was empty.", ".net", VERBOSE);
return;
}
const std::string& className = FindClassNameInScript(filePath);
if (!FileSystem::FileExists(filePath))
{
Logger::Log("Skipped .net entity script: The file path doesn't exist.", ".net", WARNING);
return;
}
// We can now scan the file and look for this pattern: class XXXXX : Entity
// Warning, this doesnt do any bound check so if there is a semicolon at the end
// of the file. IT MIGHT CRASH HERE. POTENTIALLY - I have not tested.
// -----------------------------------------------------
std::string fileContent = FileSystem::ReadFile(filePath);
fileContent = fileContent.substr(3, fileContent.size());
fileContent = String::RemoveWhiteSpace(fileContent);
// Find class token
size_t classTokenPos = fileContent.find("class");
if (classTokenPos == std::string::npos)
{
Logger::Log("Skipped .net entity script: file doesnt contain entity class.", ".net", WARNING);
return;
}
size_t classNameStartIndex = classTokenPos + 5; // 4 letter: class + 1 for next char
// Find semi-colon token
size_t semiColonPos = fileContent.find(":");
if (semiColonPos == std::string::npos || semiColonPos < classTokenPos)
{
Logger::Log("Skipped .net entity script: Not class inheriting Entity was found.", ".net", WARNING);
return;
}
size_t classNameLength = semiColonPos - classNameStartIndex;
const std::string className = fileContent.substr(classNameStartIndex, classNameLength);
if(m_GameEntityTypes.find(className) == m_GameEntityTypes.end())
{
// The class name parsed in the file was not found in the game's DLL.
@@ -343,4 +305,48 @@ namespace NuakeShowcase
)";
}
std::string ScriptingEngineNet::FindClassNameInScript(const std::string & filePath)
{
if (filePath.empty())
{
Logger::Log("Skipped .net entity script since it was empty.", ".net", VERBOSE);
return "";
}
if (!FileSystem::FileExists(filePath))
{
Logger::Log("Skipped .net entity script: The file path doesn't exist.", ".net", WARNING);
return "";
}
// We can now scan the file and look for this pattern: class XXXXX : Entity
// Warning, this doesnt do any bound check so if there is a semicolon at the end
// of the file. IT MIGHT CRASH HERE. POTENTIALLY - I have not tested.
// -----------------------------------------------------
std::string fileContent = FileSystem::ReadFile(filePath);
fileContent = fileContent.substr(3, fileContent.size());
fileContent = String::RemoveWhiteSpace(fileContent);
// Find class token
size_t classTokenPos = fileContent.find("class");
if (classTokenPos == std::string::npos)
{
Logger::Log("Skipped .net entity script: file doesnt contain entity class.", ".net", WARNING);
return "";
}
size_t classNameStartIndex = classTokenPos + 5; // 4 letter: class + 1 for next char
// Find semi-colon token
size_t semiColonPos = fileContent.find(":");
if (semiColonPos == std::string::npos || semiColonPos < classTokenPos)
{
Logger::Log("Skipped .net entity script: Not class inheriting Entity was found.", ".net", WARNING);
return "";
}
size_t classNameLength = semiColonPos - classNameStartIndex;
return fileContent.substr(classNameStartIndex, classNameLength);
}
}

View File

@@ -61,5 +61,7 @@ namespace Nuake {
void CopyNuakeNETAssemblies(const std::string& path);
void GenerateSolution(const std::string& path, const std::string& projectName);
void CreateEntityScript(const std::string& path, const std::string& entityName);
std::string FindClassNameInScript(const std::string& filePath);
};
}

View File

@@ -1,5 +1,6 @@
using Coral.Managed.Interop;
using System;
using System.Reflection.Metadata;
namespace Nuake.Net
{
@@ -8,6 +9,7 @@ namespace Nuake.Net
internal static unsafe delegate*<NativeString, int> GetEntityIcall;
internal static unsafe delegate*<NativeString, NativeInstance<Entity>> GetEntityScriptFromNameIcall;
internal static unsafe delegate*<int, NativeInstance<Entity>> GetEntityScriptFromHandleIcall;
internal static unsafe delegate*<NativeString, int> InstancePrefabIcall;
public static T? GetEntity<T>(string entityName) where T : class
{
@@ -22,6 +24,7 @@ namespace Nuake.Net
return null;
}
public static T? GetEntity<T>(int entityHandle) where T : class
{
NativeInstance<Entity> handle;
@@ -53,5 +56,32 @@ namespace Nuake.Net
return entity;
}
public static Entity? InstancePrefab(string path, Entity? parent = null)
{
int handle;
unsafe { handle = InstancePrefabIcall(path); }
if (handle == -1)
{
return null;
}
//NativeInstance<Entity> nativeInstance;
//unsafe { nativeInstance = GetEntityScriptFromHandleIcall(handle); }
//
//Entity? entity = nativeInstance.Get();
//if (entity != null && entity is T)
//{
// return entity as T;
//}
Entity entity = new Entity
{
ECSHandle = handle
};
return entity;
}
}
}