User Tools

Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
modding:addingnewdmentities [2012-06-05 00:20]
Stoo [Header file (Speaker.hpp)]
— (current)
Line 1: Line 1:
-====== Adding new Entities to the DeathMatch MOD ====== 
  
-===== Introduction ===== 
- 
-This guide will show you how to create new entities for the DeathMatch MOD. To make this guide more demonstrative every step to create a new entity is illustrated on the example of a speaker entity class. This speaker entity is a non-visible source of a sound effect. The effect is played depending on configuration either as the entity is created or when it is triggered. It is possible to let the entity to play the sound one time or in a loop. 
- 
-Use cases for such an entity would be ambient bird chipper or shocking sound effects that are triggered when the player moves to a specific location. 
- 
-===== What are entities? ===== 
- 
-Generally an entity is an dynamic object in the game world. Dynamic means that the object is interactive and can change in its state or even change other entities in their state. This is opposed to the level geometry which is static and doesn'​t change during game play. Entities may move around a level or change their appearance over time (e.g. a player model that moves around and is animated). But entities don't need to me movable or visible they can also be at a fixed location but interact in some other way with the world. Take for example a trigger entity that reacts on players moving into it by opening a door (that is an entity itself). 
- 
-So basically entities make an otherwise static world interactive and most importantly introduce the "​game"​ aspect into a game. Without entities you would be running trough a static level with no means of interaction. 
- 
-===== Entities in the DeathMatch MOD ===== 
- 
-Entities in the DeathMatch MOD fulfill the above description. Since the DeathMatch MOD is a multi player game entities exist not only in one game instance (as it would be with single player games). Each entity exists on the game server as well as on every client that is participating in the game. 
- 
-The server is responsible for all interaction of the entities and sends the results of this to each client. This is necessary so each client will see the same results (e.g. the position of a player will be calculated on the server side and sent to each client so this player will be visible at the exact same location on all clients). 
- 
-But there are also things that are performed on the client side. These include visual and acoustic events, like the explosion of a grenade together with an explosion sound. There is no need for these things to be calculated on the server and the clients only need to know when and where to show this explosion, but how it is seen or heard is up to the client code. 
- 
-===== Insert new entity class in EntityClassDefs.lua ===== 
- 
-Entities are instances of entity classes. An entity class contains a name and different properties of an entity. Furthermore it contains a reference to the entities source code file (entity functionality is implemented in C++, see next section).\\ 
-So to implement a new entity you first need to declare a new entity class in the entity class definitions script. This lua script is located in the DeathMatch root directory and has the filename EntityClassDefs.lua. 
- 
-This script contains all entity classes usable in the DeathMatch MOD and poses as a good example of what entity class scripts can look like. 
- 
-To define a new entity class for our speaker entity we first need to add the class to the global entity class table: 
-<code lua> 
-EntityClassDefs["​speaker"​]=newEntClassDef(Common,​ 
-</​code>​ 
- 
-This line adds a new entry named "​speaker"​ into the entity class definitions table (too learn more about Lua and tables refer to the Lua documentation on [[http://​www.lua.org]]). The new entry is created using the function newEntClassDef() that can also be found in the script file. 
- 
-The first parameter ''​Common''​ is a table of common entity properties that are used for most entities (it contains for example a name property used by each entity). Followed by this Common table comes the table of our own entity class. Here we define each property ourselves. 
- 
-<code lua> 
-{ 
-    isPoint ​   =true; 
-    CppClass ​  ​="​EntSpeakerT";​ 
-    description="​A simple invisible speaker that can playback a sound shader.";​ 
-</​code>​ 
- 
-The first three properties are default properties that are part of each entity. Defining them give the engine the ability to use properties of this class. 
- 
-''​isPoint''​ Determines if the entity is a point entity. Point entities aren't related to any map brush and therefore invisible to the player. If an entity should be part of a brush and therefore have a volume (e.g. trigger entities) you would have to set this property to ''​false''​ and the ''​isSolid''​ property to ''​true''​. Both properties are ''​false''​ per default so we don't net to explicitly set ''​isSolid''​ to false. 
- 
-:!: You always have to define either ''​isPoint''​ or ''​isSolid''​ as ''​true''​ otherwise an entity of this class cannot be created. :!: 
- 
-The next property ''​CppClass''​ points to cpp class that implements the functionality of this entity. In the next section we will create this class, for now we just declare the classname ''​EntSpeakerT''​ here. 
- 
-''​description''​ is a short description of this entity class used by CaWE. 
- 
-<code lua> 
-    soundshader= 
-    { 
-        type       ​="​string";​ 
-        description="​Name of the sound shader used by this speaker for playback.";​ 
-        value      ="";​ 
-    }; 
-</​code>​ 
- 
-Now comes the first custom property. Properties are defined by a name following ''​type'',​ ''​description''​ and default ''​value''​ of this property. In this case each speaker entity needs a sound shader name to correspond to. This sound shader will be played when playback is triggered by the entity. Since the name of a sound shader is a character string its type is defined as ''"​string"''​. 
- 
-<code lua> 
-    autoplay= 
-    { 
-        type       ​="​flags";​ 
-        description="​Whether the sound plays automatically once the entity is created and doesn'​t need to be triggered first.";​ 
-        flags      ={ "​autoplay",​ true }; 
-    }; 
-</​code>​ 
- 
-The next property is a boolean property. ''​autoplay''​ determines if the sound starts playing as soon as an instance of this entity class is created or if playback has to be triggered first. 
- 
-The property type is ''​flags''​. Flags can contain one or more flags that can be set/unset. Each flag is set in the ''​flags''​ table at the bottom of each property along with a default value. 
- 
-<code lua> 
-    interval= 
-    { 
-        type       ​="​float";​ 
-        description="​Time in seconds between two sound playbacks. [...]";​ 
-        value      =0; 
-    }; 
-</​code>​ 
- 
-The last property is an ''​float''​ value that defines the time interval between two sound playbacks. A small value means that the sound is played more often whereas a big value means that there are longer pauses between two playbacks. The default value of zero means that the sound is only played one time on each trigger. 
- 
-<code lua> 
-    innerCone= 
-    { 
-        type       ​="​integer";​ 
-        description="​Inner sound cone of the speaker (where the sound is at it's inner volume)";​ 
-        value      =360; 
-    }; 
- 
-    innerVolume= 
-    { 
-        type       ​="​float";​ 
-        description="​Sound volume inside the inner cone. Values between 0.0 and 1.0 are valid.";​ 
-        value      =1; 
-    }; 
- 
-    outerCone= 
-    { 
-        type       ​="​integer";​ 
-        description="​Outer sound cone of the speaker (where the sound is at it's outer volume)";​ 
-        value      =360; 
-    }; 
- 
-    outerVolume= 
-    { 
-        type       ​="​float";​ 
-        description="​Sound volume inside the outer cone. Values between 0.0 and 1.0 are valid.";​ 
-        value      =0; 
-    }; 
-}) 
-</​code>​ 
- 
-The above properties are used to define a inner and outer cone for the speaker entity. The sound played by the entity is has ''​innerVolume''​ in the ''​innerCone''​ and ''​outerVolume''​ in the ''​outerCone''​. 
- 
-This concludes the class definition of the new speaker entity class. The defined properties can be set to custom values inside CaWE where will will create instances of this entity and define for example a specific shader name that is used with this entity instance. 
- 
-===== Implementing the C++ class of the entity ===== 
- 
-To give the entity life you need to implement its functionality in a C++ class. To do this you have to create a source and header file inside the ''​DeathMatch/​Code''​ directory. For the speaker entity these files are ''​Speaker.hpp''​ and ''​Speaker.cpp''​. 
- 
-==== Header file (Speaker.hpp) ==== 
- 
-<code cpp> 
-#ifndef _SPEAKER_HPP_ 
-#define _SPEAKER_HPP_ 
- 
-#include "​../​../​BaseEntity.hpp"​ 
- 
- 
-class SoundI; 
-struct luaL_Reg; 
- 
- 
-class EntSpeakerT : public BaseEntityT 
-{ 
-</​code>​ 
- 
-The class name needs to be exactly the same name as declared in the entity class definition: ''​EntSpeakerT''​. This class must be derived from ''​BaseEntityT''​ the basic class that is used for all entities.\\ 
- 
-<code cpp> 
-    public: 
- 
-    // Constructor 
-    EntSpeakerT(const EntityCreateParamsT&​ Params); 
- 
-    // Destructor 
-    ~EntSpeakerT();​ 
-</​code>​ 
- 
-You need to implement a constructor that accepts ''​const EntityCreateParamsT&''​ as parameter since these parameters are always passed to a newly created entity. The destructor is optional and needed here for release of the sound object used by the speaker entity. 
- 
-<code cpp> 
-    // TypeInfo stuff. 
-    const cf::​TypeSys::​TypeInfoT* GetType() const; 
-    static void* CreateInstance(const cf::​TypeSys::​CreateParamsT&​ Params); 
-    static const cf::​TypeSys::​TypeInfoT TypeInfo; ​   ​ 
-</​code>​ 
- 
-These lines are necessary to couple the class with the type system. More information about their implementation follow in the .cpp file of this entity. If you want to learn about the type system, see the TypeSys.hpp file inside the Libs/ directory. 
- 
-<code cpp> 
-    // BaseEntityT implementation. 
-    virtual void PostDraw(float FrameTime, bool FirstPersonView);​ 
-    virtual void ProcessEvent(char EventID) const; 
-</​code>​ 
- 
-To give an entity life we need to overwrite some methods from the BaseEntityT class. In the case of the speaker entity these are ''​PostDraw()''​ and ''​ProcessEvent''​. ''​PostDraw()''​ is called every game frame and used to playback the sound object related to the speaker entity in intervals. 
- 
-The ''​ProcessEvent''​ methods reacts on events that occur on the server. If for example another entity triggers the speaker to play this is handled on the server and an event is sent to all clients. 
- 
-Another interesting method not used with the speaker entity is ''​Think()''​ that is called each game frame on the server side. 
- 
-More information about these methods and other can be found in the BaseEntity.hpp file in the Games/ directory. 
- 
-<code cpp> 
-    // Scripting methods. 
-    static int Play(lua_State* LuaState); 
-    static int Stop(lua_State* LuaState); 
-</​code>​ 
- 
-These are methods that can be called from Lua scripts to control speaker entity behavior. 
- 
-<code cpp> 
-    private: 
- 
-    enum EventIDs { EventID_Play,​ EventID_Stop }; 
- 
-    float   ​m_Interval; ​                 // Interval between two sound playbacks. 0 means the sound is only played one time if triggered. 
-    float   ​m_TimeUntilNextSound; ​       // Time left until the sound is played another time. 
-    SoundI* m_Sound; ​                    // The sound object to play. 
- 
-    static const luaL_Reg MethodsList[];​ // List of methods that can be called from Lua scripts. 
-}; 
- 
-#endif 
- 
-</​code>​ 
- 
-This is mostly speaker entity related stuff (see code commentary). 
- 
-Important are the EventIDs that are used to identify events sent from the server to all clients and the ''​MethodsList[]''​ that is used to inform Lua of all methods that can be called from Lua scripts and has to be used by all entities that want to provide methods to Lua. 
- 
-==== Source file (Speaker.cpp) ==== 
- 
-<code cpp> 
- 
-#include "​Speaker.hpp"​ 
- 
-#include "​EntityCreateParams.hpp"​ 
-#include "​TypeSys.hpp"​ 
-#include "​ScriptState.hpp"​ 
- 
-extern "​C"​ 
-{ 
-    #include <​lua.h>​ 
-    #include <​lualib.h>​ 
-    #include <​lauxlib.h>​ 
-} 
- 
-#include "​SoundSystem/​SoundSys.hpp"​ 
-#include "​SoundSystem/​Sound.hpp"​ 
-#include "​SoundSystem/​SoundShaderManager.hpp"​ 
-</​code>​ 
- 
-The includes above are necessary to make the new entity work. ''​SoundSystem''​ related includes are speaker entity specific and don't needed for each entity. 
- 
-<code cpp> 
-// Implement the type info related code. 
-const cf::​TypeSys::​TypeInfoT* EntSpeakerT::​GetType() const 
-{ 
-    return &​TypeInfo;​ 
-} 
- 
-void* EntSpeakerT::​CreateInstance(const cf::​TypeSys::​CreateParamsT&​ Params) 
-{ 
-    return new EntSpeakerT(*static_cast<​const EntityCreateParamsT*>​(&​Params));​ 
-} 
- 
-// Create method list for scripting. 
-const luaL_Reg EntSpeakerT::​MethodsList[]= 
-{ 
-    { "​Play", ​ EntSpeakerT::​Play }, 
-    { "​Stop", ​ EntSpeakerT::​Stop }, 
-    { NULL, NULL } 
-}; 
- 
-const cf::​TypeSys::​TypeInfoT EntSpeakerT::​TypeInfo(GetBaseEntTIM(),​ "​EntSpeakerT",​ "​BaseEntityT",​ EntSpeakerT::​CreateInstance,​ MethodsList);​ 
-</​code>​ 
- 
-This is the type system related stuff. You need to provide type information trough the ''​GetType''​ method. Further the ''​CreateInstance''​ method is needed using CreateParamsT as parameter so new entities can be created by code or from Lua scripts. 
- 
-Next we fill out the Lua methods list declared in the header file with the two methods that allow control of speaker playback. Last we need to create our own type info and register it with the type info manager. For more details about the params passed to this method and general type info stuff see the TypeSys.hpp in the Libs/ directory. 
- 
-<code cpp> 
-EntSpeakerT::​EntSpeakerT(const EntityCreateParamsT&​ Params) 
-    : BaseEntityT(Params,​ 
-                  EntityStateT(Params.Origin,​ 
-                               ​VectorT(),​ 
-                               ​BoundingBox3T<​double>​(VectorT(0.0,​ 0.0, 0.0), 
-                                                     ​VectorT(0.0,​ 0.0, 0.0)), 
-                               ​0, ​                          // Heading 
-                               ​0, ​                          // Pitch 
-                               ​0, ​                          // Bank 
-                               ​0, ​                          // StateOfExistance 
-                               ​0, ​                          // Flags 
-                               ​0, ​                          // ModelIndex 
-                               ​0, ​                          // ModelSequNr 
-                               ​0.0, ​                        // ModelFrameNr 
-                               ​0, ​                          // Health 
-                               ​0, ​                          // Armor 
-                               ​0, ​                          // HaveItems 
-                               ​0, ​                          // HaveWeapons 
-                               ​0, ​                          // ActiveWeaponSlot 
-                               ​0, ​                          // ActiveWeaponSequNr 
-                               ​0.0)), ​                      // ActiveWeaponFrameNr) 
-      m_Interval(GetProp("​interval",​ 0.0f)), 
-      m_TimeUntilNextSound(m_Interval),​ 
-      m_Sound(SoundSystem->​CreateSound3D(SoundShaderManager->​GetSoundShader(GetProp("​soundshader",​ ""​)))) 
-{ 
-    State.Flags=GetProp("​autoplay",​ 0); // Set initial playback state. 
- 
-    m_Sound->​SetInnerVolume ​  ​(GetProp("​innerVolume", ​  ​0.5f));​ 
-    m_Sound->​SetOuterVolume ​  ​(GetProp("​outerVolume", ​  ​0.0f));​ 
-    m_Sound->​SetInnerConeAngle(GetProp("​innerCone", ​  ​360.0f));​ 
-    m_Sound->​SetOuterConeAngle(GetProp("​outerCone", ​  ​360.0f));​ 
-} 
- 
- 
-EntSpeakerT::​~EntSpeakerT() 
-{ 
-    SoundSystem->​DeleteSound(m_Sound);​ 
-} 
-</​code>​ 
- 
-The constructor of the speaker entity creates its parent base entity class using the passed entity creation parameters and an initial entity state. After setting entity specific members, the entities state flags are set depending on the ''​autoplay''​ property and the sound attributes are set according to entity properties. 
- 
-The destructor is just used to delete the sound object for this speaker entity. 
- 
-<code cpp> 
-void EntSpeakerT::​PostDraw(float FrameTime, bool FirstPersonView) 
-{ 
-    m_Sound->​SetPosition(State.Origin);​ 
- 
-    const float ViewDirZ=-LookupTables::​Angle16ToSin[State.Pitch];​ 
-    const float ViewDirY= LookupTables::​Angle16ToCos[State.Pitch];​ 
- 
-    const Vector3dT Direction(ViewDirY*LookupTables::​Angle16ToSin[State.Heading],​ ViewDirY*LookupTables::​Angle16ToCos[State.Heading],​ ViewDirZ); 
- 
-    m_Sound->​SetDirection(Direction);​ 
- 
-    if (!State.Flags || m_Interval==0.0f) 
-    { 
-        m_TimeUntilNextSound=m_Interval;​ 
-        return; 
-    } 
- 
-    m_TimeUntilNextSound-=FrameTime;​ 
-    if (m_TimeUntilNextSound<​0.0f) 
-    { 
-        m_TimeUntilNextSound=m_Interval;​ 
-        if (m_Interval<​=0.0f) State.Flags=0;​ // Only play this sound once per trigger if interval is 0.0f. 
-        m_Sound->​Play();​ 
-    } 
-} 
-</​code>​ 
- 
-The implementation of the PostDraw() method sets the entities position and heading since it may have changed since the last PostDraw() call. Further more it checks if the entities state flags are set to some value (which means for the speaker entity that the sound playback has been triggered). If this is the case and enough time has passed since the last sound playback another playback is started using ''​m_Sound->​Play()''​. 
- 
-<code cpp> 
-void EntSpeakerT::​ProcessEvent(char EventID) const 
-{ 
-    if (EventID==EventID_Play) 
-        m_Sound->​Play();​ 
- 
-    if (EventID==EventID_Stop) 
-        m_Sound->​Stop();​ 
-} 
-</​code>​ 
- 
-The ProcessEvent() method reacts on events sent from the server and starts/​stops the sound playback according to the event. 
- 
-<code cpp> 
-int EntSpeakerT::​Play(lua_State* LuaState) 
-{ 
-    EntSpeakerT* Ent=(EntSpeakerT*)cf::​GameSys::​ScriptStateT::​GetCheckedObjectParam(LuaState,​ 1, TypeInfo); 
- 
-    Ent->​State.Flags=1;​ 
-    Ent->​State.Events^=(1 << EventID_Play);​ 
- 
-    return 0; 
-} 
- 
- 
-int EntSpeakerT::​Stop(lua_State* LuaState) 
-{ 
-    EntSpeakerT* Ent=(EntSpeakerT*)cf::​GameSys::​ScriptStateT::​GetCheckedObjectParam(LuaState,​ 1, TypeInfo); 
- 
-    Ent->​State.Flags=0;​ 
-    Ent->​State.Events^=(1 << EventID_Stop);​ 
- 
-    return 0; 
-} 
-</​code>​ 
- 
-Last you see the speaker entity control methods that can be called from Lua scripts to start speaker playback or stop it. Since these methods are static the important part here is that you need to get the object for which this call was made using ''​cf::​GameSys::​ScriptStateT::​GetCheckedObjectParam(LuaState,​ 1, TypeInfo)''​ and cast it to the class of your entity (in this case ''​EntSpeakerT''​). This enables you to make Lua code driven changes to the entity. 
-Note that these methods are called on the server and events are set, so all clients will react to the state change. 
- 
-==== Compile and Link ==== 
- 
-The build scripts automatically add the new ''​cpp''​ file to the set of files that are compiled and linked for the DeathMatch DLL. 
-Just restart the compilation as described at [[cppdev:​gettingstarted]]. If everything completes without errors, the new entity is now ready for use. 
- 
-===== Final steps ===== 
- 
-Now you have to insert your new entity into a map using CaWE and set its properties. Additional steps could be the use of a trigger volume to activate the entity. To configure trigger behavior you would have to add a script to the maps world script (each world script is found in the Games/​DeathMatch/​Worlds/​ directory and has the same name as the .cw file of the world but a .lua extension (see available files for examples). 
- 
-===== Conclusion ===== 
- 
-This concludes the creation of a new entity. For more information it is always helpful to read the source code commentary of the various header files found in the Libs/ directory or the Games/ and DeathMatch/​Code directory. 
- 
-Further more the existing entities serve as an example of how to create entities and how to use them in scripts. 
modding/addingnewdmentities.1338848405.txt.gz ยท Last modified: 2013-01-07 12:07 (external edit)