UE2 - UT2kX Bone Rotating (client side)

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
Hello, how can i get bone rotation on client?
I attempt to do SetBoneRotation, but it doesn't work on client, for example on non-dedicated server or single game bone is rotating well.
Engine: UE2 (Postal2 edition)
 
Last edited:

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
p.s: script is very simple
Code:
class mgunweapon extends weapon;
const ROT_BONE = 'barrel';
simulated function DoRotate() {
local rotator rotme;
rotme.roll=32768;
SetBoneRotation('ROT_BONE', rotme, 0, 1);
}
function ProjectileFire() {
DoRotate();
}
for example it fully works on ut2004's clients
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
I'm pretty sure the third parameter of SetBoneRotation() should be set to 1 (or omitted, as 1 is the default) for an effect to become visible. And for a barrel, I think you actually want local space, in other words omit the fourth parameter as well or set it to 0.
 

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
I'm pretty sure the third parameter of SetBoneRotation() should be set to 1 (or omitted, as 1 is the default) for an effect to become visible. And for a barrel, I think you actually want local space, in other words omit the fourth parameter as well or set it to 0.
Thanks for advise but doesn't work on client(
 

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
I found that SetBoneRotation not simulated like in ut2003/2004
Code:
native final function SetBoneRotation( name BoneName, optional rotator BoneTurn, optional int Space, optional float Alpha );
so, it's imposible to do bone rotating on client without rewriten class Actor? :/
 

meowcat

take a chance
Jun 7, 2001
803
3
18
I don't know about Postal2, but in UT2004, the equivalent function of ProjectileFire (DoFireEffect/SpawnProjectile) was only executed on the server (in the weaponFire class, not weapon), so your DoRotate function would only be called there, not on the client. To run on the client you should call your DoRotate function from the appropriate point in the PlayFiring function instead (which should be simulated no matter where it is located).
 
Last edited:

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
I don't know about Postal2, but in UT2004, the equivalent function of ProjectileFire (DoFireEffect/SpawnProjectile) was only executed on the server (in the weaponFire class, not weapon), so your DoRotate function would only be called there, not on the client. To run on the client you should call your DoRotate function from the appropriate point in the PlayFiring function instead (which should be simulated no matter where it is located).
Thank you for advise. So, i already attempt to do bone rotation for pawn on the Tick, and it too doesn't work

Code:
simulated function Tick(float DeltaTime) {
local rotator rotme;
rotme.yaw = 32768;
SetBoneRotation('FAN_BONE', rotme, 0, 1);
Super.Tick(DeltaTime);
}

it's not imortant where function called, it anyway doesn't work on client
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
I found that SetBoneRotation not simulated like in ut2003/2004
Code:
native final function SetBoneRotation( name BoneName, optional rotator BoneTurn, optional int Space, optional float Alpha );
so, it's imposible to do bone rotating on client without rewriten class Actor? :/

Native functions are treated like simulated ones. There is no need at all to declare native functions simulated, except maybe to increase the amount of confusion.
Did you make sure the code containing your SetBoneRotation call is executed at all?
 
Last edited:

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
Native functions are treated like simulated ones. There is no need at all to declare native functions simulated, except maybe to increase the amount of confusion.
Did you make sure the code containing your SetBoneRotation call is executed at all?
Yes, it's successfully working on single-player game and non-dedicated server
 

EQ²

Code Monkey
Oct 30, 2004
244
0
16
41
Near Birmingham, UK
www.teambse.co.uk
Yes, it's successfully working on single-player game and non-dedicated server
I think Wormbo meant "Did you make sure the code containing your SetBoneRotation call is executed at all on the client?". You say your code is called from Tick(), but does the actor have an appropriate RemoteRole which means that Tick() is executed on a client as well? Not all actors do by default so it depends on what the superclass is and whether you've set RemoteRole explicitly: if RemoteRole is ROLE_DumbProxy then that may be your issue.

Also, for performance reasons, sometimes notify functions are explicitly disabled client-side, look for disable('tick'); in superclass definitions to see if tick is being forcibly disabled.

If you want to check whether something is being executed client-side, a quick and dirty way is to deliberately crash the game (logging works too but crashing is quicker :D) using:

Code:
simulated function Tick(float DeltaTime)
{
    Super.Tick(DeltaTime);

    if (Level.NetMode != NM_DedicatedServer) // Client or listen server
    {
        // Do some effects stuff

        if (Level.NetMode == NM_Client) // only a client
        {
            assert(false);   // crash the game instantly "assertion failed"
        }
    }
}

If the game crashes then you know it's being executed client-side, if it runs then you know that the function is never being called.
 

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
I think Wormbo meant "Did you make sure the code containing your SetBoneRotation call is executed at all on the client?". You say your code is called from Tick(), but does the actor have an appropriate RemoteRole which means that Tick() is executed on a client as well? Not all actors do by default so it depends on what the superclass is and whether you've set RemoteRole explicitly: if RemoteRole is ROLE_DumbProxy then that may be your issue.

Also, for performance reasons, sometimes notify functions are explicitly disabled client-side, look for disable('tick'); in superclass definitions to see if tick is being forcibly disabled.

If you want to check whether something is being executed client-side, a quick and dirty way is to deliberately crash the game (logging works too but crashing is quicker :D) using:

Code:
simulated function Tick(float DeltaTime)
{
    Super.Tick(DeltaTime);

    if (Level.NetMode != NM_DedicatedServer) // Client or listen server
    {
        // Do some effects stuff

        if (Level.NetMode == NM_Client) // only a client
        {
            assert(false);   // crash the game instantly "assertion failed"
        }
    }
}

If the game crashes then you know it's being executed client-side, if it runs then you know that the function is never being called.
Ok, game doesn't crash
 

EQ²

Code Monkey
Oct 30, 2004
244
0
16
41
Near Birmingham, UK
www.teambse.co.uk
if RemoteRole is ROLE_DumbProxy then that may be your issue.

KActor, RemoteRole is ROLE_DumbProxy

It's unlikely that tick() is being executed client-side then, and since it's KActor you can't replicate functions to it (the actor is not "owned" by a client). Change the RemoteRole to ROLE_SimulatedProxy and see if this cures your problem.

Be aware that changing the RemoteRole can affect other things though so you may need to do some more advanced testing, for example if the superclass' tick() function doesn't check whether it's client side or not then it may be necessary to only call the super.tick() on the 'server', like this:

Code:
simulated function Tick(float DeltaTime)
{
    if (Role == ROLE_Authority) Super.Tick(DeltaTime);

    if (Level.NetMode != NM_DedicatedServer)
    {
        // Do funky client-side stuff
    }
}

Basically this is only an issue if the superclass has been coded with the assumption that certain functions are only executed on the authority, which is pretty messy and stupid but something to be aware of nonetheless.

Another point to note is that having your actor ticked client-side all the time might not be what you really want, for example if the bone rotation effect happens only once you could disable the tick function client-side once the rotation is complete, and re-enable it when needed (for example use a replicated variable and check its value in PostNetReceive.

Code:
var byte ClientEffect, ServerEffect;

replication
{
    unreliable if (Role == ROLE_Authority)
        ServerEffect;
}

simulated function Tick(float DeltaTime)
{
    Super.Tick(DeltaTime);

    if (Level.NetMode != NM_DedicatedServer)
    {
        // Do funky client-side bone rotation

        [b][COLOR="Red"]if (Level.NetMode == NM_Client && SomeVarIndicatesRotationIsFinished)
            disable( 'tick' );[/COLOR][/b]
    }
}

// Call this function server-side to trigger client-side effect
function ServerTriggerEffect()
{
    ServerEffect++;
}

simulated function ClientTriggerEffect()
{
    [COLOR="Red"][B]enable( 'tick' )[/B][/COLOR];

    // Do stuff
}

simulated event [b][COLOR="Red"]PostNetReceive()[/COLOR][/b]
{
    if (Level.NetMode == NM_Client && ClientEffect < ServerEffect)
    {
        ClientTriggerEffect();
    }

    ClientEffect = ServerEffect;
}

defaultproperties
{
    [B][COLOR="Red"]bNetNotify=true[/COLOR][/B]  // Enables the PostNetReceive event
}

Notice that this example only disables the tick event on clients (not servers, single player, or listen server). In reality if the event happens once and never again you can disable tick client-side after your bone rotation has played and not worry about re-enabling it. Conversely, if your effect happens at some arbitrary time after the actor is spawned then you should disable tick in PreBeginPlay (on client) and enable it with a replicated variable as shown when needed.
 
Last edited:

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
Be aware that changing the RemoteRole can affect other things though so you may need to do some more advanced testing, for example if the superclass' tick() function doesn't check whether it's client side or not then it may be necessary to only call the super.tick() on the 'server', like this:
Second code will be affected without ROLE_SimulatedProxy?
Because actor with simulatedproxy have collapsed on client :lol:

And "bNetNotify" not written on game's class Actor
 
Last edited:

EQ²

Code Monkey
Oct 30, 2004
244
0
16
41
Near Birmingham, UK
www.teambse.co.uk
Please elaborate? Are you saying that changing it to SimulatedProxy causes the physics to fail client-side? If that's the case then you may have to do the rotation with a "helper" actor. Basically this would be another replicated actor which actually manages the effects for the KActor and can safely be set to SimulatedProxy to run the code you need.
 

EvilT-ModZ

Un-Gravitify
Aug 3, 2011
42
0
6
30
Russia
www.set-games.ru
Please elaborate? Are you saying that changing it to SimulatedProxy causes the physics to fail client-side? If that's the case then you may have to do the rotation with a "helper" actor. Basically this would be another replicated actor which actually manages the effects for the KActor and can safely be set to SimulatedProxy to run the code you need.
So, im sure what bone rotating working with simulatedproxy, but
http://s55.radikal.ru/i149/1108/dd/d343e374a968.jpg, you can see:
rendering mesh of actor stay static and it's collision and some stuff have moved
 

EQ²

Code Monkey
Oct 30, 2004
244
0
16
41
Near Birmingham, UK
www.teambse.co.uk
If making it SimulatedProxy breaks it client-side then you'll have to stick with dumb proxy - this is likely either because some code isn't checking whether Role == Authority and is executing client-side with empty values, or because setting it to SimulatedProxy alters the physics behaviour, either way you need to find another solution.

If your actor has other attached actors you might be able to put your code in them, either that or just make your own attached "helper" actor to just provide the animation. Here's a canned example (pseudocode, I'm typing directly into the edit window not into a nice IDE):

