Anda di halaman 1dari 20

Game Engine Delta3D untuk Simulasi

Oleh

Mohammad Hakim Adiprasetya

DAFTAR ISI
DAFTAR ISI.................................................................................................................................... i DAFTAR GAMBAR ...................................................................................................................... ii 1 PERCOBAAN IMPLEMENTASI........................................................................................... 1 1.1 1.2 1.3 1.4 1.5 Arsitektur Umum Delta3D ........................................................................................... 1 Game Manager ............................................................................................................. 2 Message ........................................................................................................................ 4 Component.................................................................................................................... 9 Actor ........................................................................................................................... 13

DAFTAR GAMBAR
Figure 1 Game manager .................................................................................................................. 2 Figure 2 Game actor proxy ........................................................................................................... 13

ii

BAGIAN I 1 PERCOBAAN IMPLEMENTASI


Dalam usaha membangun arsitektur Virtual Reality yang lebih rhobust, maka arsitektur Virtual Reality Simulator yang dibangun sebisa mungkin menggunakan fasilitas dan framework Delta3D yang sudah ada. Dengan demikian, maka akan diperkenalkan beberapa konsep-konsep arsitektur system didalam Delta3D. 1.1 Arsitektur Umum Delta3D

Seperti arsitektur game pada umumnya, Delta3D membuat dan menggunakan sebuah arsitektur dengan game loop yang dijalankan setiap frame-nya. Semua ini disediakan secara low-level didalam kelas dtCore::System. Namun dengan demikian, dalam menggunakan framework Delta3D yang baik dan benar, sesuai dengan yang disarankan para pembuatnya dalam software design document, game loop tersebut sebaiknya tidak diakses secara langsung namun melalui messaging system yang didefinisikannya. Untuk saat ini, Delta3D memiliki dua cara untuk melakukan pertukaran informasi antar kelaskelas. Cara pertama adalah dengan menggunakan kelas dtCore::Base. Setiap-setiap kelas didalam Delta3D diturunkan dari kelas dtCore::Base tersebut, kelas ini menyediakan memory management melalui reference pointernya sekaligus menyediakan metoda pertukaran informasi melalui messages yang sederhana. Untuk melakukan pertukaran informasi melalui dtCore::Base, sebuah kelas cukup mensubscribe untuk mendapatkan informasi dari kelas lain dengan meregistrasikannya menggunakan metode AddSender(kelas untuk diregistrasi). Dengan cara inilah dtCore::System mengirim sinyal pre-frame, post-frame dll yang ditangkap oleh kelas-kelas lain sebagai sinyal untuk melakukan sesuatu yang harus dilakukan setiap frame-nya. Cara pertukaran informasi yang pertama tersebut seperti yang telah disebutkan sebelumnya, digunakan oleh sebagian besar kelas-kelas Delta3D untuk melakukan pertukaran informasiinformasi internal yang penting sebagai bagian dari frameworknya. Namun untuk menggunakan dan memberdayakan kekuatan dan fitur-fitur canggih Delta3D secara utuh, sebaiknya digunakan metode pertukaran informasi melalui kelas GameManager yang telah disediakan Delta3D. 1

1.2

Game Manager

Fitur paling canggih yang disediakan oleh Delta3D adalah arsitekturnya yang menggunakan sebuah Game Manager. Game Manager tersebut (mulai sekarang disingkat GM) menyediakan metode pertukaran informasi yang lebih high level dibandingkan yang disediakan oleh dtCore::Base. Gambar dibawah ini merupakan gambaran umum GM Delta3D.

Figure 1 Game manager Bayangkan bahwa dalam sebuah game 3d terdapat berbagai pemeran didalam game tersebut. Mungkin ada sebuah Tank, ada sebuah Pesawat terbang, ada berbagai infanteri, ada juga berbagai elemen seperti medan, pencahayaan, serta benda-benda statis seperti rumah. Bagaimanakah sebaiknya hal-hal tersebut didefinisikan dalam sebuah game? Delta3D mendefinisikan hal-hal tersebut sebagai Actor (pemeran). Sebuah actor bisa jadi benda apa saja yang dapat berada didalam sebuah game, kalau game tersebut memerlukan fungsionalitas seperti

input dari pengguna, atau networking antar computer, maka fungsionalitas tersebut dimasukkan kedalam Component yang dikelola oleh game manager. Game Manager mengelola pertukaran informasi antar sesame actor dan component, maupun antar actor dan component. Bisa saja kita mendefinisikan sebuah component yang menerima input dari sebuah joystick, kemudian component tersebut mengirim sebuah message ke actor tank di dalam game untuk bergerak sesuai input yang diterima component tersebut. Berarti sejauh ini bisa disimpulkan bahwa game manager menyimpan dan mengelola berbagai actor dan component, selain itu game manager juga mengetahui berbagai messages yang ada serta mengirimkannya antar actor atau component didalam game tersebut. Pada bagian-bagian selanjutnya dari dokumen ini, akan dijelaskan langkah-demi-langkah dari cara penggunaan framework Delta3D serta bagaiman Virtual Reality Simulator

