6.Mar.07 Lecture 6
Networking, Modeling and Animations, Interiors

RAR files

Please follow instructions and upload in zip format.

Motivate

63 Days left, probably less since you will have a screen shot and marketing materials due the week before so the judges can prepare...

Torque Networking

In Torque, every game is a server, but we are going to take a much closer look and the book does a good job of separating out a server from a single player game.

The most common example is a chat system.

If your code is running on the client, you can call:

CommandToServer('MyCommand',%somedata);

On the server, the following function would get called.

function ServerCmdMyCommand(%client,%data)
{
// do something
}

If your code is running on the server, you can call:

CommandToClient(%client,'Something',%data);

On the client, the following code would get called::

clientCmdSomething(%sender,%data)
{
  // do something
}

This is called direct messaging and you can use it to send messages back and forth.

At some point on the server you will want to send a message to a specific client, or to all clients. You can iterate through the clients using the following.

%count = ClientGroup.getCount();
for (%i=0;%i < %count; %i++)
  {
  %client = ClientGroup.getObject(%i);
  CommandToClient(%client,'Broadcast',%sender,%msg);
  }

client code looks like this:

clientCmdBroadcast(%sender,%data);

A good time to start looking at C/C++ code.

No time is going to be perfect to start digging into the engine code, but I think this is a good opportunity to get our feet wet. The question we are searching for, what is that ClientGroup object?

Looking through the common directory, can you find where ClientGroup is declared. If it's a variable, it must be declared somewhere right???

A search for ClientGroup returns audio.cs, clientConnection.cs, message.cs, missionLoad.cs and server.cs. Looking at all these files, they are all just references.

So, let's search through the c code under the engine directory. This returns: simBase.cc, simBase.h, simManager.cc, aiClient.cc, aiConnection.cc, debugView.cc, gameConnection.cc, gameProcess.cc, netObject.cc, pathManager.cc, lightning.cc and weatherLightning.cpp.

Just based on names we can narrow our search to: simBase.cc, simBase.h, simManager.cc, gameConnection.cc, gameProcess.cc, and netObject.cc.

Yes, I spent a lot of time on this rabbit hole and you are going to save some time if you pay attention.

Go search through each of those files for ClientGroup and you will not find a declaration for the variable. Search all you want, it doesn't exist.

So, how does a variable get used if it is never declared? Yes, C and C++ require variables be declared before they are used.

Notice that most of the calls look like this:

Sim::getClientGroup()->addObject(this);

So, if you may want to extend your search for getClientGroup().

A quick note on namespaces since Sim is a name space. This is a simple C++ way of creating a code group. You could name two routines getObject in different namespaces and they will not conflict. Before namespaces, function and variable names had to be unique across the entire code base. It's nothing fancy, just declare a namespace MyNameSpace with an open bracket {, write a bunch of code and variable declarations and close the bracket. That code can be accessed by using the name space, just like the Sim namespace above. I did find a good namespace tutorial.

Since we struck out on the declaration of ClientGroup, maybe it is in the getClientGroup. Did you find it? Nope, me neither. Search all you want, it doesn't exist. Odd, now we have a variable and a function that have not been declared.

What is going on!!? The C language can be quite complex and while that is one of it's strengths, it is also one of it's weaknesses since it can be hard to learn and manage.

In this case, do you remember our discussion on the preprocessor and macros? Take a look at simBase.h and the line.

DeclareNamedGroup(ClientGroup);

This should be telling in that you are seeming to call a function with a variable that has not been declared. So, let's take a look at that function. In our search we find.

#define DeclareNamedGroup(set) extern SimGroup *g##set;inline SimGroup *get##set() { return g##set; }

Ah ha! A macro is the culprit! Remember, this does a substitution before the compiler runs and this would be the result of that substitution.

extern SimGroup *gClientGroup; inline SimGroup *getClientGroup() { return gClientGroup;}

There it is, the declaration of getClientGroup, which was placed into our code with a macro. There is more behind the scenes which connects up the script side of ClientGroup with getClientGroup, but this was just to get our feet wet searching the code to come to a better understanding of the Torque engine. We'll try and come back to that later.