Your helper actor:
Code:
class BoneRotationHelper extends Actor notplaceable;

var float currentBoneRotation, desiredBoneRotation, boneRotationRate;
var name boneName;

simulated event PreBeginPlay()
{
    currentBoneRotation = 0;
    disable( 'tick' );
}

// Call this function to begin rotating the specified bone at the specifed rate
simulated function RotateBone(name boneToRotate, float desiredPitch, float desiredRate)
{
    currentBoneRotation = MyKActor(Owner).GetBoneRotation( boneToRotate ).Pitch;
    desiredBoneRotation = desiredPitch;
    boneRotationRate = desiredRate;
    boneName = boneToRotate;
    enable( 'tick' );
}

simulated event Tick(float DeltaTime)
{
    local Rotator boneRotation;
    local float deltaPitch;

    Super.Tick(DeltaTime);

    if (currentBoneRotation < desiredBoneRotation)
        deltaPitch = boneRotationRate * DeltaTime;
    else if (currentBoneRotation > desiredBoneRotation)
        deltaPitch = -boneRotationRate * DeltaTime;
    else
        disable( 'tick' );   // Target reached, disable ticking
  
    currentBoneRotation += deltaPitch
    boneRotation.Pitch = currentBoneRotation;

    MyKActor(Owner).SetBoneDirection( boneName, boneRotation );
}

defaultproperties
{
    RemoteRole=ROLE_None
}

Your KActor derived class
Code:
class MyKActor extends KActor placeable;

var BoneRotationHelper rotationHelper;
var byte serverEffect, clientEffect;

replication
{
    reliable if (Role == ROLE_Authority)
        serverEffect;
}

simulated event PreBeginPlay()
{
    Super.PreBeginPlay();

    // Only need the helper on client, listen server and SP
    if (Level.NetMode != NM_DedicatedServer)
    {
        rotationHelper = Spawn( class'BoneRotationHelper' );
    }
}

simulated event PostNetBeginPlay()
{
    Super.PostNetBeginPlay();

    // Initial replication, set effect counter to avoid triggering
    // effect when actor becomes relevant
    clientEffect = serverEffect;
}

simulated event Destroyed()
{
    // Clean up the helper when we are destroyed
    if (rotationHelper != None)
    {
        rotationHelper.Destroy();
        rotationHelper = None;
    }
}

// Call this function server-side to trigger the effect, client replication
// is handled by the replicated variable
function TriggerEffect()
{
    serverEffect++;
    ClientTriggerEffect();    // Trigger event on listen server / single player
}

simulated function ClientTriggerEffect()
{
    if (Level.NetMode != NM_DedicatedServer && rotationHelper != None)
    {
        rotationHelper.RotateBone( 'someBone', 16384, 600 );
    }
}

simulated event PostNetReceive()
{
    if (Role < ROLE_Authority && serverEffect > clientEffect)
    {
        clientEffect = serverEffect;
        ClientTriggerEffect();
    }
}

defaultproperties
{
    RemoteRole = ROLE_DumbProxy
    bNetNotify = true
}

Notice that RemoteRole on the helper actor is ROLE_None, this means the actor isn't replicated, instead a separate copy is spawned by the client and the server and the replication of the effect (the bone rotation) is still handled on your main KActor.

The example also disables ticking the helper actor when it is not doing any work, this isn't strictly necessary but a useful optimisation in case you have lots of these.

You should also note that since we're spawning a new actor we have to manage cleaning it up ourselves, this is managed in the KActor's Destroyed() function.

This is just a canned example really but hopefully it helps, sorry if the code doesn't compile but I don't have UT2k4 or an IDE here at work so I've just bashed this out from memory.
 

EQ²

Code Monkey
Oct 30, 2004
244
0
16
41
Near Birmingham, UK
www.teambse.co.uk
Afraid not, it looks like they took that function out in the Postal2 build of UE2 (it's not uncommon for licensees to remove functions they don't need and I don't think multiplayer was thought about. Basically you can't use the method above by the looks of it.

So, you can't make it simulatedproxy and you can't do efficient replication on the actor so you'll have to attach the helper actor server-side and do your replication in there. The down side is that because you can't use PostNetReceive you'll have to tick the helper actor all the time and check the value of your replication flag every tick. It sucks and but I can't see there's much alternative really.