diimplementasikan agar dapat dengan lebih mudah dikembangkan lebih lanjut oleh pihak-pihak lain dari tim ini. Untuk menjalankan Delta3D, perlu didefinisikan sebuah kelas aplikasi Delta3D yang menjadi kelas dasar untuk menjalankan keseluruhan fungsi framework Delta3D. Dengan menggunakan dtABC::Application, kita dapat membuat sebuah standalone application dalam bentuk file .exe yang menjalankan aplikasi yang telah kita buat. Namun dengan mengikuti perkembangan Delta3D selanjutnya, Virtual Reality Simulator yang dikembangkan menggunakan kelas Game Entry Point yang mendefinisikan sebuah .dll yang akan dipanggil oleh sebuah executable bernama Game Start untuk menjalankan aplikasi yang kita inginkan. Berikut adalah isi header Game Entry Point yang kita buat untuk aplikasi Virtual Reality Simulator:
class CLASS_EXPORT GameEntryPoint: public dtGame::GameEntryPoint { public: static dtGame::GameApplication* gameApplication; static GameEntryPoint* gameEntryPoint; GameEntryPoint() {gameEntryPoint = this; }; virtual ~GameEntryPoint() { }; virtual void OnStartup(dtGame::GameApplication &app); private: dtCore::RefPtr<dtCore::Transformable> mTree; };

