Thursday, February 19, 2015

Combat And Problems Therein

CombatMover Components
I finally worked my way through a problem that I'd been skirting around.  This had to do with combat, ship statistics, and the links between those two.

Ships exist in the general game structure as Ship objects, which have a Class (which describes size, item loadout, hit points, etc) and a Pattern (describes the actual 3D model used and thus turret and item placement).

Meanwhile, in the combat scene there was an extensive set of prefabs spawned for each combatant.  This was a prefab loaded up at runtime that included the following components:





Component Use
Box ColliderVery tiny, needs to be on root object in order for collision events from colliders on model to bubble up
Rigidbody Sets the physics system mass of the unit
Base Combat MobDerived from BaseCombatObject.  This is pretty much the basic info needed to exist on the board.  BaseCombatMob extends BaseCombatObject, adding thrust/speed/etc. ...Object would be a largely static object
Selectable ObjectHandles clicking or drag selecting of the unit.  Also highlighting on mouseover
Highlightable ObjectFrom 3rd party Highlighting system.  Makes the visual effect work
Orderable Objectllows a unit to receive commands.  Requres BaseCombatMob and SelectableObject.  Without this a unit could do things, but not get external input.  I.e. it could be a mine or a missile or automated turret
Base Combat ExecutiveMain decision maker.  Given a target decides what to do.  Friendly?  Follow and fire on it's target.  Enemy?  Move toward it and attack it, etc.
Area Target Tracker 3rd Party TriggerEventPro item.  Vastly superior replacement for my original target detection code.  Basically detects, sorts and categorizes targets that have...
Targetable 3rd Party TriggerEventPro item. Component lets target trackers know this is (or may be) a valid target for the system
Ignore ModifierTriggerEventPro again.  May hold a list of targets that will be ignored
Base Fire DirectorGets targets from BaseCombatExecutive, or if none chooses available targets.  Direct individual weapon mounts to track individual targets.  Note that I may make a variant of this a Defense Fire Director, that will work only on point defence type weapons and ignore all else

