Blender Development: creating an actuator part 2

I’m back with part 2 of my creating an actuator series. Part 1 dealt with creating the actuator and getting it to appear in the UI so you could attach it to a game object. In this instalment we’re going cover creating the code to get the actuator doing something.

I lied somewhat in part 1: the new actuator still wont be doing anything particularly useful, however, we will go through the game object and the sort of things you can access and modify. From there you can begin to create a useful actuator. In next part is going to focus on adding properties that can be modified in the UI, for now everything will be hard coded in C++. I think this part is going to be a bit more straightforward than the first one (personally, I prefer working with C++ over C) and there’s going to be a bit less jumping around the source code. Like the first part, I don’t know everything and I’m still learning. So please help fill in the gaps in the explanations and correct any errors.

Setup
First of all, make sure you’ve read part 1 and got your new test actuator setup as explained. I’m still using Qt Creator, CMake and MinGW. We’re going to be editing the build setup today, so I can only write about how to do it for CMake as I don’t use Scons. If someone wants to let me know the details for adding files to a Scons build I’ll include them. Following on from part 1, the actuator we’re adding is still called ‘Test’. Right, let’s get stuck in.

SCA_IActuator.h
We start by updating the SCA_IActuator class to include a new type for our new actuator:

...
		KX_ACT_SHAPEACTION,
		KX_ACT_STATE,
		KX_ACT_ARMATURE,
		KX_ACT_STEERING,
		KX_ACT_TEST,
	};
...

This will get passed to SCA_IActuator along with KX_GameObject when we initalise the new actuator class.

KX_TestActuator.h
Actuators live in two places in the Blender source code: ../blender/source/gameengine/GameLogic/ and ../blender/source/gameengine/Ketji/. The directory /GameLogic/ contains what comments in the Blender source describe as native logic bricks while /Ketji/ holds the game engine specific logic bricks. Through getting things wrong I learnt that in order to access the KX_GameObject class methods your actuators source files need to live in /Ketji/, otherwise they can only access the methods associated with the CValue class. There are probably other differences and reasons for the separation, if anyone knows, speak up and I’ll expand this section.

So we start by adding a new header file to the project in ../blender/source/gameengine/Ketji/ called SCA_TestActuator.h. In that file we’re going to declare a class called KX_TestActuator and add a few function declarations. So add the following:

/* GPL license block and what not - copy it in from another source file*/

#ifndef KX_TESTACTUATOR_H
#define KX_TESTACTUATOR_H

#include "SCA_IActuator.h"

class KX_TestActuator : public SCA_IActuator
{
public:
KX_TestActuator(
SCA_IObject* gameobj);

~KX_TestActuator();

CValue* GetReplica();
virtual bool Update();
};

#endif // KX_TESTACTUATOR_H

There’s not a huge amount going on here. The only thing the actuator needs to take is a pointer to an SCA_Object as it is required by the SCA_IActuator parent class. I don’t really know what the GetReplica() function does. My guess it’s something to do with copying data when creating instances, might also do some reference counting(?). If anyone knows, please let me know! Anyway, Blender wont compile without it. The Update() function defines what it needs to do on each logic tic.

KX_TestActuator.cpp
Now we need to add implementations for the functions we declared. So create the file KX_TestActuator.cpp in ../blender/source/gameengine/Ketji/ and enter:

/* GPL license block and what not - copy it in from another source file*/

#include "KX_TestActuator.h"
#include "KX_GameObject.h"

KX_TestActuator::KX_TestActuator(SCA_IObject *gameobj)
: SCA_IActuator(gameobj, KX_ACT_TEST)
{
;
}

KX_TestActuator::~KX_TestActuator()
{
;
}

bool KX_TestActuator::Update()
{
bool bNegativeEvent = IsNegativeEvent();

RemoveAllEvents();

if (bNegativeEvent)
return false;

cout << "Hello World!" << endl;

return false;
}

CValue* KX_TestActuator::GetReplica() {
KX_TestActuator* replica = new KX_TestActuator(*this);

replica->ProcessReplica();
return replica;
}

The GetReplica() functions do more or less what it does in other actuators. The main area we’re interested in is the Update() function. This can return true or false, which will affect the actuators behaviour. The logic manager builds a list of triggered sensors and from that it creates a list of controllers and actuators attached to that sensor. When a actuators Update() function is called by the manager if it returns false it is removed from the active actuator list, if it returns true it remains in this list. This means that actuators that returns true will continue to run until the actuator receives a negative pulse whereas those that return false then it only trigger on each positive pulse. Experiment with returning true and false to see the effects.

The SCA_IActuator class, which our actuator inherits from, has 2 bool member variables for storing received events: m_posevent and m_negevent. These both start out as false and receipt of an event are respectively set to true. The first thing Update() does is fetch the value of the negative event variable and store it. It then resets these values by calling RemoveAllEvents() ready for the next frame. Both these functions come from SCA_IActuator.