Pada kode diatas, metode yang menjadi perhatian utama dalam kelas Game Entry Point adalah metode OnStartup, didalam metode ini kita melakukan semua inisialisasi awal yang diperlukan didalam gam, seperti mendefinisikan semua komponen dan actor yang akan digunakan pada awal game, serta meload berbagai file seperti map. Baris kode dibawah ini memperlihatkan inisialisasi awal game seperti meload map, menentukan komponen apa saja yang akan digunakan, meregistrasi message yang kita definisikan khusus untuk game kita (akan dijelaskan lebih lanjut nanti), serta peletakkan awal camera pada game.
void GameEntryPoint::OnStartup(dtGame::GameApplication &app) { GameEntryPoint::gameEntryPoint->gameApplication = &app; // Load the map we created in STAGE. dtDAL::Project::GetInstance().SetContext("StageProject"); dtGame::GameManager& gameManager = *app.GetGameManager(); gameManager.ChangeMap("HoverMap"); gameManager.GetScene().UseSceneLight(true); // Add Component - DefaultMessageProcessor dtGame::DefaultMessageProcessor *dmp = dtGame::DefaultMessageProcessor("DefaultMessageProcessor"); gameManager.AddComponent(*dmp,dtGame::GameManager::ComponentPriority::HIGHEST); //Add Client Network Component dtNetGM::ClientNetworkComponent *cnc = dtNetGM::ClientNetworkComponent("VRNetwork",1,"VRClientLog.log"); gameManager.AddComponent(*cnc,dtGame::GameManager::ComponentPriority::HIGHER); // Add Component - Input Component dtCore::RefPtr<InputComponent> inputComp = new InputComponent("InputComponent"); gameManager.AddComponent(*inputComp, dtGame::GameManager::ComponentPriority::NORMAL); //Register New Message types VRMessageType::RegisterMessageTypes( gameManager.GetMessageFactory() ); std::vector<const dtDAL::ActorType*> actort; gameManager.GetActorTypes(actort); // Attach our camera to the fly motion model.. dtCore::FlyMotionModel *fmm = new dtCore::FlyMotionModel(app.GetKeyboard(), false); fmm->SetTarget(app.GetCamera()); // Set initial camera position so we can see our tank dtCore::Transform tx(0.0f,-40.0f,15.0f,-50.0f,-15.0f,0.0f); app.GetCamera()->SetTransform(tx); } app.GetMouse(), new

new

1.3

Message

Arsitektur umum Virtual Reality Simulator mengikuti standar yang telah ditetapkan oleh Delta3D. Oleh sebab itu, komunikasi antar kelas-kelas harus dilakukan melalui pengiriman

message dan tidak boleh dengan langsung memanggil metode kelas lain. Seperti layaknya sebuah pesan biasa, dalam pembuatan message dalam Delta3d, kita harus memikirkan: 1. siapa pengirimnya, dan kapan pesan tersebut akan dikirim oleh pengirimnya 2. Apa isi pesan tersebut 3. Siapa saja yang menerima pesan tersebut Sebagai contoh didalam dokumen ini, kita akan membuat sebuah pesan tentang kendali kendaraan sebagai berikut: 1. Pengirimnya adalah komponen input, pesan dikirim setiap kali input berubah 2. Isi pesannya adalah tombol apa saja yang ditekan pengguna beserta entitas kendaraan tujuannya 3. Penerima pesan tersebut adalah semua jenis Actor yang sama pada umumnya, namun hanya digunakan oleh kendaraan dengan entitas kendaraan yang sama. Untuk mendefinisikan sebuah pesan baru, pertama kita harus membuat sebuah kelas khusus yang dapat meregistrasikan pesan-pesan yang dibuat khusus oleh kita sendiri kedalam framework Delta3D yang sudah ada. Untuk itu, didalam file VRMessage.h , didefinisikan kelas VRMessageType yang diturunkan dari kelas dtGame::MessageType. Berikut adalah source code untuk kelas VRMessageType:
class CLASS_EXPORT VRMessageType : public dtGame::MessageType { DECLARE_ENUM(VRMessageType); public: //message types static const VRMessageType STEER_CHANGED; static const VRMessageType CREATE_VRENTITY; static const VRMessageType UPDATE_POS_VRENTITY; //static const VRMessageType MESSAGE_BARU; static void RegisterMessageTypes(dtGame::MessageFactory& factory); protected: VRMessageType( const std::string &name, const std::string &category, const std::string &description, const unsigned short messageId) : dtGame::MessageType(name, category, description, messageId) { AddInstance(this); } virtual ~VRMessageType() { } };

Sesuai contoh yang ingin kita buat, maka kita mendefinisikan STEER_CHANGED sebagai sebuah pesan yang berisi data-data tombol apa saja yang ditekan oleh pengguna untuk menggerakkan sebuah kendaraan didalam game. Hal pertama dalam pembuatan pesan tersebut adalah dengan mendefinisikannya pada bagian public dari kelas tersebut sebagai sebuah variable statis:
static const VRMessageType STEER_CHANGED;

Kemudian didalam file VRMessage.cpp, message STEER_CHANGED tersebut perlu diinisialisasi nilainya serta diregister kedalam game manager.
IMPLEMENT_ENUM(VRMessageType); const VRMessageType VRMessageType::STEER_CHANGED("Steer Changed", "Info", "Vehicle steer USER_DEFINED_MESSAGE_TYPE + 1); const VRMessageType VRMessageType::CREATE_VRENTITY("Create VR Entity", "Info", "New VR entity USER_DEFINED_MESSAGE_TYPE + 2); const VRMessageType VRMessageType::UPDATE_POS_VRENTITY("Update VRPos", "Info", "Update VR Entity USER_DEFINED_MESSAGE_TYPE + 3); void VRMessageType::RegisterMessageTypes(dtGame::MessageFactory& factory) { //Register new messages factory.RegisterMessageType<SteerMessage>(STEER_CHANGED); factory.RegisterMessageType<CreateMessage>(CREATE_VRENTITY); factory.RegisterMessageType<UpdatePosMessage>(UPDATE_POS_VRENTITY); // factory.RegisterMessageType<kelasMessage>(tipe message); }

changed", created", Position",

Untuk menginisialisasi awal tipe pesan didalam file VRMessage.cpp, kita memanggil konstruktor variable STEER_CHANGED dengan nilai-nilai parameter fungsi yang sesuai, misalnya Steer Changed sebagai nama pesan, Info sebagai pemberitahuan pesan, isi parameternya sendiri terserah, namun yang harus diperhatikan adalah parameter terakhir, dimana diisi USER_DEFINED_MESSAGE_TYPE + 1. Untuk message-message berikutnya yang kita buat, parameter terakhirnya harus +2, +3, dan seterusnya (tidak boleh sama). Kemudian tipe pesan baru tersebut harus ditautkan pada kelas yang mewakili isinya dengan cara diregister didalam fungsi RegisterMessageTypes:
factory.RegisterMessageType<SteerMessage>(STEER_CHANGED);

Untuk saat ini, kelas SteerMessage belum kita definisikan, namun inti dari kode diatas adalah menautkan tipe message STEER_CHANGED dengan kelas SteerMessage yang mengandung isi dari informasi yang ingin disampaikan.

Setelah mendefinisikan sebuah tipe pesan baru, maka langkah berikutnya adalah membuat sebuah kelas yang mewakili tipe pesan tersebut. Dalam hal ini, kelas steerMessage akan mewakili pesan STEER_CHANGED dan definisi kelasnya dimasukkan kedalam VRMessage.h sebagai berikut:
class CLASS_EXPORT SteerMessage : public dtGame::Message { public: /// Constructor SteerMessage() : Message() { AddParameter(new AddParameter(new AddParameter(new AddParameter(new AddParameter(new }

dtGame::BooleanMessageParameter("accel")); dtGame::BooleanMessageParameter("decl")); dtGame::BooleanMessageParameter("turnLeft")); dtGame::BooleanMessageParameter("turnRight")); dtGame::StringMessageParameter("vehicleId"));