Back to our regular lecture

The other networking communication is with Player Event Control Triggers. We saw these last week when digging through the orc code. There are six Player Event Control Triggers.

Simply setting the variable $mvTriggerCount# = 200; (where # is the number 0 - 5) will cause the server to receive the MyAvatarClass::onTriggerEvent 200 times. Triggers 0-3 are pre programmed (See page 241) for avatar actions, but you are free to change them or use the optional 4 and 5 triggers. note: I've tried setting the counter to a large number like the book suggest and could not prove that the onTrigger method was called more than once. This needs more investigation, your mileage may vary.

The MyAvatarClass is the name of the class you have defined in your player avatar's datablock. Do you know what that is for EMAGA5?

In server.cs

// Create the player object
%player = new Player()
{
  dataBlock = MaleAvatar;   // defined in players/player.cs
  client = %this;           // the avatar will have a pointer to its
};                           // owner's connection

In player.cs (which has the datablock for MaleAvatar)

datablock PlayerData(MaleAvatar)
{
className = OrcClass;
shapeFile = "~/data/models/avatars/orc/player.dts";
emap = true;
....
}

The answer is: MaleAvatar. It might be a good idea to add an MaleAvatar::onTrigger method which prints something to the log. Then to map a key to set $mvTriggerCount5 to some value and see what happens. This is in player.cs:

function MaleAvatar::onTrigger(%this,%obj,%triggerNum,%val)
{
  echo ("MaleAvatar onTrigger obj=" @ %obj @ " trignum=" @ %triggerNum @ " val=" @ %val @ "*********************");

  switch$(%triggerNum)
  {
    case 5:
	  echo("onTrigger5, val=" @ %val);
	  return;
  }
}

Then in presetkeys.cs

function DoJump(%val)
//----------------------------------------------------------------------------
// momentary upward movement, with character animation
//----------------------------------------------------------------------------
{
   $mvTriggerCount2++;

   echo ("*********Do Jump");
   $mvTriggerCount5++;
}
      

Since event 0 is fire, why would you want to do this instead of just creating a bunch of new objects on the client or sending an Command message? For one, you don't want to send a bunch of commands for each bullet. Sure, you could send one command that contained a number, but there are other things that could be at work here. This mechanism may take a different route than command messages and have a higher priority. I think we would all agree that weapons fire notification events would have a much higher network priority than chat. So this simpler mechanism was created. It also allows you to send a number that is the number of bullets that fire or some number of iterations you want the server to perform.

GameConnection

The GameConnection class is how you get your client to connect to your server. It looks like this.

%conn = new GameConnection(ServerConnection);
%conn.SetConnectArgs(%username);
%conn.Connect();

The Connect() method launches a convoluted process that you really don't have to know a lot about. The Connect() method will make calls back the the GameConnection object when events or errors occur during connection. I have not found a good list of these calls online, your best bet is pages 243-248 in the textbook. The one we have seen implemented in all of our game so far is onConnect(). This is called when the connection is made successfully. This is fine for our test games, but for a commercial quality game you would need to come back and implement the other call backs.

Finding Servers

Most lan games just search all the machines on a specific subnet to find an active game server. They do this by going through a list of ip address and connecting to a specific port. If the connection succeeds and the machine exchanges some preset information, then the server is available. We had a sample of this last year.

There are two types of internet game systesm. A central directory server which lists all the available servers on the internet and you would then make connections directly to that server. The second is a server that manages the directory and all the game to game communications. The second is easier to implement for users as there will probably be less firewall issues, but it also requires an expensive infrastructure of networking equipment and servers. The simple directory server is much easire to implement for the developer, but requires more work on the part of the game player in that they will have to do some firewall or router configuration, ie, setting up port forwarding.

Torque has the capability of all three types of games, but we will be mostly focused on lan games because they are all that will be required for the final game and taking that to a directory or server based system will just be an added layer and not a different architecture.

Torque has builtin functions to find games on a lan. This is the function.

QueryLANServers(port,flags,gtype,mtype,minplayers,maxplayers,maxbots,region,ping,cpu,filters,buddycount,buddylist);

More information QueryLANServers is on TDN. Two variables this uses outside of the actual function call are.

$Client::GameTypeQuery = "3D2E";
$Client::MissionTypeQuery = "Any";

The GameTypeQuery is a Hexadecimal number and I will do a quick overview of hexadecimal since I don't think the entire class has had assembly language or computer architecture yet. A quick explanation here is that you can use four digits, and the digits are from 0-F. So, valid numbers are "0000" - "FFFF". Or you could use any combination of those letters and numbers. "K00K" would be valid.

TCP/IP and UDP ports. Network traffic works on well defined ports. Most traffic uses TCP/IP and web traffic is port 80, while mail traffic is on port 25. For server, you tell it what port you want to listen on and you will get notifications when connections are made and from there notifications when information is received from specific connected clients. For clients, you make a connection to a specific ip address on a specific port and can send and receive information to/from that connection. TCP/IP guarantees (in order) delivery of information written to the network, while UDP information may not ever arrive. UDP is faster, but you will have packet loss.Ports below 1024 are protected and not always available. For your games you should always choose a port > 1024. For specific information about what ports are used for what, logon to a unix/linux machine and look at the /etc/services file.

The book shows a lot of gui interaction and how to populate a list of LAN game servers. You should use this for your game, it is a great example. The dedicated server is interesting and useful, but you should leave it for reference and save the complexity for a future exercise when you turn your game into a commercial game.

 

Power Ups and how they work

I think a good discussion that is not made in the book is powerups and how they are implemented.

Search for FirstAid in the EMAGA/control directory. You should find it in: book_ch5.mis, and item.cs. Let's look at item.cs.

//-----------------------------------------------------------------------------
// Health Patchs cannot be picked up and are not meant to be added to
// inventory.  Health is applied automatically when an objects collides
// with a patch.
//-----------------------------------------------------------------------------

datablock ItemData(FirstAidKit)
{
   // Mission editor category, this datablock will show up in the
   // specified category under the "shapes" root category.
   category = "Health";

   // Basic Item properties
   shapeFile = "~/data/models/items/healthPatch.dts";
   mass = 1;
   friction = 1;
   elasticity = 0.3;

   respawnTime = 600000;
   // Dynamic properties defined by the scripts
   repairAmount = 200;
   maxInventory = 0; // No pickup or throw
};

