Jump to content
  • Advertisement
Sign in to follow this  
Bismuth

Creating object instances and initializing instance data

This topic is 2888 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm a bit confused here. Been digging through the manual, but I couldn't find a full document on this topic, only bits and pieces. So I'll just ask here. [sick] What's the proper way to spawn a class instance and initialize its variables to predefined values (using the engine API)? Suppose the Angelscript engine loads a few class declarations. We won't load any more script files from this point on so we can't have the script do the work for us. We'd like to have the engine read some user-specified data and create a number of class instances plus initialize the member variables according to user data (specified values for class member variables may be different for each class instance). This user-specified data is basically a list of objects to be created, and with values to be assigned. I think that asking for a full source code to handle all classes with built-in data parser is a bit over the edge, so I'll just ask for a simple example with a single class. Suppose we loaded a class:
class Bullet {
	int32 x, y, z; // Location
	int32 vx, vy, vz; // Speed
	int32 damage;
}
And our user specified this data (this is just a mockup):
[Bullet(x=133,y=415,z=0,damage=6)]
[Bullet(x=13,y=158,z=20,damage=3)]
[Bullet(x=0,y=0,z=0,vx=33,vy=16,damage=12)]
[Bullet(vx=0,vy=0,vz=100)]
As you can see, not all variables have been specified (thanks to our lazy user who wrote this code). Unspecified vars should probably be set to their defaults. Now we'd like the engine to: A) Create these four instances of the "Bullet" class B) Initialize the class members with the specified values Now my questions are: What's the proper way to do A? What's the proper way to do B? Any hints? Thanks, Bismuth

Share this post


Link to post
Share on other sites
Advertisement
A)

If you only want to create the class instance with the default constructor you can do so by calling asIScriptEngine::CreateScriptObject, and passing the type id obtained with asIScriptModule::GetTypeIdByDecl.

If you want to call a constructor that takes parameters, you'll first need to obtain the function id of the correct factory function. This you'll do by getting the asIObjectType interface, with asIScriptEngine::GetObjectTypeById. Then you can get the proper factory function with asIObjectType::GetFactoryIdByDecl. The factory declaration has the form "typename @typename(parameter list)", where you need to substitute typename for the class name. The factory function is then called like any other global function through the asIScriptContext.

B)

Once you have the object instance, you can access it's members through the asIScriptObject interface. Simply enumerate the properties, and then call asIScriptObject::GetAddressOfProperty to get a pointer to the property so that you can assign the proper value.

Regards,
Andreas

Share this post


Link to post
Share on other sites
So that *was* the correct way of doing it, thanks for clarifying. I have some more questions.

The Angelscript manual states that CreateScriptObject returns a void pointer. This is the pointer to the newely created object, but how does this pointer relate to asIScriptObject? Can I simply static-cast it to asIScriptObject? If yes then why does the CreateScriptObject function not return a asIScriptObject directly?

Speaking about classes, how does one determine whether a property is a primitive, an object or a handle? Does Angelscript have any built-in ways to determine this or is it simply necessary to externally parse the variable definition?

In your to-do list I found this line: "TypeId and asIObjectType should be removed. asITypeInfo should be introduced instead". How is this modification going to affect the type handling (i.e. will all typeID's and asIObjectType's simply be replaced with asITypeInfo)? Will primitive types fit in here as well?

The asIObjectType and asIScriptObject interfaces have a number of functions with a common name (GetPropertyCount, GetPropertyTypeId, GetPropertyName). These are identical, right? And GetAddressOfProperty and GetPropertyOffset are related like: GetAddressOfProperty(...) == CreateScriptObject(...) + GetPropertyOffset(...) yes?

A bit offtopic though. Do you have any plans to add support for CMake for easier project files generation?

Thanks,
Bismuth

Share this post


