Hi, I'm trying to recreate a Unity-like scripting system using Angelscript. I have an ECS and each entity can hold scripts. Each script has a start, update, and end. These function get called from the C++ side for updating.
Right now, I can access the default components I made in C++ (like transform). But what would be even better is if I could access a script instance from within a different script. Without knowing whats inside the script beforehand on the C++ side. For example:
class Player {
float health;
Player(GameEntity e) {
}
void OnStart() {
}
void OnUpdate(float dt) {
}
void OnEnd() {
}
void TakeDamage(float hp){
health -= hp;
}
}
Then in a different script:
class Manager {
GameEntity player;
Manager(GameEntity e) {
}
void OnStart() {
}
void OnUpdate(float dt) {
player.GetScript("Player").TakeDamage(10);
//or
player.GetPlayerScript().TakeDamage(10);
}
void OnEnd() {
}
}
Would this be possible?
For more context. This is currently the way I initialize the scripts:
void kudzu::Script::InitializeScript(const std::string& scriptName, const std::string& className, const std::string& source)
{
path = scriptName;
m_className = className;
m_source = source;
asIScriptEngine* engine = kudzu::Engine.scripting_engine().get_engine();
if (!engine)
{
kudzu::Log::Error("ScriptResource: engine is null");
return;
}
CScriptBuilder builder;
int r;
r = builder.StartNewModule(engine, scriptName.c_str());
assert(r >= 0);
r = builder.AddSectionFromMemory(scriptName.c_str(), source.c_str());
if (r < 0)
{
kudzu::Log::Error("AddSectionFromMemory failed");
return;
}
r = builder.BuildModule();
if (r < 0)
{
kudzu::Log::Error("BuildModule failed");
return;
}
m_module = engine->GetModule(scriptName.c_str());
if (!m_module)
{
kudzu::Log::Error("Failed to create module for script: {}", scriptName);
return;
}
m_scriptType = m_module->GetTypeInfoByName(className.c_str());
if (!m_scriptType)
{
kudzu::Log::Error("Could not find class '{}' in script.", className);
}
int typeId = m_scriptType->GetTypeId();
int propertyCount = m_scriptType->GetPropertyCount();
for (int i = 0; i < propertyCount; i++)
{
const char* propName;
int propTypeId;
r = m_scriptType->GetProperty(i, &propName, &propTypeId);
if (r < 0)
{
kudzu::Log::Error("Failed to get property info");
continue;
}
std::vector<std::string> metadata = builder.GetMetadataForTypeProperty(typeId, i);
for (auto& md : metadata)
{
std::string typeDecl = engine->GetTypeDeclaration(propTypeId);
auto info = PropertyInfo(i, propName, propTypeId, typeDecl);
if (md == "editable")
{
kudzu::Log::Info("Found editable property: {}", propName);
m_editableProperties.push_back(info);
}
if (md == "serialize")
{
kudzu::Log::Info("Found serialize property: {}", propName);
m_serializableProperties.push_back(info);
}
}
}
}
kudzu::Script::~Script() {}
std::shared_ptr<kudzu::ScriptInstance> kudzu::Script::CreateInstance(const entt::entity entity, const bool addToList)
{
if (!m_scriptType)
{
Log::Error("Failed creating script instance. Script type is null");
return nullptr;
}
bool isValid = IsValid();
asIScriptEngine* engine = kudzu::Engine.scripting_engine().get_engine();
// get constructor
auto decl = m_className + "@ " + m_className + "(GameEntity)";
asIScriptFunction* factory = m_scriptType->GetFactoryByDecl(decl.c_str());
if (!factory)
{
Log::Error("Failed creating script instance. No factory found for script type {}", m_scriptType->GetName());
isValid = false;
}
asIScriptContext* ctx = engine->CreateContext();
int r = ctx->Prepare(factory);
if (r < 0)
{
Log::Error("Failed creating script instance. Failed to prepare factory for script type {}", m_scriptType->GetName());
ctx->Release();
isValid = false;
}
GameEntity* gameEntity = new GameEntity(entity);
ctx->SetArgObject(0, gameEntity);
r = ctx->Execute();
if (r != asEXECUTION_FINISHED)
{
Log::Error("Failed creating script instance. Factory execution didn't finish properly");
ctx->Release();
isValid = false;
}
auto** retAddress = reinterpret_cast<asIScriptObject**>(ctx->GetAddressOfReturnValue());
asIScriptObject* obj = (retAddress ? *retAddress : nullptr);
if (obj)
{
obj->AddRef();
}
ctx->Release();
auto instance = std::make_shared<ScriptInstance>();
instance->instance = obj;
instance->onStart = m_scriptType->GetMethodByName("OnStart");
instance->onUpdate = m_scriptType->GetMethodByName("OnUpdate");
instance->onEnd = m_scriptType->GetMethodByName("OnEnd");
instance->initialized = false;
instance->entity = entity;
instance->valid = isValid;
if (addToList)
{
m_instances.push_back(instance);
}
return instance;
}
Any help or suggestions would be greatly appreciated!