Lua and C++ integration

0

Guys, I am trying to expose a c++ function to Lua just to be able to use windows clipboard, copy and paste. I am not sure this is the best solution but it is what we are doing right now.

We created a GEM and add to the gem a .h and cpp file. This is all we have right now:

.h

	#include "StdAfx.h"
#include <ScriptHelpers.h>
#include <IScriptSystem.h>
class ClipB:
public CScriptableBase
{
public:
ClipB(ISystem* pSystem);
virtual ~ClipB() {}
int GameLog(IFunctionHandler* pH, char* pText);
};

.cpp

	#include "StdAfx.h"
#include <ScriptHelpers.h>
#include <IScriptSystem.h>
#include <ScriptHelpers.h>
#include "ClipB.h"
ClipB::ClipB(ISystem * pSystem)
{
Init(pSystem->GetIScriptSystem(), pSystem);
SetGlobalName("Game");
#undef SCRIPT_REG_CLASSNAME
#define SCRIPT_REG_CLASSNAME &ClipB::SCRIPT_REG_TEMPLFUNC(GameLog, "text");
}
int ClipB::GameLog(IFunctionHandler * pH, char * pText)
{return 0;}

When we run waf to compile this code we receive this error:

c:\Amazon\Lumberyard\1.8.0.1\dev\Code\CryEngine\CryCommon\ScriptHelpers.h(32): error C2061: syntax error: identifier 'IFunctionHandler'

I could not find a fix for this. I am reading all the GEMs and another codes like LyShine but none of them showed me the error.

Could you guys help us?

Tks a lot!

asked 7 years ago169 views
6 Answers
0
Accepted Answer

Honestly, everything is migrating away from ScriptableBase system and moving to Components. This details how to make a component: https://forums.awsgametech.com/t/how-to-create-a-system-component/2553/1

As for exposing lua functions, you use behavior contextes.

Here's an exerpt of the transform component's reflect function and take a look at it's behavior context section of it:

    void TransformComponent::Reflect(AZ::ReflectContext* reflection)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<TransformComponent, AZ::Component, NetBindable>()
