Authoring a Model Entity: Difference between revisions
m (Fixed a </pre> tag) |
|||
Line 59: | Line 59: | ||
Much like our logical entity, we must declare the variables used by the entity so that the engine knows their intention. | Much like our logical entity, we must declare the variables used by the entity so that the engine knows their intention. | ||
It’s important to note that the <code>MoveThink()</code> function must be declared as an entity think function in the entity’s data description table using the <code>DEFINE_THINKFUNC</code> macro. See the Data Description Table Document document for more information. | It’s important to note that the <code>MoveThink()</code> function must be declared as an entity think function in the entity’s data description table using the <code>DEFINE_THINKFUNC</code> macro. See the Data Description Table Document [[Data Descriptions |document]] for more information. | ||
==Creating The Precache() Function== | ==Creating The Precache() Function== |
Revision as of 07:09, 2 July 2005
After having created a logical entity in the previous example, we will now create an entity that can move, collide with other objects, and that has a visual component (in this case, a model). In this example we will create an entity that is displayed using a model, and make that entity randomly move around the world.
Create a .CPP file for the new entity
Add the source file to the server.dll project by right-clicking.
- Create a file named
sdk_modelentity.cpp
. The file should go under the dlls folder under your source code folder. For example, if you installed the source code intoC:\MyMod\src
, then you would create a file calledC:\MyMod\src\dlls\sdk_modelentity.cpp</
- Next, copy this code and paste it into this new file.
- Last, add this file to your server.dll project. If you opened the
game_sdk.sln
solution, then you can right-click on the hl project in the Solution Explorer window and choose Add, then Add Existing Item.
Walking Through The code
Creating The Class Definition
class CMyModelEntity : public CBaseAnimating
{
public:
DECLARE_CLASS( CMyModelEntity, CBaseAnimating );
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void MoveThink( void );
// Input function
void InputToggle( inputdata_t &inputData );
private:
bool m_bActive;
float m_flNextChangeTime;
};
We descend our new entity from the CBaseAnimating
class. This allows us to use models and animate. Also new to this entity is the Spawn()
and Precache()
function.
Defining The Data Description
LINK_ENTITY_TO_CLASS( my_model_entity, CMyModelEntity );
// Start of our data description for the class
BEGIN_DATADESC( CMyModelEntity )
// Save/restore our active state
DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flNextChangeTime, FIELD_TIME ),
// Links our input name from Hammer to our input member function
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
// Declare our think function
DEFINE_THINKFUNC( MoveThink ),
END_DATADESC()
Much like our logical entity, we must declare the variables used by the entity so that the engine knows their intention.
It’s important to note that the MoveThink()
function must be declared as an entity think function in the entity’s data description table using the DEFINE_THINKFUNC
macro. See the Data Description Table Document document for more information.
Creating The Precache() Function
#define ENTITY_MODEL "models/gibs/airboat_broken_engine.mdl"
void CMyModelEntity::Precache( void )
{
PrecacheModel( ENTITY_MODEL );
}
The Precache()
function is where all asset precaching must be done. For more information on this subject, see Precaching Assets. Here we also define the model we’ll use to represent our entity in the world.
In this example, we call PrecacheModel()
to precache our model. Without this step the entity’s model would not appear in the world and the engine would complain of a missed precache.
Creating The Spawn() Function
void CMyModelEntity::Spawn( void )
{
Precache();
SetModel( ENTITY_MODEL );
SetSolid( SOLID_BBOX );
UTIL_SetSize( this, -Vector(20,20,20), Vector(20,20,20) );
m_bActive = false;
}
The Spawn()
function is called after the entity is first created. This function can be thought of as the game’s constructor method for the entity. In this function the entity can setup its initial state, including what model to use for itself, its method of movement and its solidity. It’s important to note that the Spawn()
function is called immediately after the allocation of the entity and that if this has occurred at the beginning of a map, there is no guarantee that other entities have been spawned yet. Therefore, any code which requires the entity to search or otherwise link itself to other named entities must do so in the Activate()
function of the entity. Activate()
is called when all entities have been spawned and had their Spawn() function called. Searching for entities before the Activate() function will rely on the spawning order of the entities and is unreliable.
In this example, we first call the Precache()
function to be sure all of our assets are precached properly. After that, we use the SetModel()
function to set our entity’s model to the one we defined previously.
Next, we set the solidity of the entity via the SetSolid()
function. There are multiple possible solid types, defined as:
SOLID_NOT
Not solid.
SOLID_BSP
Uses the BSP tree to determine solidity (used for brush models)
SOLID_BBOX
Uses an axis-aligned bounding box.
SOLID_CUSTOM
Entity defines its own functions for testing collisions.
SOLID_VPHYSICS
Uses the vcollide object for the entity to test collisions.
For this example, we’ll make the entity use a bounding box. The UTIL_SetSize()
function allows us to set the size of that bounding box. Here we set it to a 40x40x40 cube.
Creating The MoveThink() Function
Entities have the ability to update internal state and make decisions via a think function, which will be called at a rate specified by the entity. Here we will create a think function that we will have called up to 20 times a second. We will ll use this think function to randomly update our movement and direction in the world.
void CMyModelEntity::MoveThink( void )
{
// See if we should change direction again
if ( m_flNextChangeTime < gpGlobals->curtime )
{
// Randomly take a new direction and speed
Vector vecNewVelocity = RandomVector( -64.0f, 64.0f );
SetAbsVelocity( vecNewVelocity );
// Randomly change it again within one to three seconds
m_flNextChangeTime = gpGlobals->curtime + random->RandomFloat(1.0f,3.0f);
}
// Snap our facing to where we are heading
Vector velFacing = GetAbsVelocity();
QAngle angFacing;
VectorAngles( velFacing, angFacing );
SetAbsAngles( angFacing );
// Think every 20Hz
SetNextThink( gpGlobals->curtime + 0.05f );
}
While a lot of code is packed into this function, its outcome is fairly simple: once a random time interval has elapsed, the entity will choose a new, random direction and speed to travel at. It will also update its angles to face towards this direction of travel.
The call to SetNextThink()
is important in this function, because it tells the entity when next to think. Here it is set to think again 1/20th of a second in the future. Most entities will only need to think at a rate of 1/10th of a second, depending on their behaviors. It’s important to note that failure to update the next think time of the entity will cause it to stop thinking (which is sometimes desired).
Create the ToggleInput() function
For this entity, we’ll use an input to toggle its movement on and off. To do so, we declare the input function like any other.
void CMyModelEntity::InputToggle( inputdata_t &inputData )
{
// Toggle our active state
if ( !m_bActive )
{
// Start thinking
SetThink( MoveThink );
SetNextThink( gpGlobals->curtime + 0.05f );
// Start flying
SetMoveType( MOVETYPE_FLY );
// Set our next time for changing our speed and direction
m_flNextChangeTime = gpGlobals->curtime;
m_bActive = true;
}
else
{
// Stop thinking
SetThink( NULL );
// Stop moving
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
m_bActive = false;
}
}
To start the entity thinking, we use the SetThink()
function in conjunction with the SetNextThink()
function. This tells the entity to use our MoveThink()
function and to call it 1/20th of a second in the future. It’s important to note that an entity can have any number of think functions and use the SetThink()
function to choose between them. Entities can even have multiple think functions running at the same time using the SetContextThink()
covered in another document.
We also set the entity’s movement type to MOVETYPE_FLY
. This allows the entity to move along a direction without gravity.
In the second portion of this function we stop the entity from moving. The think function is set to NULL
to stop all thinking. Its movement type is also set to MOVETYPE_NONE
to keep it from moving.