Generalities On Entities: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
 
(22 intermediate revisions by 15 users not shown)
Line 1: Line 1:
{{tutpov}}
{{LanguageBar|Generalities On Entities}}
[[Category:Programming]]
== Introduction ==
 
{{note|This article assumes that the reader has at least basic experience in C++}}
{{note|This article assumes that the reader has at least basic experience in C++}}


== Introduction ==
This article is mainly about entities in the Source engine and will try to explain everything about entities.
This article is mainly about entities in the Source engine and will try to explain everything about entities.


Every object, even the world, is an entity in the Source engine. All entities are derived from CBaseEntity.
Every object, even the world, is an entity in the Source engine. All entities are derived from CBaseEntity.


== Naming Conventions ==
== Naming conventions ==
Before anything else, I must emphasize a point which is naming conventions.
In the '''Source SDK''', all class names begin with a capital <code>C</code>. Furthermore, on the client, the names begin with <code>C_</code> to differentiate them from their server counterparts.


So you would have:
Within the Source SDK, server classes begin with a capital C (<code>C</code>), while client classes begin with a capital C followed by an underscore (<code>C_</code>) - this helps differentiate between the two code bases.


Server: CMyEntity
For example:


Client: C_MyEntity
Server: CMyEntity
Client: C_MyEntity


You should follow that naming convention, and also look into [[Wikipedia:Hungarian Notation|Hungarian Notation]] if you haven't done so already, learn it, and use it, it saves you a LOT of time, because just by looking at the name of a variable, you know its type and whether it belongs to a class or not.
The style of naming convention followed through the SDK code base is known as [[Wikipedia:Hungarian notation|Hungarian notation]]. Beginners are recommended to maintain the same style as used throughout the code base to save confusion.
It saves you the 5-30 seconds that MSVC 2003 takes to find the definition of the variable when you use the right click menu and select "Go to Definition". And believe me, those few seconds are precious time for a programmer.


You should also use tabs. If you are lazy, there is a shortcut in MSVC 2003 to tabulize a certain selection of text. It should be in Edit->Advanced Options->Tabulize or some similar name. Learn the shortcut, and use it often, it makes things a lot easier to read, and also allows you to spot missing brackets, if you have any.
== Base classes ==
Every entity is based on <code>CBaseEntity</code>, however there are many derived classes. The following is a list of the more common derived classes.


== Which base class is right for me? ==
=== [[CBaseAnimating]] ===
Ah, this is a question which I have asked myself many times when creating new entities. This is usually my starting point when trying to make something new: You look around in the different existing entities and try to pick the best one to use as a Base Class. There are many possibilities offered to you, the simplest one being CBaseEntity of course, because every entity is derived from it.
Every entity that has a model uses CBaseAnimating. Classes derived from CBaseAnimating can set a model and animate.
{{note|Entities without models are not networked by default. A workaround is <code>AddEFlags( EFL_FORCE_CHECK_TRANSMIT );</code>}}
{{note|To use animated models be sure to initialize the playback rate using SetPlaybackRate(1.0f); and call StudioFrameAdvance(); in the think function.}}


===CBaseEntity===
=== [[CBaseTrigger]] ===
CBaseEntity, as said above, is the base for every other class out there. I rarely use it as a base though, I have done it sometimes, but usually you want to use something a little more evolved. You should derive from CBaseEntity when you need something really basic, that doesn't use a Model, and that is usually going to be static.  An example for that would be say... A Spawning entity, which you only need to know the position and team number of.
[[trigger|Triggers]] are brush based entities that are generally placed during the map creation process.


===CBaseAnimating===
=== [[CBasePlayer]] ===
Ah, this one already offers many possibilities. First of all, this entity has a model. This might sound a little obvious but not all entities have Models.
This entity is the player itself. Every player-entity in the game is CBasePlayer or is derived from this entity.
I think this is the right time to point out something important for later: '''ENTITIES WITHOUT MODELS DO NOT GET SENT OVER THE NETWORK BY DEFAULT'''. You might even have the corresponding Client entity for it all set up, and have network tables all done, fine and dandy, but it will NOT transmit ANYTHING if it doesn't have a model. The simple workaround is to add the Entity flag EFL_FORCE_CHECK_TRANSMIT using the AddEFlags() function in that entity's Spawn(). That will make sure it does transmit it's information to all clients normally.