Link to post
Share on other sites
I'm not sure about the rest, but CreateScriptObject() can be used to create objects other than just AS script objects. For example, if I create a C++ class that I bind to AS as the class "Ref" then I can use GetTypeIdByDecl("Ref") to get the type id of that class and use CreateScriptObject() to create an object of that type.

As for the types of properties, you can look at the type id to determine what kind of property it is. In the angelscript.h header the asETypeIdFlags lists the type ids for the primitives and the flags for various other types. I don't know if this is the right way to do things, but it works well enough in my code, including my AngelScript debugger. However, that project is more than a few revisions behind the current version of AngelScript.

On that note, is there a reason that RegisterObjectType() doesn't return the type id of the newly registered type? Looking at the type id flags, it doesn't look like the type id of a newly registered class should ever be negative.

Share this post


Link to post
Share on other sites
SiCrane is correct. CreateScriptObject can be used to create both script class instances and instances of application registered types, which is why it returns a void pointer.

The asETypeIdFlags can and should be used to get information directly from the type id. All primitive types have fixed type ids, so you can directly compare the type id with the expected type id flag for specific primitives, e.g. asTYPEID_DOUBLE. You can also determine if the type is an application registered object or a script class instance by checking the bits asTYPEID_APPOBJECT and asTYPEID_SCRIPTOBJECT respectively. If the type is a script class instance you can statically cast the pointer to asIScriptClass* or asIScriptClass** depending on the bit asTYPEID_OBJHANDLE. If the type is an application registered object, then you will need to determine which it is, either by comparing the type id with a known type id or by calling GetObjectTypeById and then checking the name of the type.

SiCrane: There is no real reason RegisterObjectType doesn't return the type id upon success. I'll see if I can fit that change into the next version. Thanks for the suggestion.

Quote:

In your to-do list I found this line: "TypeId and asIObjectType should be removed. asITypeInfo should be introduced instead". How is this modification going to affect the type handling (i.e. will all typeID's and asIObjectType's simply be replaced with asITypeInfo)? Will primitive types fit in here as well?


Yes, primitive types will fit in here as well. The idea is to unify the way the types are identified. I'm not sure if I'll actually do this change though, as it may not bring much benefit. I still need to investigate that.

Quote:

The asIObjectType and asIScriptObject interfaces have a number of functions with a common name (GetPropertyCount, GetPropertyTypeId, GetPropertyName). These are identical, right? And GetAddressOfProperty and GetPropertyOffset are related like: GetAddressOfProperty(...) == CreateScriptObject(...) + GetPropertyOffset(...) yes?


Yes, to the first and no to the second. If a class member is of an object, then GetAddressOfProperty will give you the address of the object instance, whereas when using GetPropertyOffset you'll get the location of the pointer to the object instance. For primitive types the relation is exactly as you said.

Quote:

A bit offtopic though. Do you have any plans to add support for CMake for easier project files generation?


I don't use CMake, but if you can send me the necessary files I'll gladly add it to the SDK.

Share this post


Link to post
Share on other sites
Quote:
Original post by WitchLord
SiCrane is correct. CreateScriptObject can be used to create both script class instances and instances of application registered types, which is why it returns a void pointer.

Makes sense. Since I mostly work with script classes, I missed this possibility.

Quote:
Original post by WitchLord
If the type is a script class instance you can statically cast the pointer to asIScriptClass* or asIScriptClass** depending on the bit asTYPEID_OBJHANDLE.

asIScriptClass? I've never heard of that one before. Did you mean asIScriptObject?

Quote:
Original post by WitchLord
Yes, primitive types will fit in here as well. The idea is to unify the way the types are identified. I'm not sure if I'll actually do this change though, as it may not bring much benefit. I still need to investigate that.

By the looks of your ToDo list, I had this sensation that you *wanted* to get rid of both Id's (FuncID and TypeID), having asIScriptFunction in favor of FuncId and asITypeInfo in favor of TypeId + asIObjectType. Although I partially agree that removing the Id's will make the interface a bit cleaner, I'll let you be the judge and decide whether this is necessary. I'll just settle down with the Id's for now.


