Generalities On Entities: Difference between revisions
mNo edit summary |
|||
Line 29: | Line 29: | ||
This entity is only used by mappers. Mappers need [[trigger|triggers]] for their maps. | This entity is only used by mappers. Mappers need [[trigger|triggers]] for their maps. | ||
== CBasePlayer == | === CBasePlayer === | ||
This entity is the player itself. Every player-entity in the game is CBasePlayer or is derived from this entity. | This entity is the player itself. Every player-entity in the game is CBasePlayer or is derived from this entity. | ||
== CGameRules == | === CGameRules === | ||
This entity regulates the rules of the current game. It's mainly the gameplay. | This entity regulates the rules of the current game. It's mainly the gameplay. | ||
== CBaseCombatCharacter == | === CBaseCombatCharacter === | ||
Every NPC & player are derived from this class. | Every NPC & player are derived from this class. | ||
Revision as of 11:22, 25 August 2006


Introduction
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
In the Source engine, all class names begin with a capital C
. Furthermore, on the client, the names begin with C_
to differentiate them from their server counterparts.
For example:
Server: CMyEntity Client: C_MyEntity
More information about these naming convention can be found at Hungarian Notation. Following this will save time later because classes and its variables will be easier to read.
Which base class?
Every entity is based on CBaseEntity
, however there are many derived classes. Here's a short list with the most famous ones:
CBaseAnimating
Every model that has a model uses CBaseAnimating. Classes derived from CBaseAnimating can set a model and animate.

AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
CBaseTrigger
This entity is only used by mappers. Mappers need triggers for their maps.
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
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); void SmokeThink(void);
When the entity spawns, I will do this:
SetThink( &CMyEntity::IdleThink ); SetNextThink( gpGlobals->curtime + 0.5 );
What this does is set this entity to run the IdleThink functions in 0.5 seconds. 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 ); 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 client_thinklist.cpp
. 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 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 network section section in this wiki's 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