CBaseAnimating offers many interesting functions and possibilities.
=== [[CGameRules]] ===
This entity regulates the rules of the current game. It's mainly the gameplay.


====SetModel()====
=== [[CBaseCombatCharacter]] ===
SetModel is simply a function to set a certain entity's model! You need to use the Precache() function to Precache (load into memory) the model beforehand. Precaching is usually done in the Precache() function, which gets called up when the entity is first loaded. You also need to use the PRECACHE_REGISTER macro to tell the engine to Precache your entity (we'll come back to that later).
Every NPC & player are derived from this class.


====SetSequence() and LookupSequence()====
== Think functions ==
At some point I'm sure, you're going to want your model to animate! Well these functions do just that. SetSequence() takes the number of the animation. It starts (I believe) at 0, -1 being the invalid Sequence number, and the order is determined by how you compiled the model. The list in Model Viewer is an easy way to find out the number for the animation sequence you want to use. You can also use LookupSequence() which takes the name of the sequence as a parameter and returns the corresponding number, or -1 if it did not find any animation with the correct name.  
{{main|Think()}}
Think functions are a group of functions comprising the main way to have an entity act without input.  
An entity's Think() is run once after it spawns, and can be told when to call itself again using SetNextThink(). Using SetThink() can change between several Think() functions, and GetLastThink() returns the time of the last think function. See [[Think()]] for detailed description.


Also you can get the sequence number via [[Activity List|activity]] name using SelectWeightedSequence().(e.g. SelectWeightedSequence( ACT_IDLE )) The advantage to using activities is that the modeler can bind multiple sequences to one activity name, thus getting some variation in the animations the model plays without programmer intervention.
== Macros ==
Badly set up macros will give error messages, crash the game and cause erratic behavior. The following is a list of the more common macro definitions and some of the issues that surround them.


===CBaseTrigger etc...===
=== [[LINK_ENTITY_TO_CLASS]]() ===
We now enter the domain of map entities (entities primarily used by mappers.) There's a couple different types of those, but I have not had the chance yet to explore them all. There is one important thing to note about map entities though, and that is the following function:
This is one of the main macros. An entity cannot be created without this macro being used to define the entity's classname (as returned from <code>GetClassname()</code> on the server). However, on the client, an entity will not return its classname unless there is a matching data description (<code>DATADESC</code>).


===Keyvalues===
=== [[PRECACHE_REGISTER]]() ===
When a mapper makes an entity in Hammer, he fills in a certain number of values (name, angles, parent, etc...). Those are called keyvalues, and go in pairs of name/value.  You define what keyvalues an entity has in an [[FGD]] file.  The format is pretty straightforward, looking through one should be enough for you to work out how to write fgd entries for your own entities. To get the value from a keyvalue put into one of your entity’s class’s variables you use a [[Data Descriptions|data description]] and the DEFINE_KEYFIELD macro, [[Authoring a Logical Entity]] has an example of this.  There is also a keyvalue function in the base entity class that you can override.  This function gets passed the various keyvalues from the map file and sets variables appropriately, ordinarily you shouldn’t have to worry about it as using a data description sets all of this up automatically but if you’re doing something different with keyvalues you may want to override it, just remember to call BaseClass::KeyValue at some point in your version otherwise other keyvalues won’t get setup properly.
This is used to tell the engine that it needs to precache the entity when it loads. This is provided with the name of the entity (the same name as passed to [[LINK_ENTITY_TO_CLASS]]() ).


Right. Now that you have the class set up, you might want it to DO something:
=== [[DECLARE_CLASS]]() ===
This should be placed in the public section of an entity's Class definition. This provides access to the BaseClass Macro.


