1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. 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.

UE2 - UT2kX Bone Rotating (client side)

Discussion in 'Programming' started by EvilT-ModZ, Aug 3, 2011.

  1. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    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: Aug 3, 2011
  2. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    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
     
  3. Wormbo

    Wormbo Administrator Staff Member

    Joined:
    Jun 4, 2001
    Messages:
    5,910
    Likes Received:
    35
    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.
     
  4. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    Thanks for advise but doesn't work on client(
     
  5. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    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? :/
     
  6. meowcat

    meowcat take a chance

    Joined:
    Jun 7, 2001
    Messages:
    774
    Likes Received:
    1
    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: Aug 7, 2011
  7. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    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
     
  8. Wormbo

    Wormbo Administrator Staff Member

    Joined:
    Jun 4, 2001
    Messages:
    5,910
    Likes Received:
    35
    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: Aug 8, 2011
  9. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    Yes, it's successfully working on single-player game and non-dedicated server
     
  10. EQ²

    EQ² Code Monkey

    Joined:
    Oct 30, 2004
    Messages:
    244
    Likes Received:
    0
    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.
     
  11. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    Ok, game doesn't crash
     
  12. EQ²

    EQ² Code Monkey

    Joined:
    Oct 30, 2004
    Messages:
    244
    Likes Received:
    0
    Which means your code is never being executed client-side. What is the superclass of the actor this code is inside, and what is the actor's RemoteRole setting?
     
  13. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    KActor, RemoteRole is ROLE_DumbProxy
     
  14. EQ²

    EQ² Code Monkey

    Joined:
    Oct 30, 2004
    Messages:
    244
    Likes Received:
    0
    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: Aug 8, 2011
  15. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    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: Aug 8, 2011
  16. EQ²

    EQ² Code Monkey

    Joined:
    Oct 30, 2004
    Messages:
    244
    Likes Received:
    0
    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.
     
  17. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    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
     
  18. EQ²

    EQ² Code Monkey

    Joined:
    Oct 30, 2004
    Messages:
    244
    Likes Received:
    0
    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.
     
  19. EvilT-ModZ

    EvilT-ModZ Un-Gravitify

    Joined:
    Aug 3, 2011
    Messages:
    36
    Likes Received:
    0
    I have a problem, game's class "Actor" doesn't have this bool,
    it will be working without it?
     
  20. EQ²

    EQ² Code Monkey

    Joined:
    Oct 30, 2004
    Messages:
    244
    Likes Received:
    0
    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.
     

Share This Page