Advertisement

Script instance in other script

Started by February 25, 2025 09:58 AM
1 comment, last by WitchLord 2 days, 11 hours ago

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!

You can use shared entities to allow script to exchange data without the engine having to know about the details of those entities.

https://angelcode.com/angelscript/sdk/docs/manual/doc_script_shared.html

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement