Adding custom animations to NeoAxis that work over network 



This tutorial shows how to get custom animations working in NeoAxis and most of all how to do it over client-server network.

You can easily adapt this to send any custom commands over network.

I did this tutorial in one hour plus compiled and tested with dedicated server and two clients. Didn't take any screen shots, but there is hardly anything here that requires them.

I hope you can get the basic idea behind all this even if this tutorial is just code slices plus some text.

The thing to understand here is that the client only sends information and it is the server that makes all decisions.

Let's say in this scenario I want to make my player character sit down/crouch when ever I press key 'C'.

First thing that comes to mind is that I need to add the 'C' to the GameControlKeys.cs so NA can hook to it.

I just added as the last key definition in that class


...
...
...
Weapon7,
[DefaultKeyboardMouseValue( EKeys.D8 )]
Weapon8,
[DefaultKeyboardMouseValue( EKeys.D9 )]
Weapon9,
[DefaultKeyboardMouseValue(EKeys.C)]
Crouch,
}
}



When I press 'C' key on the client the event is being captured by


PlayerIntellect.GameControlsManager_GameControlsEvent( GameControlsEventData e )


There is a check to see if this is in fact a client


if( EntitySystemWorld.Instance.IsClientOnly() )


and after that there is a call to function


Client_SendControlKeyPressToServer( evt.ControlKey, evt.Strength );


On the server side the keypress is being received by


PlayerIntellect.Server_ReceiveControlKeyPress( RemoteEntityWorld sender, ReceiveDataReader reader )


which then calls


Intellect.ControlKeyPress( controlKey, strength );


and inside that function it calls


controlledObject.DoIntellectCommand( new Command( controlKey, true ) );


which is inside Unit.cs


public void DoIntellectCommand( Intellect.Command command )
{
OnIntellectCommand( command );
}


which then finally translates into


Character.OnIntellectCommand( Intellect.Command command )



OnIntellectCommand() then decides what to do and is the action actually legal. In my case I want it to work on single player for testing purposes and

if this instance is a server it also informs all clients to do the crouching.


So this is my modified Character.OnIntellectCommand()


protected override void OnIntellectCommand( Intellect.Command command )
{
base.OnIntellectCommand( command );

if( EntitySystemWorld.Instance.IsServer() || EntitySystemWorld.Instance.IsSingle() )
{
if( command.KeyPressed )
{
if( command.Key == GameControlKeys.Jump )
{
//TPS arcade specific (camera observe)
//No jump
if( GameMap.Instance.GameType == GameMap.GameTypes.TPSArcade )
{
if( Intellect != null && PlayerIntellect.Instance == Intellect )
return;
}

TryJump();
}
else if (command.Key == GameControlKeys.Crouch)
{
TryCrouch();
}

}
}
}



Character.cs needs new methods TryCrouch() and Crouch() which are below


public void TryCrouch()
{
//cannot be called on client
//return and crouch when server tells us to do it
//with a network message

if (EntitySystemWorld.Instance.IsClientOnly())
{
Log.Fatal("Character: TryCrouch: EntitySystemWorld.Instance.IsClientOnly().");
return;
}


if (EntitySystemWorld.Instance.IsServer())
Server_SendCrouchEventToAllClients();
}

public void Crouch()
{
//this method is called either by the server in TryCrouch() or by the client in void Client_ReceiveCrouchEvent()
mainBody.LinearVelocity = Vec3.Zero;
SetForceAnimation("crouch", false);

}



Control keys all already sent from client to server, so all we gotta do is create the Server_SendCrouchEventToAllClients() in Character.cs


void Server_SendCrouchEventToAllClients()
{
SendDataWriter writer = BeginNetworkMessage(typeof(Character),
(ushort)NetworkMessages.CrouchEventToClient);
EndNetworkMessage();
}



Remember to add NetworkMessages.CrouchEventToClient to the list in Character.cs


///////////////////////////////////////////

enum NetworkMessages
{
JumpEventToClient,
CrouchEventToClient,
GroundRelativeVelocityToClient,
}

///////////////////////////////////////////



then we also need to add the code that receives Crouch-event on client when server sends it. This is also in Character.cs


[NetworkReceive(NetworkDirections.ToClient, (ushort)NetworkMessages.CrouchEventToClient)]
void Client_ReceiveCrouchEvent(RemoteEntityWorld sender, ReceiveDataReader reader)
{
if (!reader.Complete())
return;
Crouch();
}



Now all that comes to mind is to make sure my playercharacter mesh has animation called "crouch".

All this in short is:

1. Client sends KeyEvent
2. Server receives KeyEvent
3. Server decides action is ok and plays animation on server object + sends command to play animation on all clients
4. All clients including the one that sent the KeyEvent play animation on their copy of the server object

It compiled without errors and tested with dedicated server plus 2 clients. Might not be perfect, but the animation plays on the clients anyway :)

-Wellu