I don't think Angelscript supports this, but I had this idea about dynamic class loading.
Suppose the engine loads some scripts that define a number of different classes. Next, the module is built and some of these classes are put to use, as the scripting engine creates a few instances of each class (they are Addref()'ed and become persistent). Finally, the script engine notices that one object just called a registered function, which is supposed to spawn a new persistent instance of a particular class on the stage. The problem is that the referenced class definition is missing because it has not been loaded. Is there a way to dynamically load this class into the engine without having to rebuild the whole script module or damage any of the existing class instances?


Thanks,
Bismuth

Share this post


Link to post
Share on other sites
Quote:
Original post by Bismuth
asIScriptClass? I've never heard of that one before. Did you mean asIScriptObject?


Yes, I meant asIScriptObject :)

Quote:
Original post by Bismuth
By the looks of your ToDo list, I had this sensation that you *wanted* to get rid of both Id's (FuncID and TypeID), having asIScriptFunction in favor of FuncId and asITypeInfo in favor of TypeId + asIObjectType. Although I partially agree that removing the Id's will make the interface a bit cleaner, I'll let you be the judge and decide whether this is necessary. I'll just settle down with the Id's for now.


Yes, I want to keep the interface as clean as possible, but I'm not so sure anymore that removing the function and type ids will actually make the interface that much cleaner. Anyway, this won't happen anytime soon, and even when it does I'll make the change as smooth as possible to current users.


Quote:
Original post by Bismuth
I don't think Angelscript supports this, but I had this idea about dynamic class loading.
Suppose the engine loads some scripts that define a number of different classes. Next, the module is built and some of these classes are put to use, as the scripting engine creates a few instances of each class (they are Addref()'ed and become persistent). Finally, the script engine notices that one object just called a registered function, which is supposed to spawn a new persistent instance of a particular class on the stage. The problem is that the referenced class definition is missing because it has not been loaded. Is there a way to dynamically load this class into the engine without having to rebuild the whole script module or damage any of the existing class instances?


It is currently not possible to load a new class into an existing module, but there is nothing stopping you from loading a new module with the new class and have it coexist with the first one.

In my game engine each entity defines a controller type. The controller is a script class that implements the interface IController. Whenever an entity is spawned the engine loads the script for the controller into a module unless it is already loaded, and then creates a new instance of the controller class.

As the different controllers are in different modules, they can't call the methods on each other directly, but they can communicate through messages and through the application registered entity class which holds the controller object.

Share this post


Link to post
Share on other sites
Here's a working CMake script for Angelscript:

cmake_minimum_required(VERSION 2.6)
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE)
cmake_policy(SET CMP0003 NEW)

project(Angelscript)


set(ANGELSCRIPT_SOURCE
source/as_arrayobject.cpp
source/as_atomic.cpp
source/as_builder.cpp
source/as_bytecode.cpp
source/as_callfunc.cpp
source/as_callfunc_mips.cpp
source/as_callfunc_sh4.cpp
source/as_callfunc_x86.cpp
source/as_compiler.cpp
source/as_configgroup.cpp
source/as_context.cpp
source/as_datatype.cpp
source/as_gc.cpp
source/as_generic.cpp
source/as_globalproperty.cpp
source/as_memory.cpp
source/as_module.cpp
source/as_objecttype.cpp
source/as_outputbuffer.cpp
source/as_parser.cpp
source/as_restore.cpp
source/as_scriptcode.cpp
source/as_scriptengine.cpp
source/as_scriptfunction.cpp
source/as_scriptnode.cpp
source/as_scriptobject.cpp
source/as_string.cpp
source/as_string_util.cpp
source/as_thread.cpp
source/as_tokenizer.cpp
source/as_typeinfo.cpp
source/as_variablescope.cpp
)