If the event received is negative (ie, bNegativeEvent == true) then there’s nothing to do and we can return from the function – returning false to ensure that the actuator is removed from the logic managers active list. If the event is not negative then we can continue on with the rest of the function. In this case we print ‘Hello World!’ to the console.

SCA_ConvertActuators.cpp
Now we need to get our new test actuator code running in the game engine. Open up ConvertActuators.cpp and include the test actuator header:

...
#include "KX_ParentActuator.h"
#include "KX_SCA_DynamicActuator.h"
#include "KX_SteeringActuator.h"
#include "KX_TestActuator.h"
...

The next step is to add a new case to the switch statement in the BL_ConvertActuators() function. Not too sure on the purpose of this function, my guess is that it passes the necessary data to the actuator code and links it into the game engine.

...
case ACT_TEST:
{
KX_TestActuator *tmptest = new KX_TestActuator(gameobj);
baseact = tmptest;
break;
}
default:
; /* generate some error */
}
...

Remeber we defined ACT_TEST in DNA_actuator_types.h in part 1.

CMakeList.txt
If you compile now you’ll get an error complaining about an undefined reference to KX_TestActuator in ConvertActuators.cpp, that’s because we need to include the class source files has part of the build. Open up ../blender/source/gameengine/Ketji/CMakeList.txt in any text editor and add:

set(SRC
BL_Action.cpp
BL_ActionManager.cpp
BL_BlenderShader.cpp
BL_Material.cpp

...

KX_VisibilityActuator.cpp
KX_WorldInfo.cpp
KX_WorldIpoController.cpp
KX_TestActuator.cpp

...

KX_WorldInfo.h
KX_WorldIpoController.h
KX_TestActuator.h
)

As mentioned previously, this step applies only to using CMake as the build system. I don’t know what the equivalent is for Scons is. Now Blender should compile. If you open up Blender and attach the test actuator to an object you should now see ‘Hello World!’ being printed in the console. Success!

KX_TestActuator.cpp
Ok, back to the test actuators source to explore the KX_GameObject a little. Lets modify the Update() function to do a little more:

bool KX_TestActuator::Update()
{
bool bNegativeEvent = IsNegativeEvent();

RemoveAllEvents();

if (bNegativeEvent)
return false;

KX_GameObject *ob = (KX_GameObject*) GetParent();
cout << "Object: " << ob->GetName() << " on layer: " << ob->GetLayer() << endl;
cout << "Visible: " << ob->GetVisible() << endl;
cout << "State: " << ob->GetState() << endl;
cout << "Linear Velocity: " << ob->GetLinearVelocity(false) << endl;
cout << "Orientation: " << ob->NodeGetWorldOrientation() << endl;
cout << "Position: " << ob->NodeGetWorldPosition() << endl;
cout << "#### Object properties ####" << endl;
int propCount = ob->GetPropertyCount();
vector<str_string> propNames = ob->GetPropertyNames();
for(int i = 0; i < propCount; i++)
{
CValue* prop = ob->GetProperty(i);
cout << propNames[i] << ": " << prop->GetText() << endl;
}

return false;
}

The actuator still isn’t doing a lot, but now we’re printing out to the console various details about the object the actuator is attached to – you could call it a debug brick if you like. We get the KX_GameObject by calling GetParent() – a method of SCA_ILogicBrick, inherited through SCA_IActuator, that returns a pointer to the logic bricks owner, an SCA_IObject. Here were asking it to return the game object. Through the game object we can access lots of functions to do things or return details – like it’s position, orientation, name, attached properties (stored as a CValue object) and so on.

As a final example, we’ll look at setting some values through the game object:

bool KX_TestActuator::Update()
{
bool bNegativeEvent = IsNegativeEvent();

RemoveAllEvents();

if (bNegativeEvent)
return false;

KX_GameObject *ob = (KX_GameObject*) GetParent();

//Move along the y axis - like simple motion
MT_Vector3 motion(0,1,0);
ob->ApplyMovement(motion, false);

//Rotate on the z axis
MT_Vector3 rotate(0,0,5);
ob->ApplyRotation(rotate, false);

//Add force on the x axis to a dynamic object
MT_Vector3 force(4,0,0);
ob->ApplyForce(force, false);

return true;
}

And that’s it for now. It’s worth looking through KX_GameObject.h to see the functions that you can access and having a play with them. The file is quite well commented and most of the functions and parameters should appear pretty straightforward. At this point the best way to learn is through experimenting and using this as a starting point to exploring the rest of the source code. If you’ve spotted any errors, or know more than I do, let me know. If you have any questions ask them in the comments and I’ll do my best to answer them. In the next installment we’ll looking at adding properties to the actuator in the UI and using them in the update function.

Change Log

07/04/12 – expanded explanation of Update() function.
08/04/12 – missing changes to SCA_IActuator.h

Advertisements

~ by Jay on April 3, 2012.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: