Game Development Community

TGE / C++ Conceptual Problem (with strings!)

by Chris Fitzgerald · in Torque Game Engine · 08/28/2007 (10:40 pm) · 4 replies

Greetings. I'm trying to get a general grasp of the C++ side of the TGE engine before I delve into it. As I'm new to the engine, I am having a hard time with string definitions. My coding style allows me to easily shape other people's code to what I want, as soon as I get a grasp of it. Until then, it's a slow process. I'm also bad with pointers :D

The problem
While I appreciate the datablock structure, it appears that many of the variables set in the Player.cs datablocks can be hard-coded for efficiency. Some of the values are actually decared twice -- once in PlayerData::PlayerData() in Player.cc and once in the datablock. I understand that datablocks are sent at the beginning of the mission and override the hardcoded values in PlayerData::PlayerData(). I intend to reduce load time, though, by making a lot of the variables in datablocks hardcoded. Since most of my game systems will be coded in C++ rather than TorqueScript, this make sense.

I decided to add a new string to player.h under struct PlayerData: public ShapeBaseData. Let's call it "zomething." I tried the standard "std::string zomething;" declaration but that didn't work. I went and made sure that was included (and it was). Well, that's interesting. So, I did a bunch of research and it appears that it's not possible to use standard C++ string declarations in TGE.

It appears that I have to use "const char*" to point to a string declaration. This doesn't make a lot of sense to me. From what I have researched, this declaration adds a string to the String Table. I assume this is for cross-platform compatability.

So I tried adding "const char* zomething" to the player.h file. That actually worked (I could compile). But, there were additional problems. I noticed that I need to add my variable to PlayerData::initPersistFields. From what I know, this just initializes the various fields in a datablock. Actually, I'm not sure what it does but all the other variables in player.h and player.cs are in here. So, I did the following:

addField("zomething", TypeString, Offset(zomething, PlayerData))

That seemed to work. But, I'm not sure what "TypeString" is. I made it up. I just followed the other examples (TypeS32). TypeChar works as well.

Okay, not so bad. But then I get to this: PlayerData::packData --- stream->write(zomething); Okay, this works. I'm not quite sure what this does. I assume that it initializes and feeds the datablock that is sent to the client.

But then I get to PlayerData::unpackData, which I assume is used to unpack datablocks client-side. So, all the other variables read something like stream->read(&zomething). Okay, it appears that I am simply streaming the pointer value of zomething. That sorta makes sense. But, I get the following error:

engine\game\player.cc(672) : error C2664: 'bool Stream::read(ColorI *)' : cannot convert parameter 1 from 'const char' to 'ColorI *'
1> Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast

At tihis point, I'm lost.

In general, all I want to do is:

1. Add a new string variable to player.h.
2. Initialize a default value in player.c
3. Have the default value overrid by the datablock.
4. Be able to update the value as time goes on.

I thought of some solutions, but not sure how they would fly. I could declare zomething as "char zomething[10]" in player.h but then I would be absolutely limited to 11 characters in zomething.

The second option is to create a vector. Would something like "vector zomething" work?

I really appreciate any help :)

#1
08/29/2007 (12:23 am)
You've got the right spirit in copying something else which already does what you want,
but you probably want to look at a preexisteing example of a string rather than an F32.
check out how textureName is done in DebrisData.

but in summary:
* add a StringTableEntry member to the class declaration of the datablock.
* initialize the member in the class constructor with StringTable->insert().
* if you want it exposed to script, do that via AddField() in myClass::initPersistFields().
* if you want it transmitted to clients, add it to packUpdate() and unpackUpdate() with stream->writeString() and stream->readString().

.. that's about it for your desires 1, 2, and 3.

however, number 4: "Be able to update the value as time goes on" is fundamentally at odds with the notion of datablocks. datablocks are designed to be static data. It's data which is shared by All instances of objects of that class, and it never changes. If you want it to be a value which changes over time or which is different from instance to instance, you should add it to the object (eg player) itself rather than the associated datablock. you'll also want to create a new NetMask for your member field, and also a "setter" console function to set the NetMask when you set the value.

it's a bit of work to grok it all, but you'll get there.
#2
08/29/2007 (2:04 am)
Thank you for the response! I'll be certain to take a look at DebrisData.

I realize that I was trying to add to PlayerData which is used for all players. I'll look at using Player instead. And maybe touch up on my C++ :)
#3
08/29/2007 (11:19 am)
This tutorial covers pretty much everything you are asking for.
#4
08/29/2007 (1:33 pm)
Thank you. Another good reference for others getting started with this is here.