Search Unity

Best method for shooting/firing a weapon for multiplayer non-authoritative

Discussion in 'Multiplayer' started by Caps, Apr 26, 2011.

  1. Caps

    Caps

    Joined:
    Jul 23, 2010
    Posts:
    21
    Hello guys,


    I would like to know what would be best approaches for "shooting"firing a weapon (like a gun) for a multiplayer non-authoritative setup.

    Currently I'm not thinking of rigidbodies, don't really think they are necessary because I think one could achieve similar effects just by using a raycast, that is, if the hit object is a rigidbody, than just apply a force at impact. Please correct me if I'm missing something...

    My main concern would be how to send the information over the network, so for example, if a character is shooting an automatic rifle, shooting several times a second, what method would be best? RPC or Stream? I mean, it is important to have the exact same number of times a client shoots and the timing he shoots being received by another client.

    If anyone could give some advice or ideas or whatever for this discussion, please do so, I'm sure more people out there might come up to this same issue.

    Thanks in advance,
     
  2. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,290
    RPC is a lot easier on the coding side. Also, it is the method of choice for discrete events, so for your case I don't see why you wouldn't want to use it. Don't start optimising if you don't have a problem.
     
  3. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    RPCs are fine for slower weapons that shoot a few times a second, but for a fully automatic weapon with a high rate of fire, I probably wouldn't use RPCs. RPCs are always reliable, so if there is any packet loss on an RPC, that RPC will continue to try to get through completely before it moves on to the next RPC in the "queue" (not to be confused with the RPC buffer).

    So if there is even a slight amount of packet loss when there are a ton of RPCs being broadcast, you will start to get a buildup of RPCs because it might get hung up on a few, and it will effect your gameplay.

    I started to use an isFiring boolean variable that I sync with OnSerializeNetworkView() that gets enabled when the player is holding down the fire button, and all the other clients simulate the gunfire locally as long as isFiring is true.
     
  4. Caps

    Caps

    Joined:
    Jul 23, 2010
    Posts:
    21
    Hey guys,


    Thanks for your replies.

    I have decided to test both approaches. So, for now, I'm just returning from a test done with RPCs...

    These are the results:

    If a player "taps" the mouse button to fire once or twice, ok, the other clients see the player shooting once or twice.

    If a player holds down the mouse button (for a automatic fire), we have a problem, clients get a build up of shootings, that is, completely off the fire rate. The fire rate is the timing for wich a shot is allowed to be fired after another, for test purposes I'm using 150 ms for fire rate. So, in this case, even though the player is shooting in a correct timing, the other clients "receive" those shots as a frame per frame rate, that is, the gun is now firing way faster then the fire rate established. That's exactly what ledend411 mentioned above, there is a buildup of RPCs being received... Maybe there is a work around this, but I can imagine it's going to be complicated.

    So before I start testing with streaming (OnSerializeNetworkView()), I would like to check some statements. From what I understand, streaming comunication using OnSerializeNetworkView() will "update" using "sendrate" settings from network settings, by default, it is set to 15, this of course can be set by the user.

    So, what I'm thinking is, how can I have a correct fire rate being sent, if player holds down the fire button if the sendrate is differente from the actual Update() function? Would it still work? If a player shoots 15 rounds in a row, non-stop, would other clients receive those 15 shots in a row exactly in the same timing and fire rate? (I'm considering the state syncronization being set to Reliable Delta Compressed)...

    What do you guys think about it?


    Thanks again for the enlightenments...
     
    Last edited: Apr 26, 2011
  5. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    Like I said, what I do is sync an isFiring boolean, nothing else. Then everyone has a bit of code that just does something like:

    Code (csharp):
    1.  
    2. if(isFiring  (Time.time > nextFire)){
    3.     //create a bullet/raycast or whatever here
    4.     nextFire = Time.time + fireRate;
    5. }
    6.  
    In tests I've done, as far as I can tell there is never extra bullets happening, but I suppose there is a very slim chance that if there was a bit of latency the amount of time it would take for a sync send to go through might result in an extra bullet.

    But, a few things:

    -You will never get "exactly the same timing" in a multiplayer game over the Internet.

    -A common trick is to make it so that even though when a client fires it simulates his firing visually on all other clients, only the firing simulation in the firing client's game can actually broadcast damage messages. That way, an extra bullet on another client is insignificant, as it was just a visual anomaly and won't effect their gameplay.

    -The best you can do is try to make it so what all clients see is very similar, but it doesn't have to be perfect. After all, they can only see their own instance of the game anyway.

    If you've played games like Halo, Call of Duty, Gears of War, Half Life 2, and CounterStrike with latency, you can see that even AAA studios do these things. The bullets don't even hit in the same places on different clients, and there are plenty of occasions where there are different amounts of bullets fired from the same burst on different clients. They generally use these same techniques I mentioned. If an AAA studio with millions of dollars in budget can't make a perfect multiplayer simulation, you are going to kill yourself trying.
     
    Last edited: Apr 26, 2011
    HexDog likes this.
  6. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,290
    if you want the same rate of fire, then your clients need to know what the correct rate should be and simulate it locally. You won't ever get an identical ROF across the network, neither with RPCs nor with syncing.

    legend's approach is probably the best one for that case. The other solution is to use "bursts" - define that the weapon will fire 3 or 5 or whatever shots for every frame that the button was down, and transmit 1 update to the clients, who then also fire this burst with the same number of bullets.
     
  7. Caps

    Caps

    Joined:
    Jul 23, 2010
    Posts:
    21
    Ok guys,

    Legend411, thanks again for these great tips. Yes I understand that even big studios with big budgets and big teams don't make things perfect, we all see glitches in games all the time... yeah, I still play those games... That's what's inspiring me to make an FPS myself ;)

    I'm gonna attempt your approach using a bool value from the client firing and send that via streaming to the receiving player on other clients...

    Tom, thanks for your advice, I guess I'll try that: Get the bool value received and just use the same code, as if this bool value was the mouse button input... That way, as the character on other client is receiving the "isfiring"bool value, it'll just interpret it as if the actual client is holding down the fire button....

    Lets see how this goes...

    thx again!
     
  8. Caps

    Caps

    Joined:
    Jul 23, 2010
    Posts:
    21
    Ok guys,


    For some reason, since yesterday I cannot sync a simple shooting on clients...

    Either there's something very wrong with OnSerializeNetworkView() or I'm doing something wrong!

    The strangest thing is that, I was already reading and writing other variables like position, orientation and animation states, these are seen ok both on server and other clients... Now, for the shooting, I can only see a client shooting on the server, but clients can't see each other shooting... This is what I'm using:





    if (stream.isWriting)

    {
    Vector3 send_position= player_capsule.transform.position;
    stream.Serialize(ref send_position);

    //send player orientation
    Quaternion send_rotation = player_capsule.transform.localRotation;
    stream.Serialize(ref send_rotation);


    //send player character_movement (movement_type)
    char movement_type = player_capsule.GetComponent<Character_Mover_Script> ().character_movement;
    stream.Serialize(ref movement_type);


    //send firing
    bool fire = player_node.GetComponent<Character_Ballistics_Script>().firing;
    stream.Serialize(ref fire);
    }

    else
    {


    //receive character position
    Vector3 receive_position = Vector3.zero;
    stream.Serialize(ref receive_position);
    player_capsule.transform.position = receive_position;

    //receive character orientation
    Quaternion receive_rotation = Quaternion.identity;
    stream.Serialize(ref receive_rotation);
    player_capsule.transform.localRotation = receive_rotation;


    char movement_type = 'n';
    stream.Serialize(ref movement_type);
    received_movement_type = movement_type;


    bool received_is_firing = false;
    stream.Serialize(ref received_is_firing);
    other_firing = received_is_firing;

    }





    what??????? :-|
     
  9. Caps

    Caps

    Joined:
    Jul 23, 2010
    Posts:
    21
    I just made a simple test and it seems OnSerializeNetworkView is not sending a bool value when a button is pressed. I've checked all variables and everything is ok because I switched back to RPC and it sends the bool value just fine.

    Could someone please check this with me?


    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Character_Network_Script : MonoBehaviour {
    6.  
    7. public bool this_is_client = false;
    8. public bool test;
    9. public bool received_test;
    10. public bool test_result;
    11.  
    12. void OnNetworkInstantiate(NetworkMessageInfo info)
    13. {
    14.        
    15.    if (networkView.isMine)
    16.    {
    17.         this_is_client = true;
    18.        
    19.     }
    20. }  
    21.  
    22. void Update ()
    23.    
    24. {
    25.  
    26.         test = false;
    27.         received_test = false;
    28.     test_result = false;
    29.  
    30.      if (this_is_client)
    31.      {
    32.  
    33.               if (Input.GetKey (KeyCode.Alpha5))
    34.           {
    35.         test = true;
    36.                    
    37.            }
    38.      }
    39. }
    40.  
    41. void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
    42. {
    43.        if (stream.isWriting)
    44.     {
    45.             stream.Serialize(ref test);
    46.         }
    47.        else
    48.        {
    49.              stream.Serialize(ref received_test);
    50.          test_result = received_test;
    51.                    
    52.         }
    53. }
    54.  
    55.