void SetAccel( const bool accel ); const bool GetAccel() const; void SetDecl( const bool decl ); const bool GetDecl() const; void SetTurnLeft( const bool accel ); const bool GetTurnLeft() const; void SetTurnRight( const bool accel ); const bool GetTurnRight() const; void SetVehicleId( const std::string& vehicleId ); const std::string& GetVehicleId() const; protected: virtual ~SteerMessage(); };

Untuk mendefinisikan sebuah kelas message baru, kelas tersebut harus diturunkan dari kelas dtGame::Message. Keuntungan menggunakan framework pesan dtGame::Message yang disediakan oleh Delta3D adalah kemudahannya dalam mengirim pesan tersebut lewat network, dimana untuk mengirim pesan tersebut lewat network, kita tidak perlu membuat sebuah paket network khusus lagi, semuanya sudah ditanggung oleh Game Manager Delta3D. Tujuan kelas message tersebut hanyalah sebagai penyimpan nilai data-data yang ingin kita kirim didalam pesan. Karena input yang kita berikan kepada kendaraan berupa penekanan pada tombol up, down, left, dan right, maka kelas message tersebut harus menyimpan 4 variabel Boolean untuk mewakili keadaan masing-masing tombol tertekan atau tidak. Kemudian pesan kita tersebut juga harus menyimpan nama entitas kendaraan tujuan yang ingin dikendalikan, nama entitas tersebut disimpan didalam sebuah string. Bila kita ingin menggunakan fungsi7

fungsi dtGame::Message secara benar dalam framework Delta3D, maka pendefinisian variable yang ingin kita simpan tidak dapat menggunakan cara C++ biasa seperti bool accel; namun harus menggunakan pendefinisian parameter langsung didalam konstruktor kelas Messagenya:
SteerMessage() : Message() { AddParameter(new AddParameter(new AddParameter(new AddParameter(new AddParameter(new }

dtGame::BooleanMessageParameter("accel")); dtGame::BooleanMessageParameter("decl")); dtGame::BooleanMessageParameter("turnLeft")); dtGame::BooleanMessageParameter("turnRight")); dtGame::StringMessageParameter("vehicleId"));

Pada kutipan kode diatas, kelas SteerMessage menyimpan empat nilai boolean, masing-masing dengan nama accel,decl,turnLeft,dan turnRight serta juga menyimpan sebuah string vehicleId yang menyimpan nama entitas tujuan. Jadi bila kita ingin menambahkan data baru yang harus disimpan didalam message tersebut kita cukup menambahkan kode kedalam konstruktor sebagai berikut:
AddParameter(new dtGame::TipeDataMessageParameter("Nama variabel"));

Jenis tipe data yang dapat disimpan adalah semua tipe data yang native dalam C++ sepert int, float, double, bool, dan juga string yang bukan tipe data native. Untuk setiap data yang didefinisikan dengan cara AddParameter diatas, kita perlu membuat metode-metode Setter dan Getter nya. Penamaannya bebas namun supaya mudah diingat sebaiknya mengikuti contoh berikut ini (metode Setter dan Getter untuk data accel):
void SetAccel( const bool accel ); const bool GetAccel() const;