set(ANGELSCRIPT_HEADERS
include/angelscript.h
source/as_array.h
source/as_arrayobject.h
source/as_builder.h
source/as_bytecode.h
source/as_bytecode.h
source/as_callfunc.h
source/as_compiler.h
source/as_config.h
source/as_configgroup.h
source/as_context.h
source/as_datatype.h
source/as_debug.h
source/as_generic.h
source/as_map.h
source/as_memory.h
source/as_module.h
source/as_objecttype.h
source/as_outputbuffer.h
source/as_parser.h
source/as_property.h
source/as_restore.h
source/as_scriptcode.h
source/as_scriptengine.h
source/as_scriptfunction.h
source/as_scriptnode.h
source/as_scriptobject.h
source/as_string.h
source/as_string_util.h
source/as_texts.h
source/as_thread.h
source/as_tokendef.h
source/as_tokenizer.h
source/as_typeinfo.h
source/as_variablescope.h
)

set(ANGELSCRIPT_ADDON_HEADERS
add_on/scriptany/scriptany.h
add_on/scriptdictionary/scriptdictionary.h
add_on/scriptmath/scriptmath.h
add_on/scriptmath3d/scriptmath3d.h
add_on/scriptstring/scriptstring.h
add_on/scriptstdstring/scriptstdstring.h
add_on/scriptbuilder/scriptbuilder.h
add_on/vector3/as_ScriptOgreVector3.h
add_on/quaternion/as_ScriptOgreQuaternion.h
)

set(ANGELSCRIPT_ADDON_SOURCE
add_on/scriptany/scriptany.cpp
add_on/scriptdictionary/scriptdictionary.cpp
add_on/scriptmath/scriptmath.cpp
add_on/scriptmath3d/scriptmath3d.cpp
add_on/scriptstring/scriptstring.cpp
add_on/scriptstring/scriptstring_utils.cpp
add_on/scriptstdstring/scriptstdstring.cpp
add_on/scriptbuilder/scriptbuilder.cpp
add_on/vector3/as_ScriptOgreVector3.cpp
add_on/quaternion/as_ScriptOgreQuaternion.cpp
)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptdictionary
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptmath
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptmath3d
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptstring
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptstdstring
)

add_definitions("-D_CRT_SECURE_NO_WARNINGS -DANGELSCRIPT_EXPORT -D_LIB")

# Fix x64 issues on Linux
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" AND NOT APPLE)
add_definitions(-fPIC)
endif()

add_library(Angelscript STATIC ${ANGELSCRIPT_ADDON_SOURCE} ${ANGELSCRIPT_ADDON_HEADERS} ${ANGELSCRIPT_SOURCE} ${ANGELSCRIPT_HEADERS})
SET(LIBRARY_OUTPUT_PATH ../lib)

IF(WIN32)
SET_TARGET_PROPERTIES(Angelscript PROPERTIES COMPILE_FLAGS "/MP")
ENDIF(WIN32)

Share this post


Link to post
Share on other sites
This is what we use now:

cmake_minimum_required(VERSION 2.6)
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE)
cmake_policy(SET CMP0003 NEW)

project(Angelscript)

set(ANGELSCRIPT_SOURCE
source/as_arrayobject.cpp
source/as_atomic.cpp
source/as_builder.cpp
source/as_bytecode.cpp
source/as_callfunc.cpp
source/as_compiler.cpp
source/as_configgroup.cpp
source/as_context.cpp
source/as_datatype.cpp
source/as_gc.cpp
source/as_generic.cpp
source/as_globalproperty.cpp
source/as_memory.cpp
source/as_module.cpp
source/as_objecttype.cpp
source/as_outputbuffer.cpp
source/as_parser.cpp
source/as_restore.cpp
source/as_scriptcode.cpp
source/as_scriptengine.cpp
source/as_scriptfunction.cpp
source/as_scriptnode.cpp
source/as_scriptobject.cpp
source/as_string.cpp
source/as_string_util.cpp
source/as_thread.cpp
source/as_tokenizer.cpp
source/as_typeinfo.cpp
source/as_variablescope.cpp
)