So, that's quite a lot of moving parts. That isn't even counting the code for, say, turrets rotating and tracking their targets.  Or ship movement behavior (to targets, to waypoints, etc.) or local avoidance for ships (i.e. don't run into other ships, or static objects like asteroids etc.)  Oh, and all the visual effects and particle systems for the actual weapons fire.

Anyway.

All of that worked smashingly.  The Combat scene generated all the ships it was told, you could click them and move them around, and they'd target enemies and fire nicely.

However this was really just window dressing.  Sure the turrets tracked the enemy objects and sent out projectiles and laser shots.  But none of it had any actual effect... the actual ship stats were in no way linked with the live objects being spawned (other than ship name, owner and number of ships).

This was where I bogged down for a couple of weeks.  There was a lot needing to be done connecting the dots.  When ships objects were spawned, it needed to look at the linked game-data-ship and get the appropriate info.

The first item I addressed was choosing the right model  I'd just been using a default hard-wired one at first (well, just cubes and capsules at the VERY first).  Now you needed to look up, based on the ship's class and find it's Hull Pattern (for each size of ship, destroyer, heavy cruiser, battleship, etc there may be multiple Hull Patterns, which determine the model and weapon layout.  So two ships might both be battleships, but look and behave slightly differently).  The Hull Pattern has a path to another prefab, this being of the model to load.  The model prefab also includes markers for where weapons and equipment are mounted on the hull.  These can be bones from the 3D modelling program used (i.e. Blender), or empty game objects tacked on later.  I started with the former but have switched to the latter largely.   They both amount to the same, but the latter is easier.

Hull Pattern editor screen.  Ignore the funny gun on the front;
the missile launcher item there simply isn't defined yet.


So now we have the physical model with the orientation and location of item mounts.  Note that orientation is important.  Think of a sea-going battleship.  Some turrets point forward, some aft, some even to the sides.  So like I said, you have to track starting orientation as well as position.

 Now comes a tricky part.  You need to link up SPECIFIC weapons on the ship with SPECIFIC objects on your model. That's to say that specific weapons go on specific turrets, and are limited by the turning arc and speed of that turret.

When a ship class is made (the in-game editor for that isn't done yet, thus far starting ship classes are ginned up by code), each 'module' of equipment the ship gets has a mount point.  I named this 'WeaponMount', but that's a bit of a misnomer as it could be any item, not just a weapon.

The trick to all of this is that in the database (static game data is stored in a in-proc SQLLite database) there's tables HullPattern, WeaponMount and ShipPartResource.  HullPattern is basically just a list of the available HullPatterns, plus stat modifiers, etc.  Oh, and the resource path to load up the model associated with this HullPattern.

WeaponMount entries are keyed to a specific hull pattern.  WeaponMount covers the item's display name, the ModelBoneName (which must match the empty gameObject marking the position on the model), min and max fire arcs, turret turn speed, etc.  It also has a reference to ShipPartResource.

ShipPartResource is basically a name and a resource load path.  It might point to a specific turret, or radar antenna model, etc.

Combat screen. Three on three battle.
So knowing the ship Class you know the HullPattern.  Then you can iterate all the equipment modules on the Class and find the matching WeaponMounts.  Then get the ShipPartResource for that mount and bingo, you can now attach a certain turret model to a certain location of the ship model.  Oh, and then knowing the type of weapon you select a specific visual/audio effects emitter and stick it on the barrel of the turret and link that to the Fire Controller.  The ship's Fire Director component also needs to be informed about every weapon mount created so it can tell it where to target.

There is another component that gets added here, CombatShip.  Basically this is the realtime combat information for the game-data Ship entity. This is where the connection between a specific equipment item in the ship's class and a specific gun turret are made.  Not only physical position, but things like fire rate, accuracy, etc. are calculated here.  For example crew quality affects accuracy.  Weapon quality also affects accuracy, etc.

To it took a lot of work to connect all the dots.  But now ships move around based on their data, and their weapons fire at the correct ranges and rates, doing the correct amount of damage.

It's really pretty to watch.  The movement, weapon effects and sounds are really good so far, I think (note though I have a lot of work in the future on pathing and movement behavior)

Lighting in Unity 5 is very nice.  I'm really liking PBR, even if the texture setup is a bit more work.



Thursday, February 5, 2015

Retrograde

So, no posts in an awfully long time.  I have been working on things, though.

As summer (2014) rolled around, I'd more or less stopped working on Rogue Moon.  I came to the conclusion that 'World of Tanks in Space' was just way, way too much for me to bite off on my own.  It just wasn't ever going to happen, or at least not with any quality.

After a bit of thinking, I decided to take on something far less ambitions.  So I came up with the idea for 'Retrograde', which will be a 4x space empire building type game.





First, why 'Retrograde'?  Well, it is kind of space-y, the opposite of your orbital direction.  Also, it represents kind of a step backwards from an overly ambitious project to something accomplishable.

 My initial idea for the game is to give a space 4x empire builder the same sort of treatment that XCOM:Enemy Unknown gave the... well XCOM genre.  Clean and approachable, simplified down to it's important elements (but no simpler!).  Every decision should be important, and there shouldn't be too many moving parts.  Ideally you'd have no more than 20-30 units

Too many strategy games confuse 'complicated' with 'deep', I think (and sometimes 'frustrating' with 'difficult'.  IMHO this was one of the things that sank Sword of the Stars II, for instance.

I'd like the game to be a bit story driven, and have a lot of character.  Too often games in this genre feel more like spreadsheet/bureaucracy simulators.  I can never spell bureaucracy right the first time.  Or the second, it appears.

Style wise I intend to pull heavily from 30's serials.  Flash Gordon, Buck Rodgers, etc.  I'd like the artwork to have a very stylized Art Deco sort of look (though right now it's very simple and clean, something I'd like to keep, too).

I've been working away quietly on Retrograde, and have made good progress.  The framework of the game has really taken shape, and I'm pretty pleased so far.

Tuesday, September 10, 2013

Server Updates

Temporary Login/Title Screen
So, after having good success moving the client to Unity, there were still some issues.

My primary concern was that there were now two physics engines involved:  BEPU on the server, and an old version of PhysX which Unity was running as the client.

Now, I wasn't certain that this would cause problems, but I'd done some fairly deep dives into the quirks and behaviors of BEPU, so it seemed pretty likely.

It would have been really nice to be able to use the Unity engine from the Zone Server.  If there was some way to simply include it as a DLL and run it, that would have been fantastic.  This is a familiar refrain you see all over the Unity forums, particularly the networking on.

Sadly you can't, of course, and I'm not surprised.  So, one would think that-would-be-that.

Tuesday, August 13, 2013

New Posts Soon

I've just finished moving, and let me say that if I don't do that again in a long time it will be too soon.

Anyway, quite a lot of interesting dev work has happened in the last week or so, but I've been too busy packing and moving to write anything.

Monday, July 15, 2013

Radical Changes

I'm making some radical changes to the development path I'm on.  I've had an increasing number of small but extremely irritating and time consuming problems with the game client.

I've decided that I just don't have enough time to work a full rendering engine as well as the back end servers, database and game administration projects.  So I'm taking a new path with the most troublesome, the game client.

Monday, July 8, 2013

A Tiny Slice of DLL Hell

So I've begun preparing for a little ultra-pre-Alpha.  No real gameplay, just a test of the networking and physics.

In order for this to proceed, though, I naturally had to test to make sure the client would work on other machines.  Also I wanted to run the servers on another machine as well.  Everything worked fine together on my Dev box... but you know how that is.

Saturday, June 1, 2013

Physics Mismatch... of Bepu Physics and Small Rotations

So I've been working on syncing the physics between the client and server implementations.  It's been... a bit painful.

Varying Update Rates Hertz a Lot

One of the first things that tripped me up was the different update rates.  The client is running at the regular 60 fps, i.e. updating every 0.016ms.   The server is running a much more sedate 10 updates per second, or every .1ms.

I had assumed that passing the delta time into the physics simulation would deal with the difference.  I.e. it would know either one sixtieth of a second or one tenth of a second had passed, and simulate that amount of time passing:

physicsSpace.Update(elapsedMilliseconds);

First, this was WRONG, and a very common mistake!  Turns out Bepu wants the time in seconds, so really I'm telling it to simulate x1000 speed!  This is correct:

physicsSpace.Update(elapsedMilliseconds/1000.0f);

Why didn't I notice this right away?  Well, it turns out Bepu has a default timestep for its simulations, which happens to default to 1/60th of a second.  And it will only perform a certain amount (default three) physics steps per update.  So by default Bepu will simulate .005 seconds of time (3x0.016), maximum, per Update call.  Mind you, these are quite sensible numbers for defaults.

So really when I was passing in weird values like 16 (16 milliseconds, 1 frame at 60fps) I was actually asking it for SIXTEEN seconds.  In return it gave me 0.05 seconds, which is near enough to .16 that by the naked eye I couldn't tell the difference.  On the server it was getting 100 (100 milliseconds, the update period at 10 fps), so it though I was asking for a bit over a minute and a half...

So, problem number one was I was passing milliseconds rather than seconds.  The defaults were ok for the client, but clearly the server running at 10hz would still be wrong.  Each update would need to simulate 100 milliseconds of time, but by default Bepu would only step 50 milliseconds.

Easily fixed by a bit of reading through the docs and forums, however.  You can change the maximum number of timesteps simulated:

physicsSpace.MaximumTimeStepsPerFrame

... though I did not change this.  Really you want a one-to-one normal ratio, with the ability to simulate extras if for some reason the simulation falls behind.

More importantly, you can change the expected simulation step temporal duration:

physicsSpace.TimeStepSettings.TimeStepDuration = timeStepDuration

where physicsSpace is the Bepu object, and timeStepDuration is set in the simulation engine initialization call.  So the client and server both pass in their expected update tempo, and the physics engine deals with it.

Velocity Mismatches

That fixed some of the differences I was seeing between the client and server, but there was still one bothersome one.

For small accelleration values (angular or linear) despite the fact that I had set the entity's AngularDamping and LinearDamping values to zero (this is a space simulation after all) the AngularMomentum and LinearMomentum values were still damping down to zero over a short time... but only if they were very small.

I was deeply confused and verified again and again that the damping was zero, and kept looking for where I had gone wrong.  This was throwing off the client and server sync, ans the client seemed to damp down quite a bit quicker (which seemed like a useful clue, as I knew it ran more, but shorter, simulation steps.  So it had to be some sort of per-step, rather than per-time-unit effect).

I finally found the answer. On the Bepu forums, someone poses basically the same question, and Norbo answers:
That's caused by the deactivation system. It will attempt to stabilize and disable very slow objects. You can prevent it from freezing entirely by setting the object's IsAlwaysActive property to true. However, stabilization will still be applied. You can disable stabilization separately by setting the Space.DeactivationManager.UseStabilization to false.
Stabilization is nice to have on in the general case, though, particularly if you have complicated interactions like large stacks or constraint systems.
You can also tune the Space.DeactivationManager.LowVelocityTimeMinimum and Space.DeactivationManager.VelocityLowerLimit. Stabilization only occurs under the velocity limit, and deactivation will not occur until an object is below the velocity limit for the time minimum.
By default the velocity lower limit is .026f.  Thus I added:

physicsSpace.DeactivationManager.VelocityLowerLimit = .001f;

Well, that solved a lot of problems.