Didalam file VRMessage.cpp, kita harus mengisi fungsi-fungsi Setter dan Getter yang telah kita definisikan diatas, contohnya adalah sebagai berikut:
void SteerMessage::SetAccel(const bool accel) { dtGame::BooleanMessageParameter* accelParam = static_cast< dtGame::BooleanMessageParameter* >( GetParameter( "accel" ) ); accelParam->SetValue( accel ); } const bool SteerMessage::GetAccel() const { const dtGame::BooleanMessageParameter* accelParam = static_cast< const dtGame::BooleanMessageParameter* >( GetParameter( "accel" ) ); return accelParam->GetValue();

Seperti yang dapat dilihat dari kode diatas, tujuan fungsi Setter dan Getter tersebut adalah untuk menghindari penulisan static_cast yang berulang setiap kali kita ingin mengakses parameter dari message tersebut. Sekarang kita sudah memiliki sebuah kelas yang menyimpan semua tipe message dan kelas message itu sendiri. Pada bagian berikutnya, kita akan membuat sebuah komponen input yang menerima input dari pengguna dan mengirim pesan yang baru saja kita buat kepada actor yang bersangkutan. 1.4 Component

Sebuah komponen adalah bagian dari game yang memiliki fungsi khusus dan tidak dapat diperankan didalam game. Contohnya adalah input, networking, game logic, dll. Sesuai dengan contoh message yang diatas, maka pada bagian ini akan dicontohkan cara membuat komponen dan menggunakan komponen tersebut untuk mengirim pesan. Untuk membuat sebuah kelas komponen, kelas tersebut harus diturunkan dari

dtGame:GMComponent atau turunannya. Turunan dari dtGame::GMComponent biasanya disediakan oleh Delta3D sebagai komponen template yang bisa kita gunakan, contohnya adalah dtNetGM::ClientNetworkComponent yang merupakan template untuk membuat komponen client dari sebuah sistem client-server. Bagian yang penting dari sebuah komponen adalah metode ProcessMessage. Metode tersebut dapat di override untuk kita gunakan sebagai pemroses message-message yang diterimanya. kesempatan kali ini, kita akan menggunakan

dtGame::BaseInputComponent, kelas tersebut sudah mendefinisikan fungsi-fungsi untuk menerima input dari devais seperti keyboard dan mouse. Namun fungsi yang digunakan sementara ini hanyalah fungsi untuk mengecek apakah user telah memencet atau melepaskan tombol pada keyboard.
class CLASS_EXPORT InputComponent : public dtGame::BaseInputComponent { public: // Constructor InputComponent(const std::string &name); virtual bool HandleKeyPressed(const dtCore::Keyboard* keyboard, int key); virtual bool HandleKeyReleased(const dtCore::Keyboard* keyboard, int key);

// Handle messages from the GM void ProcessMessage(const dtGame::Message &message); protected: /// Destructor virtual ~InputComponent() { } private: /** * Simple helper method to fire a game event. This method creates the game event * message and sends it on to the Game Manager for processing. * @param event The game event to fire. */ void FireGameEvent(dtDAL::GameEvent &event); void FireSteerChangedMessage(bool accel, bool decl, bool turnLeft, bool turnRight, std::string& vehicleId); bool bool bool bool }; cmdaccel; cmddecl; cmdleft; cmdright;

Pada kode diatas, fungsi FireGameEvent dan FireSteerChangedMessage hanya merupakan prosedur-prosedur pembantu, dan dibuat agar kita tidak perlu mengetik kode yang sama berulang kali hanya untuk mengirim sebuah message. Khususnya, FireSteerChangedMessage adalah sebuah prosedur pembantu yang digunakan untuk mengirim sebuah pesan STEER_CHANGED.

bool InputComponent::HandleKeyPressed(const dtCore::Keyboard* keyboard, int key){ bool verdict(false); switch( key ){ case osgGA::GUIEventAdapter::KEY_Up: cmdaccel = true; break; case osgGA::GUIEventAdapter::KEY_Down: cmddecl = true; break; case osgGA::GUIEventAdapter::KEY_Left: cmdleft = true; break; case osgGA::GUIEventAdapter::KEY_Right: cmdright = true; break; //case osgGA::GUIEventAdapter::KEY_Return: // FireGameEvent(*mToggleEngineEvent.get()); // break; case osgGA::GUIEventAdapter::KEY_BackSpace: { //Bagian ini tidak perlu dibahas } break; case osgGA::GUIEventAdapter::KEY_Escape: { dtGame::GameApplication::GetInstance(0)->Quit(); verdict = true; /*while( !mToAdd.empty() ) mToAdd.pop(); while( !mToRemove.empty() ) mToRemove.pop(); while( !mObjects.empty() ) mObjects.pop();*/ } break; default: break; } //change this to be more structured later std::string vid = this->GetName(); FireSteerChangedMessage(cmdaccel,cmddecl,cmdleft,cmdright,vid);

10

return verdict; } bool InputComponent::HandleKeyReleased(const dtCore::Keyboard* keyboard, int key){ switch( key ){ case osgGA::GUIEventAdapter::KEY_Up: cmdaccel = false; break; case osgGA::GUIEventAdapter::KEY_Down: cmddecl = false; break; case osgGA::GUIEventAdapter::KEY_Left: cmdleft = false; break; case osgGA::GUIEventAdapter::KEY_Right: cmdright = false; break; default: break; } //change this to be more structured later std::string vid = this->GetName(); FireSteerChangedMessage(cmdaccel,cmddecl,cmdleft,cmdright,vid); return false; }

Kode diatas adalah kode untuk mendapatkan input dari pengguna. Saat pengguna memencet sebuah tombol, HandleKeyPressed akan berjalan dan memasukkan informasi tombol yang ditekan kedalam variable key. Variable tersebut kemudian kita tes apakah mengandung kode yang merepresentasikan tombol Up, jika iya, maka variable cmdaccel diberi nilai true. Setelah semua nilai cmdaccel, cmddecl, cmdleft, dan cmdright didapatkan, maka data tersebut dikirim ke FireSteerChangedMessage untuk dipaket sebagai sebuah message. Metode yang sama digunakan oleh HandleKeyReleased.

/////////////////////////////////////////////////////////////////////////////// void InputComponent::FireSteerChangedMessage(bool accel, bool decl, bool turnRight, std::string& vehicleId) { dtCore::RefPtr<SteerMessage> steerMessage; InputComponent::GetGameManager()->GetMessageFactory(). CreateMessage(VRMessageType::STEER_CHANGED, steerMessage); steerMessage->SetAccel(accel); steerMessage->SetDecl(decl); steerMessage->SetTurnLeft(turnLeft); steerMessage->SetTurnRight(turnRight); steerMessage->SetVehicleId(vehicleId); dtNetGM::ClientNetworkComponent* cnc static_cast<dtNetGM::ClientNetworkComponent*>(InputComponent::GetGameManager()>GetComponentByName(dtNetGM::ClientNetworkComponent::DEFAULT_NAME)); steerMessage->SetDestination(cnc->GetServer()); InputComponent::GetGameManager()->SendMessage(*steerMessage); InputComponent::GetGameManager()->SendNetworkMessage(*steerMessage); }

turnLeft,

bool

Baris-baris kode diatas merupakan inti dari cara melakukan pengiriman message. Pertama kita harus membuat message yang ingin dikirm, dalam hal ini adalah SteerMessage.
dtCore::RefPtr<SteerMessage> steerMessage;

11

dtCore::RefPtr<kelas> merupakan template smart pointer dan sama dengan penulisan kelas* yang merupakan pointer biasa. Keunggulan menggunakan dtCore::RefPtr adalah bahwa destruksi dari kelas-kelas tersebut dilakukan secara otomatis sehingga pemanggilan delete tidak diperlukan. Namun dtCore::RefPtr hanya dapat digunakan pada kelas-kelas dari Delta3D atau turunannya. Setelah SteerMessage tersebut telah dibuat, berikutnya adalah mengisinya. Biasanya untuk sebuah pointer biasa, cara mengisinya adalah dengan menggunakan operator new seperti.
steerMessage = new SteerMessage();

Namun untuk beberapa kelas yang disimpan didalam game manager, seperti Actor dan messages, cara mengisi pointer tersebut adalah dengan menggunakan sebuah factory (design pattern yang khusus untuk mengelola pembuatan objek). Jadi untuk mengisi variabel steerMessage tersebut, variabel tersebut dimasukkan sebagai salah satu parameter fungsi CreateMessage dari factory sebagai berikut:
InputComponent::GetGameManager()->GetMessageFactory(). CreateMessage(VRMessageType::STEER_CHANGED, steerMessage);

Setelah baris kode tersebut, pointer steerMessage sekarang sudah menunjuk ke sebuah alamat objek SteerMessage yang valid. Maka sekarang kita dapat menggunakan fungsi-fungsi setter yang telah kita buat pada SteerMessage sebelumnya untuk mengisi nilai-nilai dari pesan yang ingin kita kirim:
steerMessage->SetAccel(accel); steerMessage->SetDecl(decl); steerMessage->SetTurnLeft(turnLeft); steerMessage->SetTurnRight(turnRight); steerMessage->SetVehicleId(vehicleId);

Kemudian untuk mengirim pesan tersebut kepada Game Manager, dimana Game Manager kemudian akan bertindak sebagai tukang pos yang memastikan pesan tersebut terkirim, kita tinggal memanggil fungsi sendmessage dengan message kita sebagai parameter fungsinya:
InputComponent::GetGameManager()->SendMessage(*steerMessage);

Untuk mengirim pesan tersebut melalui network, maka pertama-tama kita perlu memberi tahu destination atau tujuan pesan, apakah ke client lain atau server. Pada kasus ini adalah server, maka cara mencari tahu server yang terhubung dengan aplikasi kita adalah dengan fungsi get server yang dimiliki client network component kita.

12

dtNetGM::ClientNetworkComponent* cnc static_cast<dtNetGM::ClientNetworkComponent*>(InputComponent::GetGameManager()>GetComponentByName(dtNetGM::ClientNetworkComponent::DEFAULT_NAME)); steerMessage->SetDestination(cnc->GetServer());

kemudian pada steerMessage kita men SetDestination tujuan pengiriman pesan. Lalu pesan tersebut dikirimkan dengan menggunakan SendNetworkMessage.
InputComponent::GetGameManager()->SendNetworkMessage(*steerMessage);

Sekarang

pesan

kita

akan

terkirim

setiap

kali

kita

menggunakan

fungsi

FireSteerChangedMessage. Sebagai bahan tambahan, fungsi ProcessMessage dalam sebuah komponen dapat digunakan untuk menangkap sebuah message tertentu, pada kode dibawah ini dicontohkan untuk menangkap message CREATE_VRENTITY.
////////////////////////////////////////////////////////////////////////// void InputComponent::ProcessMessage(const dtGame::Message& message) { if (message.GetMessageType() == VRMessageType::CREATE_VRENTITY) { const CreateMessage &crMessage = static_cast<const CreateMessage&>(message); std::string oid = crMessage.GetObjectId(); } }

1.5

Actor

Seperti yang telah dijelaskan sebelumnya, demua benda yang dapat direpresentasikan didalam GM didefenisikan sebagai Actor (Pemeran). Untuk mendefinisikan sebuah Actor didalam Delta3D, kita harus mengerti tentang konsep hubungan Actor dan Actor Proxy. Semua fungsionalitas utama yang dimiliki pemeran game sepert Tank, Roket, dll dimasukkan kedalam kelas Actor. Namun karena arsitektur yang dimiliki oleh Delta3D menspesifikasikan agar Actor tersebut dapat diload kedalam STAGE secara dinamis, maka kelas yang dimengerti oleh Game Manager Delta3D adalah kelas Actor Proxy. Maka untuk setiap kelas Actor yang dibuat, harus dibikin pula kelas Actor Proxy padanannya.

Figure 2 Game actor proxy Pesan yang telah dikirim melalui fungsi sendmessage dapat diterima oleh kelas game actor melalui dua cara. Cara yang pertama adalah dengan meng-override fungsi ProcessMessage. 13

Fungsi tersebut sama dengan fungsi ProcessMessage pada komponen, dimana fungsi tersebut menerima semua message yang masuk. Cara yang kedua adalah dengan membuat sebuah fungsi khusus (invokable) yang menerima satu pesan khusus. Untuk pesan SteerMessage yang kita buat sebelumnya, kita akan membuat sebuah invokable yang dipanggil setiap kali pesan SteerMessage tersebut diterima, fungsi tersebut dinamakan SteerChanged. Langkah pertama untuk membuat sebuah invokable adalah dengan mendeklarasikannya pada Actor yang akan menerima pesan message yang bersangkutan. Pada contoh ini, kelas VehicleActor akan menerima pesan SteerMessage yang dikirim oleh input component, oleh sebab itu maka kita fungsi SteerChanged yang menerima sebuah parameter message.
class CLASS_EXPORT VehicleActor : public dtActors::GameMeshActor { public: // Constant identifier for our game event handler method. static const std::string EVENT_HANDLER_NAME; // Constructs the tank actor. VehicleActor(dtGame::GameActorProxy &proxy); //Custom Steer Changed Invokable void SteerChanged(const dtGame::Message &steerMessage); void UpdateEntity(const dtGame::Message &updateMessage); virtual void TickLocal(const dtGame::Message &tickMessage); virtual void TickRemote(const dtGame::Message &tickMessage); virtual void ProcessMessage(const dtGame::Message &message); virtual void OnEnteredWorld(); //must be called directly void SetPosition(osg::Vec3 pos); void SetEntityId(std::string& id); const std::string& GetEntityId() const; void SetObjectId(std::string& id); const std::string& GetObjectId() const; protected: virtual ~VehicleActor(); Synchronizer* synchronizer; private: // private vars dtCore::RefPtr<dtCore::ParticleSystem> mDust; dtCore::RefPtr<Vehicle> mVehicleInterface; std::string mEntityId; std::string mObjectId; osg::Vec3f intpos[2]; osg::Vec3f intposhpr[2]; double inttime[2]; bool applytransform; bool intready; float timeelapsed; float deltatime;

14

};

Fungsi SteerChanged message tersebut digunakan sama seperti ProcessMessage, hanya saja fungsi tersebut khusus hanya menerima message SteerMessage yang kita definisikan sebelumnya.
void VehicleActor::SteerChanged(const dtGame::Message &steerMessage){ const SteerMessage &steer = static_cast<const SteerMessage&>(steerMessage); if(!IsRemote()&& GetEntityId() == steer.GetVehicleId()) { float pedal,brake,steering; //because we are using binary steering, convert to analog (float) if(steer.GetAccel()) pedal = 1.0f; else pedal = 0.0f; if(steer.GetDecl()) brake = 1.0f; else brake = 0.0f; if(steer.GetTurnRight()) steering = 1.0f; else if(steer.GetTurnLeft()) steering = -1.0f; else steering = 0.0f; mVehicleInterface->Steer(pedal,brake,steering); } }

Kemudian setelah fungsi tersebut telah didefinisikan, kita perlu member tahu game manager bahwa untuk setiap pesan SteerMessage yang diterima, langsung diterusan pada fungsi SteerChanged yang kita definisikan sebelumnya. Langkah pertamanya adalah dengan membuat sebuah konstanta yang mewakili nama invokable kita, dalam hal ini adalah

STEER_CHANGED_INVOKABLE.
/** * Our proxy class for the hover tank actor. The proxy contains properties, * invokables, and hover tank actor. */ class CLASS_EXPORT VehicleActorProxy : public dtActors::GameMeshActorProxy { public: //register new invokable type static const std::string STEER_CHANGED_INVOKABLE; static const std::string UPDATE_ENTITY_INVOKABLE; // Constructs the proxy. VehicleActorProxy(); // Creates the properties that are custom to the hover tank proxy. virtual void BuildPropertyMap(); // Create custom build invokable

15

virtual void BuildInvokables(); protected: virtual ~VehicleActorProxy(); // Creates an instance of our hover tank actor virtual void CreateActor(); // Called when this proxy is added to the game manager (ie, the "world") // You can respond to OnEnteredWorld on either the proxy or actor or both. virtual void OnEnteredWorld(); };

Didalam file PhysicsTankActor.cpp, STEER_CHANGED_INVOKABLE tersebut diisi dengan nilai string yang mewakil namanya sebagai berikut:
const std::string VehicleActorProxy::STEER_CHANGED_INVOKABLE("Steer Changed Message");

Kemudian

nama

STEER_CHANGED_INVOKABLE

tersebut

ditautkan

pada

fungsi

SteerChanged didalam fungsi BuildInvokable didalam kelas VehicleActorProxy.


void VehicleActorProxy::OnEnteredWorld() { //Register an invokable for Game Events... RegisterForMessages(dtGame::MessageType::INFO_GAME_EVENT); RegisterForMessages(VRMessageType::STEER_CHANGED, STEER_CHANGED_INVOKABLE); RegisterForMessages(VRMessageType::UPDATE_POS_VRENTITY, UPDATE_ENTITY_INVOKABLE); // Register an invokable for tick messages. Local or Remote only, not both! if (IsRemote()) RegisterForMessages(dtGame::MessageType::TICK_REMOTE, dtGame::GameActorProxy::TICK_REMOTE_INVOKABLE); else RegisterForMessages(dtGame::MessageType::TICK_LOCAL, dtGame::GameActorProxy::TICK_LOCAL_INVOKABLE); dtActors::GameMeshActorProxy::OnEnteredWorld(); } void VehicleActorProxy::BuildInvokables() { //Add Custom steer invokable AddInvokable(*new dtGame::Invokable(STEER_CHANGED_INVOKABLE, dtDAL::MakeFunctor(static_cast<VehicleActor&>(GetGameActor()), &VehicleActor::SteerChanged))); AddInvokable(*new dtGame::Invokable(UPDATE_ENTITY_INVOKABLE, dtDAL::MakeFunctor(static_cast<VehicleActor&>(GetGameActor()), &VehicleActor::UpdateEntity))); GameMeshActorProxy::BuildInvokables(); }

Cara menautkan tipe pesan STEER_CHANGED dengan fungsi SteerChanged adalah dengan meregisterkan pesan tersebut dengan nama invokable STEER_CHANGED_INVOKABLE didalam fungsi OnEnteredWorld. Kemudian nama invokable

16

STEER_CHANGED_INVOKABLE tersebut dihubungkan dengan callback SteerChanged didalam fungsi BuildInvokables. Setelah semua langkah tersebut dilakukan, maka setiap pesan SteerMessage yang dikirim akan diterima oleh fungsi SteerChanged didalam kelas Vehicle Actor. Hal terakhir yang perlu dilakukan sekarang adalah memasukkan actor tersebut kedalam registry Game Manager agar dapat dikenal dan dipanggil oleh Game Manager. Hal tersebut dilakukan didalam kelas GameActorsRegistry.
class CLASS_EXPORT GameActorsRegistry : public dtDAL::ActorPluginRegistry { public: // Constructs our registry. GameActorsRegistry(); Creates the actor types easy access when needed.

// Registers actor types with the actor factory in the super class. virtual void RegisterActorTypes(); public: static dtCore::RefPtr<dtDAL::ActorType> mVehicleActorType; };

Kelas tersebut merupakan turunan dari kelas dtDAL::ActorPluginRegistry. Didalam fungsi RegisterActorTypes kita meregister actor kita kedalam GameManager. Sebelumnya kita harus mendefinisikan sebuah ActorType yang mewakili tipe actor kita, dalam hal ini adalah mVehicleActorType. Didalam file GameActorsRegistry.cpp kita melakukan inisialisasi awal mVehicleActorType sebagai berikut:
dtCore::RefPtr<dtDAL::ActorType> dtDAL::ActorType("VehicleActor","TutorialActors", "Vehicle with physics.")); GameActorsRegistry::mVehicleActorType(new

Kemudian didalam fungsi RegisterActorTypes kita meregister tipe actor kita dengan menautkan proxy VehicleActorProxy dengan mVehicleActorType yang diinisialisasi sebelumnya sebagai berikut.
void GameActorsRegistry::RegisterActorTypes() { mActorFactory->RegisterType<VehicleActorProxy>(mVehicleActorType.get()); }

17