Sunday, April 28, 2013

Temporary 'Hangar' Screen

In working towards something vaguely playable, I've fleshed out the screen you get after logging in, the 'Hangar' screen.  Here you'll pick a ship type, then you'll be able to move on to actual gameplay.

When a client connects to the Hangar 'zone', the server gives it the entity list.  For now, there are three items (well, one is a group):  a destroyer, a corvette and a fighter squadron.  These are all synced from the server, so this list can easily change without any changes to the client.

The client can click on any of the three choices, which selects and highlights it, as well as swinging the camera smoothly over to the selected option.

This was not without problem, though.  I had two major issues moving forward into this.  First, custom textures weren't yet synced over the network.  As you can see above, the fighters and the destroyer are fine. However the corvette model is messed up (the UVs appear to be bad), so instead of it's actual texture I'm just supplying a default metal texture, so at least it looks solid.  This is done in the Lua script that the Hangar zone initializes with:

argsDict:Add(EntityArgNames.Position, Vector3(0, 0, 100));
argsDict:Add(EntityArgNames.Rotation, Quaternion.CreateFromYawPitchRoll(MathHelper.ToRadians(-90), 0, 0));
argsDict:Add(EntityArgNames.LinearVelocity, Vector3(0, 0, 0));
argsDict:Add(EntityArgNames.AngularVelocity, Vector3(0, 0.0, 0));
argsDict:Add(EntityArgNames.Mass, 1000);
argsDict:Add(EntityArgNames.EngineTrail, true);
argsDict:Add(EntityArgNames.RenderObject, "Models/Ships/Corv_Hull");
argsDict:Add(EntityArgNames.CustomTexture, "Textures/MetalBase0121_9_S");
argsDict:Add(EntityArgNames.MovementType, MovementType.SpaceNewtonian)
argsDict:Add(EntityArgNames.Scale, Vector3(1, 1, 1));
argsDict:Add(EntityArgNames.Name, "Corvette");
local corvette = SimEngine:CreateGameEntity(argsDict);

(It is the 'CustomTexture' entry).  Previously I'd actually been passing the Texture2D object there, i.e. the actual texture bitmap.  Now of course we don't want to send that across the wire, that would be ruinously expensive in time and bandwith in... well even the short run.  So a quick bit of work changed that to the texture file asset path (this all also applies to custom specular, normal and emissive).  The path is synchronized over the network with the rest of the Spatial attributes, so on the client side it simply uses the asset path and loads the texture.  This went pretty easily.

The other problem was with the fighters.  What you really can't see from the pictures is that the rear four fighters are locked to the lead fighter.  Thus wherever the lead fighter goes, the rest remain in formation.  This is the 'AttachTo' component at work; an entity can be given another entity and a type of attachment:

public enum AttachmentBehavior
     LockWeld,   //Absolute lock relative to parent.
     LockTurret, //As absolute, but entity can be rotated, i.e. a turret.
     LockOrbital, //As absolute, but entity can orbit about lock point.
     LockRotateableOrbital //Combo of rotateable and orbital.

... and the physics engine does the rest.


It blew up pretty well the first time as I really expected it would, complaining of null attached entities.  The problem of course being that the entities are sent from the server in no real order.  I.e. there is no guarantee that the entity you want to attach to exists... the child-entity may have been sent first, so the entity it wants to connect to doesn't exist yet.

I thought about this for a while and settled on a kind of deferred solution.  The AttachTo component gets AttachmentDeferred = true, and no further work is done on it.

Meanwhile, each update the AttachedMovementSystem examines and (maybe) processes entities with attachments.  As it does it checks to see if a given entity has AttachmentDeferred=true.  If so it looks through the entity list for an entity with the requested masterId... and if so it initiates the coupling.  All good.

The only problem is that during the unknown period of time between the entity's creation and attachment the entity will likely move quite differently than it should.  This will definitely introduce a discrepancy between simulation engines (client, server, other clients).  I figure that the object will be brought into line fairly quickly by the server, so the discrepancy should be small, though with fast moving objects, who knows?

The other option I thought of was temporarily disabling the object completely until the requested attachment target shows up.  I may go back and do this.  One other thing I can definitely do to minimize this is sort the list of entities to be sent to the client by which-is-attached-to-which, and send the root nodes first.  I wouldn't treat that as an ironclad guarantee (nothing would prevent the root entity from being sent, deleted by some effect, then the child arrives and <boom>, for instance), but it would minimize it.  Dunno, may do this too.  I won't bother until I know it is a problem, in the spirit of avoiding premature optimization.

No comments:

Post a Comment