Game Development Community

RTSProjectile won't render

by Justin Tolchin · in RTS Starter Kit · 07/15/2005 (4:39 pm) · 20 replies

Hi all,

Before I ask my question, I want to mention that we're using TSE with the RTS code/scripts ported to it.

I tried creating an RTSProjectile and setting the various fields (velocity, target, etc.) and it gets created correctly but it doesn't render. I'm sure it's being created correctly because I can see a "dot" for it moving along it's trajectory when I press F11 to open the mission editor and it is at the correct position above the terrain. I'm using a .dts that we created here for the projectile, and it shows up fine in the Show Tool.

Since RTSProjectile inherits from Projectile, I put a breakpoint in Projectile::prepRenderImage and Projectile::renderObject but neither one gets called. Why wouldn't it? Does it have anything to do with the fact that RTSProjectile has it's Parent field set as "GameBase" instead of "Projectile"? I wouldn't think so since it should still call the actual C++ parent class methods for rendering, right? Anyway, I tried adding a prepRenderImage method directly to the RTSProjectile class but it never gets called either.

BTW, there was a "prepModelView" method in Projectile but it is commented out in TSE. I don't see this being called anywhere anyway so I don't know if it matters.

Any ideas?

Thanks!

#1
07/15/2005 (5:00 pm)
You have to manually call the parent methods, if you want the code in the parent method to run.
#2
07/15/2005 (5:06 pm)
Not sure I follow you. You don't call ::prepRenderImage yourself. It gets called from SceneGraph::buildSceneTree for each object in the scene. For some reason the RTSProjectile doesn't seem to be included in that and I don't know why.
#3
07/27/2005 (12:08 pm)
Still having this problem, if anyone has any ideas. I realized one thing I was doing wrong was that I was creating the RTSProjectile object in my client scripts, and I guess I should have been doing it in the server scripts. But now that I moved the code up to the server I'm still seeing the same behavior.

But now I'm realizing the underlying problem is that the projectile never gets ghosted (RTSProjectile::packUpdate never gets called). What controls this? RTSProjectile.cc has this line:

IMPLEMENT_CO_NETOBJECT_V1(RTSProjectile);

Isn't that enough to get the object to be sent over the network when you create one?

Anyway, here's the datablock and the code I'm using to create the projectile on the server. Is there something I'm missing?

datablock ProjectileData(AT4Projectile)
{
	projectileShapeName = "~/data/shapes/weapons/Rocket_m138/rocket.dts";
	directDamage        = 40;
	radiusDamage        = 60;
	damageRadius        = 1.5;
	explosion           = 0;
	particleEmitter     = 0;

	muzzleVelocity      = 10;
	velInheritFactor    = 0.3;

	armingDelay         = 10000;
	lifetime            = 50000;
	fadeDelay           = 5000;
	bounceElasticity    = 0;
	bounceFriction      = 0;
	isBallistic         = false;
	gravityMod = 0.80;

	hasLight    = false;
	lightRadius = 4;
	lightColor  = "0.5 0.5 0.25";
};


	%projDB = "AT4Projectile";
	
	if(isObject(%projDB))
	{
		%p = new RTSProjectile() 
		{
			dataBlock        = %projDB;
			initialVelocity  = 0;
			targetPos        = VectorAdd(%defender.getPosition(), "0 0 2");
			speed            = 0.1;
			initialPosition  = VectorAdd(%attacker.getPosition(), "0 0 2");
			sourceObject     = %attacker;
		};
		MissionCleanup.add(%p);
	}

I realize I should give the projectile a real velocity vector at some point, but right now I just want to make sure it's rendering so I don't want it to move.

I can see the object get created on the server and the red "dot" for it shows up in the mission editor, but it doesn't render because it never gets sent over the wire to the client. If I just use the mission editor to add a "static shape" image of the projectile it renders fine so I know the .dts is ok.

Any ideas on how to track this down?
#4
07/27/2005 (12:42 pm)
Actually I should have said that Projectile::packUpdate wasn't being called, not RTSProjectile. But I just noticed that RTSProjectile::packUpdate is defined to just "return 0;" so that would certainly explain why it doesn't get ghosted. :-P But when I changed it to "return Projectile::packUpdate(...);" it still never gets called. I'm really confused now. Isn't RTSProjectile supposed to be a ghosted object?
#5
07/27/2005 (1:54 pm)
Actually, packUpdate returns 0 to indicate that there is no data that needs to be repacked. The return value from packUpdate is used to indicate any flags that need to be retransmitted.