function FirstAidKit::onCollision(%this,%obj,%col)
{
   // Apply health to colliding object if it needs it.
   // Works for all shapebase objects.
   if (%col.getDamageLevel() != 0 && %col.getState() !$= "Dead" )
   {
   echo("!!@@@@@@@@@@@@@ col"@%col);
      %col.applyRepair(%this.repairAmount);
      %obj.respawn();
      if (%col.client)
      {
         messageClient
              (%col.client,'MSG_Treatment','\c2Medical treatment applied');
      }
   }

This is a very simple item. When it is picked up, it calls applyRepair on the object that touches it. In this case, Player. The amount is 200 as listed in the datablock. It then respawns itself in 60000 milliseconds or 60 seconds.

This is really a good example to try and move to the GameOne from the beginning tutorial. When you move those two program blocks to the logoitem.cs file and move the ~data/models/items direcotry (and match the new name to the datablock, and try and place a shape/Health/FirstAidKit to the mission what happens. Probably nothing. When you check the log, you see something like. Cannot find ItemData::create. What does this mean. It means you should go back to the EMAGA and search for ItemData::create. That routine is used by the editor to place items on the mission. Without it, the shape connection to the editor is incomplete. Now you know exactly how to get non-static items into the mission editors. Static items are easy, just place the dts file in the models directory, it's the items that have code behind them that are a little more tricky, but three or four lines of code isn't a lot to ask.

If there are no more errors, does anything happen when you collide with the FirstAidKit? Since I know it won't work, why not?

if (%col.getDamageLevel() != 0 && %col.getState() !$= "Dead" )
{

This line limits the effects of the FirstAidKit if the damage leve is equal to zero. Since there is no damage in the GameOne game, the health pack will never execute, but the collision is working. You can see the collision work if you put an:

echo("FirstAidKit collision working");

just before the if statement.

Did you notice that the First Aid Kit spins in the mission? You didn't add anything extra in the code to do this did you? Of course not, but ItemData::create looks like this.

function ItemData::create(%data)
{
// The mission editor invokes this method when it wants to create
// an object of the given datablock type. For the mission editor
// we always create "static" re-spawnable rotating objects.
%obj = new Item() {
dataBlock = %data;
static = true;
rotate = true;
};
return %obj;
}

Do you notice the rotate=true; line in the creation of the ItemData datablock? A simple flag and Torque will automatically make your powerups spin! Now that's simplicity.

FirstAidKits are one thing, what about a non-standard powerup that gave your weapon a special bullet (See Think Tanks) or some sort of shielding from damage (shields in unreal)?

Look at this untested code for a second.

function ExplosiveBulletPowerUp::onCollision(%this,%obj,%col)
{
   if ((%col.getDamageLevel() != 0 && %col.getState() !$= "Dead" ) && (%col.getClassName() $= "Player"))
   {
      echo("Big Bullet Power Up");
      %col.turnOnBigBulletPowerup();
      %obj.respawn();
      if (%col.client)
      {
         messageClient
              (%col.client,'MSG_Treatment','\c2Medical treatment applied');
      }
}

function PlayerBody::turnOnBigBulletPowerup(%this)
{
   %this.bigBulletPowerUp = true;
   %this.schedule( 25000, 0,  turnOffBigBulletPowerup, %this);
}


function PlayerBody::turnOffBigBulletPowerup(%this)
{
   %this.bigBulletPowerUp = false;
}

Again, this is not tested, but you should then be able to check the %player.bigBulletPowerUp flag when the weapon is fired and change the behaviour accordingly. The powerup would turn off after 25000 milliseconds (25 seconds). It might or might not make more sense to apply this to the weapon, but you can make it work either way.

Common folder

I don't have much to add to this chapter. You should read this and be familiar with it.

 

More Advanced 3D Modeling

This week we will be creating our own models for inclusion in our games. Chapter 13 and 14 in the All In One book has a great reference to Milkshape, textures and creating player models. I spent about 20 hours going through these two chapters so get started early. This week we will be building a character model and incorporating it in our game.

We will be using Milkshape 3D. For $35 you cannot beat this tool.

For the best results when exporting from Milkshape, I recommend the DTSPlus exporter written by Chris Robertson. Keep that link it was a really tough one to find even though everyone seems to recommend it.

The second tool we will be using is UV Mapper. This is a free tool (for the non-pro version) which will help us map images to our 3D objects.

The third tool that I like a lot is Blender. This is a free tool that was once a commercial product. It is much more robust than Milkshape, but has a bit of a quirky interface. I made some notes for a Blender lecture that I may or may not give. For the sake of speed it may just be best to use Milkshape and UVMapper since they are so well documented in our books. This IS a class about making games, not modeling and textures and we need to spend our time wisely.

3D studio max is also installed on the lab machines and is the tool the Garage Games folks used when building Torque. It is $3500 a seat and we don't have time to cover it in this class. You are welcome to use it for this class, but I'm probably not going to be able to help you if you get stuck.

Milkshape Overview

The book does a much better job of this than I could here. Read the book.

Viewports

Creating Objects

Selections

Transformations

Exporting to Wavefront .obj for texture mapping.

UV Mapper

Loading an object.

Separating out various objects.

Unwrapping various object.

Saving final object and bitmaps.

What is going on here? The obj now contains uv normals which are directly related to the bitmap.

Paintshop Pro

Painting the actual texture map

Milkshape

Re-importing the model

Building an avatar

Chapter 14 is a great resource and example for creating a model. You should go through this in detail, but you should pay attention to Page 616 before going through this entire tutorial. After it gets you to spend a ton of time creating a skeleton, it says that it is next to impossible to create a skeleton yourself that will work with the preexisting Torque biped animations. For this, Mr. Finney has included the Torque biped skeleton in the RESOURCES for Chapter 14. I disagree and think you should start with this and build your model around it since it will be hard to return later and retrofit your model to the skeleton. The file is called skeleton.ms3d and you should keep a copy of this in your game.

If you open this model in Milkshape and only see large circles, select File/Preferences/Misc and change Joint size to 0.02.

As far as I have read, don't move the joints around as this will cause a disconnect between Torque and your model. When you begin animating, this should be your reference position and you should build your model around this skeleton. The skeleton is tiny on Milkshape's scale and will cause problems with using the scale function with the mouse. You may have to study the skeleton as a reference and build you avatar at a larger size and shrink it down later.

Animation Sequences.

I'm building a hammer bot in class to show how animations get tied into the game engine. I'm not sure if we'll have time, but I'm going to try and show this full circle so we have an indepth understanding. This will be abbreviated, sorry it won't be as much use if you didn't come to class.

This is an overview of the actual hammer bot and the joints. First I created the model itself, created a metal texture with paintshop pro and assigned that texture to all the elements in the model

The joints from bottom to top are named: origin, mdpt, neck, leftshoulder, leftelbow, leftwrist, hammer.

      1. Build the model
      2. Add the skeleton
      3. Rig the skeleton to the model.
      4. Create a hammer animaition. My hammer animation runs from frame 1 to 72 and is too slow at the hammer, but this is just a demo.

I'm using the DTS Plus exporter. The book has a good follow along around page 208.

      1. Select File/Export/Torque DTS Plus
      2. Create the animation sequence.
      3. Export the dts file.
      4. next create the dsq animation sequence files by:
      5. Merging all the groups into one ending in a 0.
      6. Select File/Export/Torque DTS Plus
      7. Make sure both Generate cs file and Split DSQ Export boxes are check.
      8. Export again and give a file name without an extension.
      9. The exporter will create a DSQ file for each animation sequence using and _name in the file name.

You can now load the .cs file in an editor.

datablock TSShapeConstructor(HammerbotDts)
{
baseShape = "./hammerbot.dts";
sequence0 = "./hammerbot_hammer.dsq hammer";
}

See that it has associated the hammer.dsq animation with the name hammer. This is the name you gave it in the DTS Plus exporter.

You can use

Now to get it to animate in your game is a completely different matter. This is a good overview of shape based animation in Torque. The yellow book also discusses this a little pn page 169.

You have to create a datablock for your shape. The sample give is the healtkit.

datablock ItemData(HealthKit)
{
// Mission editor category, this datablock will show up in the
// specified category under the "shapes" root category.
category = "Health";

// Basic Item properties
shapeFile = "~/data/shapes/items/healthKit.dts";
mass = 1;
friction = 1;
elasticity = 0.3;
emap = true;

// Dynamic properties defined by the scripts
pickupName = "a health kit";
repairAmount = 50;
};

Adding a similar item to my hammerbot.cs file. Mine is located with the model dts file so may reference the dts files by shorter path names. You can use either method and this seemed faster to me, but more dangerous for long term. For code you want to have around for a long time, keep all the code in the same place and separate it from the visual elements.

datablock StaticShapeData(Hammerbot)
{
// Mission editor category, this datablock will show up in the
// specified category under the "shapes" root category.
category = "Bot";

// Basic Item properties
shapeFile = "./hammerbot.dts";
mass = 10;
friction = 1;
elasticity = 0.3;
};

I used StaticShapeData because ItemData rotated and was more geared towards powerups and items than funky animated figures. Once you've added it, find it's object number and use the console to type:

1809.playThread(0,"hammer");

This will play the hammer animation and stop. When it's done, you'll have to stop it by hand.

1809.stopThread(0).

You could schedule this stop with schedule.

The animation name "hammer" is simply appended to the dts file name when Torque searchs for the dsq (animation) file. Given the above information, the dsq file is located in hammerbot_hammer.dsq since the model is in hammerbot.dts.

Read pages 143-190 in the yellow book for reference. Note to self: we need to start working on an understanding of the difference between ShapeData, StaticShapeData, ItemData and PlayerData and how they all interact.

The complete hammerbot I created in class is available as a sample.

A good overview resource

The TDN site has a good FAQ on modeling.

 

Taking my figure model and importing it into TGE

Using code to play model animations.

Assignment

Create your own model. Import it into TGE and get an animation working on it. I'll warn you up front, this is a huge task and most of it requires absolutely no programming.

 

As an exercise

Use milkshape to open the various ms3d files in the starter.fps/data/shapes/ directories.

 

QuArk Notes

This is the best place for downloading a version of quark that will work with Torque.

http://www.garagegames.com/docs/tge/general/ch09s02.php

it also requires Python 1.5. Python 1.5.2 works and can be downloaded on the Python website.

Holodeck QuArk tutorials were written with Torque in mind. A great resource. To use the setInteriorRenderMode(#) function you will have to rebuild the Torque_Demo.exe file using Visual Studio. We can talk about how to do this since this is a great debugging mechanism.

Here is a website with Quark Documentation and Tutorials.

Installation location makes a difference

To get the exporter to work properly (without editing the python code), you have to install QuArk in "C:\QuArk 6.3". If you install it somewhere else, you can still create the "c:\QuArk 6.3\Torque\" directory and copy the contents of your QuArk install torque director to that location. If you just create the directories, it will still look for the dif conversion exe file in "c:\Quark 6.3" directory. Make your life easier, just install it there and work on your game, not on the tools.

Why .dif and .map files?

It would be a whole lot easier to create levels in your favorite 3D tool (Blender, Milkshare, 3dsMax), wouldn't it? You could, but the math involved in collision detection with the actual level would be much more intensive than what is going on with .dif/.map files. The .dif/.map files use something called BSP trees. These BSP trees make the mathematics of display and collision much much easier for the computer and they are used in all current game engines. While the tools aren't the best, you'll just have to deal with it or write your own. This is just another one of those cases where performance has a cost and that cost is added complexity.

Creating .dif files with Max.

For those that use 3dsMax there is a way to use Max to create .map and .dif levels, but it is beyond the scope of this class. GLBuilder for 3dsMax. You'll have to figure it out.

Creating .dif files with Valve Hammer Editor (World Craft)

Valve Hammer Editor is another editor besides QuArk that you can use to create map/dif files. There is also a World Craft discussion on the Torque site. Again, this will not be used in class so you'll have to figure it out on your own or use QuArk. The book uses a beta editor called Constructor from GarageGames, but since it is beta we will not be using it.

Hard to use tools

If you find that these tools are hard to use and find a lot of people complaining about them, think of this as an opportunity. If all the tools are this bad, you would think that there would be an opportunity to sell a better tool to these same people. So, feel free to write your own. As with all software projects, tools always have their odd behavior and take getting used to, but most of the time, once you get through the hard parts they are almost always 'usable'.

Creating Interriors

At the top of theQuArk explorer is a new Torque Map button. Click it and you'll see the four view point editor. You only create maps with simple shapes. They call these brushes, but actually they are just polygons. You create a bunch of polygons and hook they together. For those that have used Unreal editor, it maps all the interriors of the polygons, while Quark/Torque map the outsides of the polygons. One is better for interriors (Unreal), the other is better for structures (Torque). You can still create great interriors, they just take a little extra work.

To make sure you have the tool working, simply save the simple map (call it roomone.map) that is created when you create a new Torque map. Since QuArk saves in Map format, you'll have to save it into Torque .DIF format. Select Torque/Export 220 Map/Build High Def Detail. This is the highest resolution and the standard dif file you should use for your games in this class. There is a way to use LOD and we will discuss it later. You will find a file in c:\quark6.3\torque\tmpQuark\maps\roomone.dir. The texture files (we'll talk about these in a sec) that were save in c:\quark6.3\torque\tmpQuark\textures\concrete.jpg. You can see this is the file by date. You do not need NULL.jpg.

Copy both of those files to the data/interior directory of your game. Run the game, press F11 to get to the world editor. F4 to get to create mode and place the interior called roomone. This will place the interior and you can move it around and relight the scene. Try entering and exiting the room. This is just an exercise to make sure you understand the basics of the work flow. There are 1000 ways you can mess up a map file and make it unusable. Polygons typically need to be lined up perfectly (no gaps) or there will be side effects in the use of those interiors. It's a serious pain, but QuArk is no better or worse than any of the other tools I have used to do this. The use of BSP is a tradeoff between processing speed and complexity. More complexity on the build for faster executuion. You pay for that execution speed with a harder build environment.

That said, always save your work and save copies of your work in a different location than your work area. You should always be able to go backwards when you really screw up a complicated map. Yes, you will ruin a lot of perfectly good maps, just be prepared, it's part of the process to waste some time.

Placing polygons

You can place a polygon on the map by simply clicking on the new polygon buttons.

The tan cube places a cube (what you will us 99.9% of the time). The blue cubes have menus of specialized objects you can place. The one on the left is for general quark polys, the one on the right is for Torque specific objects.

Once you place a polygone, you move it around (and resize) on the map using the three viewports (top, left, front).

Moving around in the viewports.

Moving around in the viewports takes getting used to.

In the 3D view port

Hold down the middle mouse button to rotate left and right and zoom in and out.

Hold down the right mouse button to change your viewing angle

In the side/top/front view ports

Hold down the right mouse button to pan.

Hold down the middle mouse button to zoom.

Polygone tree and properties window

In the lower left is a tree view of the various polygons in your map. This is a tabbed view. The leftmost tab is for the tree view where you can select the polygons from the list. The second tab is for properties of the selected object and the third and fourth tabs are for texturing.

Point of view

When you move around in the 3D view, you will see your eyepoint change in the side,top and front views. The eyepoint looks like this.

Grouping polygons

To create a group of polygons, simply select them and click the new group button. . It's not in the most obvious place so you'll hav eto hunt for it just above the treeview.

Creating Hollow Polygons

It is no doubt you will want to create an interior. You do this by creating a polygon. Make sure it is sized exactly as you want it to be in the final. After turning it into an interrior, a change of size becomes very complicated and you would probably be better rebuilding the polygon. Once you have the poly ready to turn into an interior, select Commands/Make Hollow. This will turn the polygon into 6 separate polygones that do not have gaps. Moving individual polygons at this point will make gapes and render your map file useless. Be careful.

To create passages through your hollow polygons.

Create another polygone and place it over the area you would like to hollow you. Turn the polygon into a negative poly. To do this you select the ploygon, select the properties tab and click the 'neg' radio button.

To change a polygon's texture

Simply select the polygon, change to the blue cube tab and click the new texture button . This will bring up lists of all the available textures. This file will then be output into the tmpTorque/textures directory when you save the .dif file, you will have to remember to make sure this texture file makes it into your interiors directory for your game.

A BUG. Not that I have found that the preview 3D window stops working when I change textures. You will have to change it back to wireframe by right clicking in the window. At some point it will work again, but I have not figured out when or why. A bit annoying, but life in general.

The use of portals

Portals are important elements in building interiors. You should learn to use them. They allow the engine to optimize the drawing. When you are inside a portal (and the interior has no gaps!), the engine will be able to only draw elements that are directly inside that portal area and speed up processing. If you don't use portals, it will still have to consider elements that are no where near being visible when it draws the canvas.

To create a portal, select the add torque object, Brush-based entities, portal. Any polygone created under that group will be a portal. The portal should cover an existing hole (cannot be used with a neg poly) and should overlap by a one or two unit margin. If your map has holes, this will not work correctly.

The use of lights

You will have to place lights in your level or it will be especially dark. Click the add torque object menu and choose light entities/light_omni. Omni directional lights will be your easiest option. If the interior is still dark after loading it into torque, look where the light is, in the room. You may be able to see it, it is just not strong enough. On the light's properties are are falloff1 and falloff2. Fall off 1 is where the light begins to drop off, falloff2 is where it stops shining at all. You will have to use trial and error to get it right. This is a time buster so be prepared.

There are other types of lights we will talk about in class.

 

 
CPSC240 - Games Development
Chapman University
Instructor: W. Wood Harter
(c) copyright 2006-2007 - W. Wood Harter - All Rights Reserved
Screen shots on banner (c) copyright their resprective owners