== Think Functions ==
=== DECLARE_[[DATADESC]](), BEGIN_[[DATADESC]](), END_[[DATADESC]](), and DEFINE_XXX() ===
Think functions could be defined as Functions that dictate the behavior of an entity. They quite literally THINK for the function, and make it change states, and so on.
===Server side===
====SetThink() and SetNextThink()====
This assumes you know what a function pointer is. If you don't, go refresh your memory and come back when you have.
In setting up your entity, you will want for it to change, maybe change animations, or do something to players, or other entities... There's a host of things an entity can do, and I leave that up to you.
You can have many think functions, but here's another one of those hard-learned tips: '''THINK FUNCTIONS DO NOT EXIST ON THE CLIENT SIDE'''. At least not in the same manner that they do on the server.
Ok, let's get an example so you'll understand it more clearly:
I want to have an entity that does two things: When there is no player around, it will play it's idle animation, and do nothing more. When a player comes into range of it, it will start to emit puffs of smoke. So I will make two think functions: IdleThink and SmokeThink. These should return void and have no argument list like so:


void IdleThink(void);
The Data Description macros provide for a number of different features;
void SmokeThink(void);
* Specifying Think and Touch functions
* Providing Inputs and Outputs for mappers
* Providing external variable inputs (such as setting a model or the health of an item)


When the entity spawns, I will do this:
Further information is available on the [[Data Descriptions]] page.


SetThink( &CMyEntity::IdleThink );
=== Networking entities ===
SetNextThink( gpGlobals->curtime + 0.5 );


What this does is set this entity to run the IdleThink functions in 0.5 seconds.
See the [[:Category:Programming#Networking|Networking]] section for more information on networking entities, as that is outside the scope of this article.
Now in IdleThink, we will look for a player near us (Use a SphereQuery for example), if we find one, we will do:


SetThink( &CMyEntity::SmokeThink );
[[Category:Programming]]
SetNextThink(gpGlobals->curtime + 0.01);
 
If you tell SetNextThink that the next think time is '''almost''' now (when setting it to the current time, it may not always work) the function will get ran the next server frame. If we do not find a player near us, it is important that we still call SetNextThink:
SetNextThink(gpGlobals->curtime);
 
Otherwise, things will get messed up after that. I am not sure of the exact effect but I do believe that your entity will '''stop thinking''' if you do not set a new NextThink time.
 
Right, this is all server side. Do not forget to use the [[DEFINE_THINKFUNC]] macro for all your think functions (we'll come back to that later as well).
 
===Client Side===
As I said before, the client does not have the same System for entities to think. These Client Thinks are handled in <code>client_thinklist.cpp</code>. You cannot define multiple think functions here, as SetThink will not work properly on this side. Instead, you must use the following:
 
====ClientThink() and SetNextClientThink()====
These are the equivalent of the server system, except you only have one function at your disposal here (which sucks a bit for organization, but oh well). This will get called based on the SetNextClientThink() time you set in Spawn, and in subsequent calls to ClientThink(). You can use two special time settings here: [[CLIENT_THINK_ALWAYS]] and [[CLIENT_THINK_NEVER]].
 
====Simulate()====
Here's the commentary in c_baseentity.cpp: ''"Once-per-frame stuff should go in Simulate()."'' That really sums it up. I personally use Simulate() mainly for Particle effects, or graphical effects of any sorts. Beware of what you run in Simulate(), as said before, this gets run EVERY FRAME, meaning you should avoid creating 50 new particles every Frame ;)
 
That's about it I believe. Think functions are an essential part of any entity and you should learn to use them well.
 
== Macros ==
I want to dedicate an entire chapter to these Macros, because they are what causes problems with entities at least 60% of the time. Badly set up macros will give you errors messages, crash your game, give you erratic behavior, and so forth. I'm going to try to sum up most of them and a few more tips as best as I can:
 
===[[LINK_ENTITY_TO_CLASS]]()===
This is your main macro. You do not have an entity until you call this macro. This macro will NAME your entity for the game. Anytime you call GetClassname() on an entity, this is the name that will be returned on the server side. There is a subtlety however, which is that, on the client side, the entity name will not be the one you give to [[LINK_ENTITY_TO_CLASS]] unless you  make a DATADESC. Instead, it returns (client-side) something like "class C_MyEntity".
 
===[[PRECACHE_REGISTER]]()===
This is used to tell the engine that it needs to precache your entity when it loads. You pass it the name of your entity (the same you passed to [[LINK_ENTITY_TO_CLASS]]() ).
 
===[[DECLARE_CLASS]]()===
You need to put this in your Class definition, in a public section. This gives you access to the BaseClass Macro, which is very handy. I believe it also helps to set up some networking stuff.
 
===DECLARE_[[DATADESC]](), BEGIN_[[DATADESC]](), END_[[DATADESC]](), and DEFINE_XXX()===
This set of macros is used for two main things:
First, you need it if you're going to have think functions.
Second, I believe it takes care of saving any variable that is defined in the set for Single Player Games, and restoring their values when you load a saved game.
I refer you to the Valve tutorial [http://developer.valvesoftware.com/wiki/Data_Descriptions Data Descriptions] for more info.
 
===Networking Entities=== I was going to explain a little bit about networking entities and networked variables and such, but there are already a lot of nice tutorials by Valve that explain it all in detail. See the [[:Category:Programming#Networking|network section]] section in this wiki's [[:Category:Programming|programming category]].
 
== Conclusion ==
I have only brushed the surface here. I have been working with this SDK for over 8 months now, and I learn something new about it every time I open it. It is complex, it is vast, and you will not learn all about it by just reading tutorials. The best way to do it has always been for me to JUST DO IT. You take an idea, something you want to add to your MOD. You look around source with a few keywords to see what's already been done. You search on a few forums and see if it's already been discussed. You look at tutorial sites and see if anything relates to it. Once you have a good idea on how you're gonna do it, well you just dive in the deep end and start coding it!
 
I hope you've learned something from this tutorial. Once again, if I mislead you in anyway, it is not intentional. This is all of what I have learned through months of trial and error and I hope it speeds up your progress in learning more about this SDK.
 
Good Luck.
Imperio59

Latest revision as of 16:01, 18 July 2025

English (en)Русский (ru)Translate (Translate)

Introduction

Note.pngNote:This article assumes that the reader has at least basic experience in C++

This article is mainly about entities in the Source engine and will try to explain everything about entities.

Every object, even the world, is an entity in the Source engine. All entities are derived from CBaseEntity.

Naming conventions

Within the Source SDK, server classes begin with a capital C (C), while client classes begin with a capital C followed by an underscore (C_) - this helps differentiate between the two code bases.

For example:

Server: CMyEntity
Client: C_MyEntity

The style of naming convention followed through the SDK code base is known as Hungarian notation. Beginners are recommended to maintain the same style as used throughout the code base to save confusion.

Base classes

Every entity is based on CBaseEntity, however there are many derived classes. The following is a list of the more common derived classes.

CBaseAnimating

Every entity that has a model uses CBaseAnimating. Classes derived from CBaseAnimating can set a model and animate.

Note.pngNote:Entities without models are not networked by default. A workaround is AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
Note.pngNote:To use animated models be sure to initialize the playback rate using SetPlaybackRate(1.0f); and call StudioFrameAdvance(); in the think function.

CBaseTrigger

Triggers are brush based entities that are generally placed during the map creation process.

CBasePlayer

This entity is the player itself. Every player-entity in the game is CBasePlayer or is derived from this entity.

CGameRules

This entity regulates the rules of the current game. It's mainly the gameplay.

CBaseCombatCharacter

Every NPC & player are derived from this class.

Think functions

Main article:  Think()

Think functions are a group of functions comprising the main way to have an entity act without input. An entity's Think() is run once after it spawns, and can be told when to call itself again using SetNextThink(). Using SetThink() can change between several Think() functions, and GetLastThink() returns the time of the last think function. See Think() for detailed description.

Macros

Badly set up macros will give error messages, crash the game and cause erratic behavior. The following is a list of the more common macro definitions and some of the issues that surround them.

LINK_ENTITY_TO_CLASS()

This is one of the main macros. An entity cannot be created without this macro being used to define the entity's classname (as returned from GetClassname() on the server). However, on the client, an entity will not return its classname unless there is a matching data description (DATADESC).

PRECACHE_REGISTER()

This is used to tell the engine that it needs to precache the entity when it loads. This is provided with the name of the entity (the same name as passed to LINK_ENTITY_TO_CLASS() ).

DECLARE_CLASS()

This should be placed in the public section of an entity's Class definition. This provides access to the BaseClass Macro.

DECLARE_DATADESC(), BEGIN_DATADESC(), END_DATADESC(), and DEFINE_XXX()

The Data Description macros provide for a number of different features;

  • Specifying Think and Touch functions
  • Providing Inputs and Outputs for mappers
  • Providing external variable inputs (such as setting a model or the health of an item)

Further information is available on the Data Descriptions page.

Networking entities

See the Networking section for more information on networking entities, as that is outside the scope of this article.