->Version(2)
->Field("Parent", &TransformComponent::m_parentId)
->Field("Transform", &TransformComponent::m_worldTM)
->Field("LocalTransform", &TransformComponent::m_localTM)
->Field("ParentActivationTransformMode", &TransformComponent::m_parentActivationTransformMode)
;
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection);
if(behaviorContext)
{
behaviorContext->EBus<AZ::TransformNotificationBus>("TransformNotificationBus")->
Handler<AZ::BehaviorTransformNotificationBusHandler>();
behaviorContext->Class<TransformComponent>()->RequestBus("TransformBus");
behaviorContext->EBus<AZ::TransformBus>("TransformBus")
->Event("GetLocalTM", &AZ::TransformBus::Events::GetLocalTM)
->Event("GetWorldTM", &AZ::TransformBus::Events::GetWorldTM)
->Event("GetParentId", &AZ::TransformBus::Events::GetParentId)
->Event("GetLocalAndWorld", &AZ::TransformBus::Events::GetLocalAndWorld)
->Event("SetLocalTM", &AZ::TransformBus::Events::SetLocalTM)
->Event("SetWorldTM", &AZ::TransformBus::Events::SetWorldTM)
->Event("SetParent", &AZ::TransformBus::Events::SetParent)
->Event("SetParentRelative", &AZ::TransformBus::Events::SetParentRelative)
->Event("SetWorldTranslation", &AZ::TransformBus::Events::SetWorldTranslation)
->Event("SetLocalTranslation", &AZ::TransformBus::Events::SetLocalTranslation)
->Event("GetWorldTranslation", &AZ::TransformBus::Events::GetWorldTranslation)
->Attribute("Position", AZ::Edit::Attributes::PropertyPosition)
->VirtualProperty("Position", "GetWorldTranslation", "SetWorldTranslation")
->Event("GetLocalTranslation", &AZ::TransformBus::Events::GetLocalTranslation)
->Event("MoveEntity", &AZ::TransformBus::Events::MoveEntity)
->Event("SetWorldX", &AZ::TransformBus::Events::SetWorldX)
->Event("SetWorldY", &AZ::TransformBus::Events::SetWorldY)
->Event("SetWorldZ", &AZ::TransformBus::Events::SetWorldZ)
->Event("GetWorldX", &AZ::TransformBus::Events::GetWorldX)
->Event("GetWorldY", &AZ::TransformBus::Events::GetWorldY)
->Event("GetWorldZ", &AZ::TransformBus::Events::GetWorldZ)
->Event("RotateByX", &AZ::TransformBus::Events::RotateByX)
->Event("RotateByY", &AZ::TransformBus::Events::RotateByY)
->Event("RotateByZ", &AZ::TransformBus::Events::RotateByZ)
->Event("SetEulerRotation", &AZ::TransformBus::Events::SetRotation)
->Event("SetRotationQuaternion", &AZ::TransformBus::Events::SetRotationQuaternion)
->Event("SetRotationX", &AZ::TransformBus::Events::SetRotationX)
->Event("SetRotationY", &AZ::TransformBus::Events::SetRotationY)
->Event("SetRotationZ", &AZ::TransformBus::Events::SetRotationZ)
->Event("GetEulerRotation", &AZ::TransformBus::Events::GetRotationEulerRadians)
->Event("GetRotationQuaternion", &AZ::TransformBus::Events::GetRotationQuaternion)
->Attribute("Rotation", AZ::Edit::Attributes::PropertyRotation)
->VirtualProperty("Rotation", "GetRotationQuaternion", "SetRotationQuaternion")
->Event("GetRotationX", &AZ::TransformBus::Events::GetRotationX)
->Event("GetRotationY", &AZ::TransformBus::Events::GetRotationY)
->Event("GetRotationZ", &AZ::TransformBus::Events::GetRotationZ)
->Event("SetScale", &AZ::TransformBus::Events::SetScale)
->Event("SetScaleX", &AZ::TransformBus::Events::SetScaleX)
->Event("SetScaleY", &AZ::TransformBus::Events::SetScaleY)
->Event("SetScaleZ", &AZ::TransformBus::Events::SetScaleZ)
->Event("GetScale", &AZ::TransformBus::Events::GetScale)
->Attribute("Scale", AZ::Edit::Attributes::PropertyScale)
->VirtualProperty("Scale", "GetScale", "SetScale")
->Event("GetScaleX", &AZ::TransformBus::Events::GetScaleX)
->Event("GetScaleY", &AZ::TransformBus::Events::GetScaleY)
->Event("GetScaleZ", &AZ::TransformBus::Events::GetScaleZ)
->Event("GetChildren", &AZ::TransformBus::Events::GetChildren)
->Event("GetAllDescendants", &AZ::TransformBus::Events::GetAllDescendants)
->Event("GetEntityAndAllDescendants", &AZ::TransformBus::Events::GetEntityAndAllDescendants)
;
}
NetworkContext* netContext = azrtti_cast<NetworkContext*>(reflection);
if (netContext)
{
netContext->Class<TransformComponent>()
->Chunk<TransformReplicaChunk, TransformReplicaChunk::Descriptor>()
->Field("ParentId", &TransformReplicaChunk::m_parentId)
->Field("LocalTransformData", &TransformReplicaChunk::m_localTransform);
}
}

Here, it's mostly with EBuses but to expose a function you use the Method function in the behavior context:

behaviorContext->Class<ComponentClass>("ComponentLuaName")
->Method("MethodName", Function f, BehaviorValues* defaultValues, "dbgDesc");

This can be within a "Class" or global by taking out the "Class" section. The only cadavit, i believe, havent tested it yet, is that you have to assign an entity with the component in order to use the method unless it's a global method.

So for the example above would be:

ComponentLuaName.MethodName();

Personally still trying to figure out this myself but i was able to get ebuses working fine. More "info" about behavior contextes here but it's a bit lack luster... https://docs.aws.amazon.com/lumberyard/latest/developerguide/component-entity-system-behavior-context.html

answered 7 years ago
0

