o3de / o3de

Open 3D Engine (O3DE) is an Apache 2.0-licensed multi-platform 3D engine that enables developers and content creators to build AAA games, cinema-quality 3D worlds, and high-fidelity simulations without any fees or commercial obligations.
https://o3de.org
Other
7.68k stars 2.19k forks source link

Calling `RegisterGenericType` isn't enough to register new types with containers. #11182

Open BytesOfPiDev opened 2 years ago

BytesOfPiDev commented 2 years ago

Describe the bug Registering a generic type does not work unless the type is reflected in the behavior context as a member of at least one reflected type.

Sometimes I want to create types for use in ScriptCanvas and its containers(array, set, map). However, unless I have some type that has the generic type as a reflected member, return value or parameter, then it does not show up for use in the appropriate ScriptCanvas container. This forces me to find some way to reflect the generic type inside of something.

Take a look. Here's a hypothetical type that I exclusively want to use in ScriptCanvas. I want to use it in ScriptCanvas' Array container. First, I define the class.

    class ReflectionTest
    {
    public:
        AZ_CLASS_ALLOCATOR(ReflectionTest, AZ::SystemAllocator, 0);
        AZ_RTTI(ReflectionTest, "{BAE06682-7AEC-4CD1-B2BF-AF92F89A8906}");

        // Workaround
        static AZStd::vector<ReflectionTest> GetEmptyVector()
        {
            return {};
        }

        static void Reflect(AZ::ReflectContext* context)
        {
            if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
            {
                serializeContext->Class<ReflectionTest>()->Field("TheAnswer", &ReflectionTest::m_answerToTheUniverse);
                serializeContext->RegisterGenericType<AZStd::vector<ReflectionTest>>();
            }

            if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
            {
                behaviorContext->Class<ReflectionTest>("ReflectionTest")
                    ->Property("TheAnswer", BehaviorValueProperty(&ReflectionTest::m_answerToTheUniverse));
                // Using the workaround.
                behaviorContext->Method("GetEmptyReflectionTestVector", &ReflectionTest::GetEmptyVector);
            }
        }

    private:
        int m_answerToTheUniverse = 42;
    };

I would expect that I only have to call

ReflectionTest::Reflect(context);

to use my type in ScriptCanvas' Array type. However, doing this does nothing that successfully accomplishes that. My type will not show up for use unless I reflect some type containing the generic type as a member, parameter, or return value. In this example, I created GetEmptyVector() to use as a workaround.

        // Workaround
        static AZStd::vector<ReflectionTest> GetEmptyVector()
        {
            return {};
        }

If I reflect this type, I now have the generic type reflected as a return value.

behaviorContext->Method("GetEmptyReflectionTestVector", &ReflectionTest::GetEmptyVector);

Assets required N/A

Steps to reproduce Steps to reproduce the behavior:

  1. Define a type, e.g. ReflectionTest, and reflect it appropriately for use in a ScriptCanvas container (see the example code above) image

  2. Aside from defining ReflectionTest, don't use it anywhere in code except to call ReflectionTest::Reflect().

  3. In ScriptCanvas, notice you can create variables of the type. image

  4. Try to create an Array of the type; you'll notice it's not in the list. image

  5. Use the type somewhere and use the behavior context to reflect whatever is using it, such as the GetEmptyVector() earlier.

  6. Now the type shows up. image

Expected behavior So long as the types involved are reflected in AZ::BehaviorContext, calling AZ::SerializeContext::RegisterGenericType should be enough to reflect a type for use with ScriptCanvas' containers.

Actual behavior Calling AZ::SerializeContext::RegisterGenericType will not actually register the type for use in a ScriptCanvas container unless the registered type is used as a return, parameter, or class member in AZ::BehaviorContext.

Screenshots/Video N/A

Found in Branch Development downloaded and installed on 8/3/22.

Desktop/Device (please complete the following information):

Additional context This is related to a different issue I opened, but closed to create a more accurate, detailed ticket. o3de#7474

lemonade-dm commented 2 years ago

@lsemp3d @carlitosan @onecent1101 This is related to ScriptCanvas needing both SerializeContext reflection in order to save and load variables of that type to the .scriptcanvas file and BehaviorContext reflection to provide bindings to Scripting Languages.

I do think we can add an equivalent RegisterGenericType function to the BehaviorContext, which just goes through the flow of performing OnDemandReflection, as the SetParametersStripped function does. That can trigger calls to the OnDemandReflection template in AzStdOnDemandReflection.inl which is how template types are reflected BehaviorContext