include(CheckTypeSize)
CHECK_TYPE_SIZE("void*" ANGELSCRIPT_PTR_SIZE BUILTIN_TYPES_ONLY)
if (ANGELSCRIPT_PTR_SIZE EQUAL 8)
set(ANGELSCRIPT_PLATFORM_X64 TRUE)
else ()
set(ANGELSCRIPT_PLATFORM_X64 FALSE)
endif ()

if(ANGELSCRIPT_PLATFORM_X64)
if (WIN32)
list(APPEND ANGELSCRIPT_SOURCE source/as_callfunc_x64_msvc.cpp)
elseif (UNIX)
list(APPEND ANGELSCRIPT_SOURCE src/as_callfunc_x64_gcc.cpp)
endif ()
else()
list(APPEND ANGELSCRIPT_SOURCE source/as_callfunc_x86.cpp)
endif(ANGELSCRIPT_PLATFORM_X64)

set(ANGELSCRIPT_HEADERS
include/angelscript.h
source/as_array.h
source/as_arrayobject.h
source/as_builder.h
source/as_bytecode.h
source/as_bytecode.h
source/as_callfunc.h
source/as_compiler.h
source/as_config.h
source/as_configgroup.h
source/as_context.h
source/as_datatype.h
source/as_debug.h
source/as_generic.h
source/as_map.h
source/as_memory.h
source/as_module.h
source/as_objecttype.h
source/as_outputbuffer.h
source/as_parser.h
source/as_property.h
source/as_restore.h
source/as_scriptcode.h
source/as_scriptengine.h
source/as_scriptfunction.h
source/as_scriptnode.h
source/as_scriptobject.h
source/as_string.h
source/as_string_util.h
source/as_texts.h
source/as_thread.h
source/as_tokendef.h
source/as_tokenizer.h
source/as_typeinfo.h
source/as_variablescope.h
)

set(ANGELSCRIPT_ADDON_HEADERS
add_on/scriptany/scriptany.h
add_on/scriptdictionary/scriptdictionary.h
add_on/scriptmath/scriptmath.h
add_on/scriptmath3d/scriptmath3d.h
add_on/scriptstring/scriptstring.h
add_on/scriptstdstring/scriptstdstring.h
add_on/scriptbuilder/scriptbuilder.h
add_on/vector3/as_ScriptOgreVector3.h
add_on/quaternion/as_ScriptOgreQuaternion.h
)

set(ANGELSCRIPT_ADDON_SOURCE
add_on/scriptany/scriptany.cpp
add_on/scriptdictionary/scriptdictionary.cpp
add_on/scriptmath/scriptmath.cpp
add_on/scriptmath3d/scriptmath3d.cpp
add_on/scriptstring/scriptstring.cpp
add_on/scriptstring/scriptstring_utils.cpp
add_on/scriptstdstring/scriptstdstring.cpp
add_on/scriptbuilder/scriptbuilder.cpp
)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptdictionary
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptmath
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptmath3d
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptstring
${CMAKE_CURRENT_SOURCE_DIR}/add_on/scriptstdstring
)

add_definitions("-D_CRT_SECURE_NO_WARNINGS -DANGELSCRIPT_EXPORT -D_LIB")

# Fix x64 issues on Linux
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" AND NOT APPLE)
add_definitions(-fPIC)
endif()

add_library(Angelscript STATIC ${ANGELSCRIPT_ADDON_SOURCE} ${ANGELSCRIPT_ADDON_HEADERS} ${ANGELSCRIPT_SOURCE} ${ANGELSCRIPT_HEADERS})
set(LIBRARY_OUTPUT_PATH ../lib)

if(WIN32)
set_target_properties(Angelscript PROPERTIES COMPILE_FLAGS "/MP")
endif(WIN32)



It needs some tweaking, but it should be a good start. ;)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!