RTSProjectile is not meant to be ghosted; it's a client side only object.
#6
07/27/2005 (2:00 pm)
Hmmm... well that's kind of what I thought at first, but when I had the "new RTSProjectile()" call in the client scripts and it wasn't showing up, I started tracing through the call to RTSProjectile::onAdd. When it got into the "addToScene" call there's a "if(isClientObject())" check which failed, so it called "gServerContainer.addObject(this);" and "gServerSceneGraph->addObjectToScene(this);" instead of the client versions of those calls. That's why I moved it over to the server scripts. So why would it fail that check if I instantiated it on the client?
#7
08/08/2005 (12:16 pm)
Well, I'm still stumped on this. What determines whether an object can exist client-side only or not? I have a client-side-only class called "CSItem" (that I must have got from one of the GG resources) which is working quite well in TSE, but the RTSProjectile class doesn't do any of the weird things that CSItem does which I assumed were designed to make it work client-side only.

For example, in the CSItem constructor it sets/clears the following flags:

mNetFlags.clear(Ghostable | ScopeAlways);
mNetFlags.set(IsGhost);

But RTSProjectile doesn't have those lines, and when I try adding them the mDatablock field never gets sets.

To use the CSItem class, I create objects this way:

mBuildBox = new CSItem();
ItemData* data = new ItemData();
data->setField("shapeFile", "<mypath>/buildbox.dts");
mBuildBox->onNewDataBlock(data);

if (!mBuildBox->registerObject())
{
...
}

but again, the RTSProjectile class doesn't seem to require anything like that. So how can I get it to work in TSE? It's acting like it's purely a server-side object so it never renders it.
#8
08/08/2005 (1:52 pm)
The problem is that you need to research the general nature of client side only objects. For example, the very definition of a client side only object is that it has no networking whatsoever, so when you try to set any mNetFlags, you are missing the entire purpose of a client side only object--it is never networked, and never exists on the server (although it's creation/utilization is normally initiated by something that has happened on the server).

Good examples of client side only objects would be the actual things that are created client side when an fxXXXReplicator object is used (the replication object itself is server side, and propagated to the clients, but the results of the replicator object are client side only), as well as the RTSProjectile itself--it is created (client side only) as the result of a ranged attack occuring on the server, but only the NetEvent of the ranged attack occuring is propagated...the rendered object itself is client side only.

I would suggest going back to the stock RTS-SK and tracing the actions related to how an RTSProjectile ultimately gets utilized, and what happens on the server and the client, and then use these techniques in accomplishing what you want within TSE.

In general, from your description, you are trying to use a "client side only" object on the server, and that's not the way it works. You need something to happen on the server, and then have the server tell each appropriate client to create and use a client side only object based on that information. Instead, your description implies that you are creating the object server side and expecting it to be propagated to the clients...which isn't how RTSProjectile was designed.
#9
08/08/2005 (3:22 pm)
Hi Stephen. Originally I was trying to create it in the *client* script, but as I described above, when it wasn't rendering I started to trace through the onAdd call and when it got to the "addToScene" method, the "isClientObject" test failed. That's what I didn't understand (and I still don't). So I temporarily tried creating the projectile from the server scripts, which didn't work either, and I am long-since back to creating it in the client scripts. Sorry I didn't make that clear before.

Also, I'm puzzled by what you said about setting mNetFlags. I just noticed that the RTSBuildingMarker object (which is client-side only) has this in the constructor, much like the CSItem class I described above:

// Magic to make this a client-side only object.
mNetFlags.clear(Ghostable);  //NOT ghostable - don't want this networked
mNetFlags.set(IsGhost);      //however, it IS a client-side object