In the Reflect function of the component there's a parameter of "AZ::ReflectContext*". You convert that to a "AZ::BehaviorContext*" using "AZ::BehaviorContext* behaviorContext = azrtti_castAZ::BehaviorContext*(reflection);" From there you have access to the component's behavior context to implement lua (and/or lumberyard's new visual scripting system).

This should be bare basics of what is needed:

	//------------------------------------
//--HEADER
//------------------------------------
class ExampleComponent : public AZ::Component {
public:
~ExampleComponent() override = default;
AZ_COMPONENT(ExampleComponent, "{271C6E71-8352-49F1-A495-A96AB2825335}");
static void Reflect(AZ::ReflectContext* reflection);
void Call(int in){ CryLog("Call Received: %i", in); }
public:
protected:
// AZComponent
void Init() override {};
void Activate() override {};
void Deactivate() override {};
};
class ExampleBusGroup : public AZ::EBusTraits {
public:
virtual float Example(int in) = 0;
virtual void BusCall(int in) = 0;
};
using ExampleBus = AZ::EBus<ExampleBusGroup>;
class ExampleBusHandler : public ExampleBus::Handler, public AZ::BehaviorEBusHandler {
public:
AZ_EBUS_BEHAVIOR_BINDER(
ExampleBusHandler, //Name
"{616AFC57-8CF2-423F-BE4F-BAB3C26AEC8C}", //TypeId
AZ::SystemAllocator, //default allocator.
// List of event names to handle and support for BehaviorContext.
Example
);
float Example(int in) { return Call(FN_Example, in); }
void BusCall(int in) {}
};
//------------------------------------
//--CODE
//------------------------------------
void ExampleComponent::Reflect(AZ::ReflectContext* context) {
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext) {
behaviorContext->Class<ExampleComponent>("ExampleComponent")
->Method("Call",&ExampleComponent::Call, nullptr, "");
behaviorContext->EBus<ExampleBus>("ExampleBus")
->Handler<ExampleBusHandler>()
->Event("BusCall",&ExampleBus::Events::BusCall);
}
}<br>
answered 7 years ago
0

Here's a rough example on how to deal with ebus integration:

	class ExampleEbusGroup : public AZ::EBusTraits {
public:
virtual float Example(int in) = 0;
virtual void Call(float in) = 0;
};
using ExampleBus = AZ::EBus<ExampleEbusGroup>;
class ExampleHandler : public ExampleBus::Handler, public AZ::BehaviorEBusHandler {
public:
AZ_EBUS_BEHAVIOR_BINDER(
ExampleHandler //Name
"{85E7D575-CFE0-45CC-9F8E-EF12D6452070}", //TypeId
AZ::SystemAllocator, //default allocator.
// List of event names to handle and support for BehaviorContext.
Example
);
float Example(int in) { Call(FN_Example,in); }
};
behaviorContext->EBus<ExampleBus>("ExampleBus") //declare the ebus
->Handler<ExampleHandler>() //declare the handler
->Event("Call",&ExampleBus::Events::Call); //declare an event<br>

It's a small example and i dont have any experience in declaring events but handlers are really easy to work with.

How to deal with ebuses in lua is simple:

	local luasystem = {
Properties = {
}<br>}
function luasystem:OnActivate()
if self.exampleBus == nil then
self.exampleBus = ExampleBus.CreateHandler(self, self.entityId)
self.exampleBus.Connect()
end
end
function luasystem::OnDeactivate()
if self.exampleBus ~= nil then
self.exampleBus.Disconnect()
self.exampleBus = nil
end
end
function luasystem:Example(in)
Debug.Log("Data in: " .. in)
return 10.02f
end
return luasystem

First off, on activate you create a handler and connect to the bus. When creating the handler, you can just pass "self" or "self, self.entityId", the second one allows for targeted notification whereas the first one you would have to use broadcasting to notify it.

Now for communicating to the ebuses in C++ is as followed:

-For targeted notification with return:

int out = 10;
float ret;
ExampleBus::EventResult(ret, entity->GetId(), &ExampleEbusGroup::Example, out);<br>

-For broadcast notification without return:

int out = 10;
ExampleBus::Broadcast(entity->GetId(), &ExampleEbusGroup::Example, out);<br>

to recieve a return from a broadcast it would require return aggregation which is detailed in https://docs.aws.amazon.com/lumberyard/latest/developerguide/asset-pipeline-ebus.html under "Return Values from Multiple Handlers".

answered 7 years ago
0

Glad to help. ^.^

answered 7 years ago
0

@REDACTEDUSER

answered 7 years ago
0

@alatnet tks a lot for your answer! I am trying to follow what you have posted but I am struggling with c++... would you mind to send to me the cpp and .h?

I am trying to register the handler inside the cpp of the component/gem. There I can get the behaviorcontext. Am I correct?

thank you very much!

answered 7 years ago

This post is closed: Adding new answers, comments, and votes is disabled.