Procedural object placement
by Bryan Morgan · in Technical Issues · 08/06/2009 (12:28 pm) · 18 replies
I've been looking through the documentation and TDN, but I can't seem to find the answer to my problem. I want to be able to place a number of TSStatic objects in the level procedurally using TorqueScript as well as a player spawn point which will then be used to spawn from. I didn't see any TorqueScript functions in the function list which looked like it would obviously do this, is it possible without exposing a new function from the engine? Is there any simple way to place an object relative to another object, such as placing a TSStatic 5 units on the X axis from the player regardless of where they are?
#2
08/06/2009 (2:54 pm)
Aha, thank you very much for the examples. I found the plastic gems shortly after posting this and it sort of gave me an idea of how to do it and it seems I was on the right track, but I'm assuming setting the parentGroup to MissionGroup is necessary for it to be rendered in the game world, which I was not doing. Is this correct?
#3
08/06/2009 (3:12 pm)
No, you don't have to place it in the MissionGroup. That just allows you to see it in the World Editor Inspector Tree.
#4
The error I'm getting is specifically this after I've added the code to onMissionLoaded:
The exact position of the error is confusing because if I drop the bracket after TSStatic() down a few lines the ## ## is on the line right under TSStatic() so it's definitely not the shapeName line that it's pointing to in that block.
08/06/2009 (3:20 pm)
I can't seem to get the second example to work. The erroring line appears to be "%obj = new TSStatic(){". It's rather odd as the first example works fine so it has to be something to do with the %obj = bit. Could TSStatic possibly not be returning a handle to %obj?The error I'm getting is specifically this after I've added the code to onMissionLoaded:
>>> Some error context, with ## on sides of error halt:
%end = getWord(%start,0) SPC getWord(%start,1) SPC -5000;
// Cast ray between start and end to find intersection with terrain
%hit = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType)
// Create static object
%obj = new TSStatic(){
## ## shapeName = "art/shapes/items/healthkit.dts";
parentGroup = MissionGroup;
};
if (isObject(MissionCleanup))
MissionCleanup.add(%obj);
// Get the bounds of the object
%size = %obj.getObjectBox();
// Calculate the offset to set the object directly on the terrain
>>> Error report complete.The exact position of the error is confusing because if I drop the bracket after TSStatic() down a few lines the ## ## is on the line right under TSStatic() so it's definitely not the shapeName line that it's pointing to in that block.
#5
there's a missing semicolon on the end of the previous line
(fixed in the original post)
08/06/2009 (3:38 pm)
yeah, sorry...there's a missing semicolon on the end of the previous line
(fixed in the original post)
#6
08/06/2009 (3:40 pm)
Aha, silly me. I didn't even look at that line because the error was'nt being very specific about where it was coming from.
#7
08/06/2009 (3:57 pm)
FYI, there's another error in the second script... I just wrote that off the top of my head. But when testing it just now, the object didn't move to the proper spot. That's because I forgot that the containerRayCast() function returns the ID of the object it hit as the first parameter, in this case the terrain. I updated the first post again, so you need to change line 28 to match if you want to test this.
#8
08/06/2009 (4:07 pm)
Thanks for that. I can't get it to actually spawn relative to the player anyways since I can't find the player object or even the client object within the function I'm wanting to use it it. I need to write all the scripts from scratch since it's the physX demo and a real mess anyways.
#9
%player = ServerConnection.getControlObject();
08/06/2009 (5:36 pm)
You can grab the player (or the camera if you're in free fly mode) like this:%player = ServerConnection.getControlObject();
#10
EDIT: That still doesn't work where I was wanting to use it. I get the error "Unable to find object: 'ServerConnection' attempting to call function 'getControlObject'" even though Torsion finds it in it's intellisense database.
08/06/2009 (7:02 pm)
Nice, thanks! I don't actually need this for what I'm doing, but it could come in handy and it's a good thing to know.EDIT: That still doesn't work where I was wanting to use it. I get the error "Unable to find object: 'ServerConnection' attempting to call function 'getControlObject'" even though Torsion finds it in it's intellisense database.
#11
08/06/2009 (9:39 pm)
Try LocalClientConnection?
#12
ServerConnection exists on every client that is connected to a server, whether single- or mult-player. LocalClientConnection ONLY exists on a server that has a local client. It will not exist on a dedicated server. All your client connections exist in the ClientGroup simgroup. You can access them on the server like this:
08/07/2009 (7:03 am)
Ok, are you doing multi-player stuff? If so, then your script would definitely need to be server-side, and ServerConnection only exists on the client. In that case you would be doing something like: commandToServer('createObject'); // Tell server to create object
serverCmdcreateObject(%client)
{
%pos = %client.getControlObject().getPosition();
/// Put the rest of the object placement code here
}ServerConnection exists on every client that is connected to a server, whether single- or mult-player. LocalClientConnection ONLY exists on a server that has a local client. It will not exist on a dedicated server. All your client connections exist in the ClientGroup simgroup. You can access them on the server like this:
for(%i = 0; %i < ClientGroup.getCount(); %i++)
{
%client = ClientGroup.getObject(%i);
// Do something with %client
}
#13
08/07/2009 (7:06 am)
That's a much better idea Ryan. But I recommend using %client.player instead, so that the wrong position won't be used in the fly camera mode.
#14
So from your example, if the client issues the commandToServer('createObject') then that client will be the one affected when serverCmdcreateObject(%client) is run on the server, and then when soemthing needs to affect all clients you run through the ClientGroup to get each client object before doing whatever is needed. Is that correct?
It's somewhat annoying that I've actually been using Torquescript off and on since TGE 1.3 and yet I keep forgetting everything I teach myself. Though I never did take the time to really learn half of this stuff.
08/07/2009 (10:42 am)
Interesting. I don't particularly need to write for multi-player for what I'm doing, but it does explain why I wasn't able to get the player or client information. I would like to keep my scripts capable of running in multiplayer, but that may become too complicated. I've not been able to find a whole lot about writing scripts for the server/client other then looking through the scripts included with the example setups.So from your example, if the client issues the commandToServer('createObject') then that client will be the one affected when serverCmdcreateObject(%client) is run on the server, and then when soemthing needs to affect all clients you run through the ClientGroup to get each client object before doing whatever is needed. Is that correct?
It's somewhat annoying that I've actually been using Torquescript off and on since TGE 1.3 and yet I keep forgetting everything I teach myself. Though I never did take the time to really learn half of this stuff.
#15
To make your game work in both single- and multi-player modes, you would really need to understand the server/client relationship and write your scripts in a multi-player compliant fashion. That is not trivial. I'd suggest getting a book if you plan to go down that road.
The server holds the entire world state and tells each client about the objects it can see based upon where the client is located in the world. So if you executed commandToServer('createObject') from YOUR client, the server would create an object 5 units away from YOUR player, but all the other players would see it too, because it has been added to the "world." So in this case, it ultimately doesn't just affect your client.
Example usage:
Lets say you have a trigger, and whenever someone steps into it all the other players are pushed away from him. The client that hit the trigger would send a commandToServer('PushEveryone'). The server would then loop through the ClientGroup, calculate the vector between the position of the client who sent the 'PushEveryone' command and the other clients in ClientGroup, scale that vector based upon how far away they are, and apply the impulse to everyone except the client who sent the command.
08/07/2009 (1:06 pm)
Well, the reason that I asked if you were doing multi-player is because you said you were getting an "Unable to find ServerConnection" error. To me, that implied that the script was either running on a dedicated server, or you had modified the default .cs files where the GameConnection is created and named it something other than ServerConnection. If neither is true, then you should have both a ServerConnection and a LocalClientConnection object while inside the game.To make your game work in both single- and multi-player modes, you would really need to understand the server/client relationship and write your scripts in a multi-player compliant fashion. That is not trivial. I'd suggest getting a book if you plan to go down that road.
The server holds the entire world state and tells each client about the objects it can see based upon where the client is located in the world. So if you executed commandToServer('createObject') from YOUR client, the server would create an object 5 units away from YOUR player, but all the other players would see it too, because it has been added to the "world." So in this case, it ultimately doesn't just affect your client.
Example usage:
Lets say you have a trigger, and whenever someone steps into it all the other players are pushed away from him. The client that hit the trigger would send a commandToServer('PushEveryone'). The server would then loop through the ClientGroup, calculate the vector between the position of the client who sent the 'PushEveryone' command and the other clients in ClientGroup, scale that vector based upon how far away they are, and apply the impulse to everyone except the client who sent the command.
#16
08/07/2009 (1:40 pm)
I've not modified the scripts involving the GameConnection at all yet, but ServerConnection doesn't exist, and I'm unsure if LocalClientConnection exists either. Would the book "Multiplayer Gaming and Engine Coding" explain in detail everything I would need?
#17
08/07/2009 (1:43 pm)
Yes, I have that book, and it is very helpful.
#18
08/07/2009 (1:53 pm)
LocalClientConnection does exist, but use serverCmds whenever possible. I only use it to debug, e.g. LocalClientConnection.player.setRepairRate(1000);
Torque 3D Owner Ryan Mounts
%pos = myPlayer.getPosition(); %pos = getWord(%pos,0) + 5 SPC getWord(%pos,1) SPC getWord(%pos,2); new TSStatic() { shapeName = "some path"; position = %pos; parentGroup = MissionGroup; };// Get player position %pos = myPlayer.getPosition(); // Calculate raycast start (+5 units in x-direction from player // and very high above terrain) %start = getWord(%pos,0) + 5 SPC getWord(%pos,1) SPC 5000; // Calclate raycast end (below start point, far below terrain) %end = getWord(%start,0) SPC getWord(%start,1) SPC -5000; // Cast ray between start and end to find intersection with terrain %hit = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType); // Create static object %obj = new TSStatic() { shapeName = "some path"; parentGroup = MissionGroup; }; // Get the bounds of the object %size = %obj.getObjectBox(); // Calculate the offset to set the object directly on the terrain %offset = -getWord(%size, 2); // Move the object to hit point and shift it up by offset %obj.setTransform(getWord(%hit,1) SPC getWord(%hit,2) SPC getWord(%hit,3) + %offset); // May not be necessary... in T3D this forces a visual update %obj.setScale("1 1 1");