while the RTSProjectile code does not have any similar lines (at least not in my version and I don't think I ever touched it).

So that code must do *something* related to making the object exist purely as a client-side object, right? But if that's the case then why doesn't the RTSProjectile code have it? Like I said, I tried adding those lines but then ::onAdd always results in a NULL datablock.

I also noticed that the RTSBuildingMarker class uses the IMPLEMENT_CONOBJECT macro, and the RTSProjectile and CSItem classes use the IMPLEMENT_CO_NETOBJECT_V1 macro. Does that affect this at all? I tried switching the RTSProjectile class macro but it didn't seem to have any effect.

To be honest, I never tried using the RTSProjectile stuff in the RTS kit; it's only been since we started porting to TSE that it even came up. I'm just assuming that it did work properly in the RTS engine.
#10
08/08/2005 (3:53 pm)
To be honest, the use of mNetFlags in the RTSBuildingMarker is redundant...the object itself is never actually networked at all. I'm not sure why that code was left in.

While it may sound frustrating at first, my suggestion would be to go back as I mentioned above, and trace how the entire RTSProjectile path is executed, and then re-port the appropriate portions of the RTS-SK to TSE with that in mind, as well as the additional information you've gained from this (and other) thread. I think that most probably your code is in a very mixed up state right now as you've experiemented with various things, and starting this task from the beginning again maybe your best option.
#11
08/08/2005 (4:01 pm)
Well I can try doing that, but (other than the rendering methods) should anything about RTSProjectile *need* modification in TSE?
#12
08/08/2005 (4:07 pm)
Just FYI - there's no distinction between client and server script, except for when it is loaded and what stuff it calls. Calling a new Class() from one place in script or another won't make it magically become client or server side.

RTSBuildingMarker is just a dummy object that sets some flags so that it gets rendered as a client object, which is most easily accomplished by marking it as a ghost, since we want to reuse existing code from parent classes.

RTSProjectile is different because it was also written by a different coder, and does not inherit any functionality from parents (except for always-done stuff like getting a datablock or rendering). So it doesn't NEED to set flags because it's not tricking any code into behaving in certain ways.

RTSProjectile has a NETOBJECT macro because it inherits from a class with one and it seemed safer to implement it with commonality where possible. RTSBuildingMarker does not use that macro because it didn't.

Anyway... No idea why it's not working on TSE. You're kind of on your own as we don't officially support the RTS Kit on TSE, anyway. I would suggest seeing how things behave in the TGE version, and localizing the difference - ie, find out what's behaving differently between the two. Maybe some other change in your game (or in TSE proper) is breaking an assumption on the part of the RTS SK code.
#13
08/08/2005 (4:18 pm)
Hmm... ok. I guess that leaves me with 2 main questions:

1) since the SceneObject::addToScene(...) method thinks the RTSProjectile is a server object even though I'm creating it from client script, it's getting added to the server-side scene graph and not the client-side scene graph. Is that in fact why it's not being rendered? (seems reasonable but I want to make sure I understand this)

2) The reason the projectile is being added to the server-side scene graph is because the "isClientObject" test is failing, and that test is based on calling "mNetFlags.test(IsGhost);". So what controls whether or not that net flag is set, if I don't set it in the constructor (as you're telling me I don't need to)?
#14
08/08/2005 (7:01 pm)
1) That would explain that.

2) Can't say - in the RTS SK this all works just fine, so there might be some factor (in a parent class for instance) that enables this work in TGE but not TSE.
#15
08/09/2005 (9:36 am)
Ok, thanks Ben/Stephen.

Guess I got some digging to do now. :-(
#16
08/11/2005 (10:57 am)
Ok, so I went back to RTS kit and guess what? I see the same thing there! That is to say, the particle emitter attached to the projectile works and the "glow" around the projectile works, but the actual projectile itself never gets drawn. Since I wasn't even using a particle or glow effect in my TSE version, I wasn't seeing anything at all.

The RTSProjectile in the RTS kit "starter.RTS" example is using the crossbow bolt "projectile.dts" and that *is* supposed to be drawn, right? But I see the same thing: in the addToScene call it gets added to the server scene graph but not the client, and none of the Projectile class rendering methods ever get called. Here is the screenshot showing this:

www.tolchin.net/GG/projectile1.jpg
Anyway I was hoping one of you could verify this behavior for me. Do you see the actual crossbow bolt rendered?

Now it's definitely possible that I modified something in the RTS kit code. I'm sure I didn't touch the projectile stuff but I could have changed something else higher up in the class hierarchy. But I don't have access to a stock version at the moment and I'd rather not go through the hassle of downloading it again if one of you wouldn't mind testing this out for me really quick. :-)

Thanks!
#17
08/15/2005 (10:51 am)
Could be it's getting LODed out?
#18
08/18/2005 (11:31 am)
Don't think so, although I don't know anything about how the LOD system works so I don't know how to verify it. But I tried substituting a much larger object that definitely renders at that distance and it doesn't show up either. The server knows it's in the scene but not the client.
#19
08/18/2005 (11:49 am)
It does sound like you've spotted a bug...since projectiles themselves are not networked, adding them to the server scene doesn't do anything at all (well, in hosted multiplayer the host will see it possibly, but that's unconfirmed).

You may want to play around with adding it to each client scenegraph as part of the projectile creation, and see what you get.
#20
03/25/2010 (12:17 pm)
So the conclusion here ... was the stock RTS Kit has a "bug" which prevents the projectile DTS from being visible.

Yet the lighting effect is seen as it moves ... and it can't be set to ScopeAlways because its client side only (and doing so causes a crash due to invalid packet recieved)

And you cant use regular Projectiles, because RTSCamera ignores them ... so they will also never been sent to client or be visible.

There was once a mention of stock projectile DTS not being defined properly in the kit ... but no further details.

So basically, just another broken feature ? Like vismanager ?