- Home /
Adding weapons to a networking game
So, I have tried to add weapons to my multi-player networking game. The problem is the only place they are working is the host. The outline of my code is just to pick up the weapon and that is working fine the problem now is when I pick up the object on a client it turns around weirdly instead of just staying straight, which is what I want to happen. When I pick the weapon up on a client the host doesn't even register it. So, what am I dong wrong, is it my coding or what.
this my "equip" code: private bool HoldingWeapon;
private Layer$$anonymous$$ask Weapon;
Vector3 hold;
Vector3 point;
public Transform HeldWeapon;
public Transform self;
Collider collider;
public float range;
public GameObject Gun;
// Use this for initialization
void Start () {
RaycastHit hit;
Weapon = 1 << 8;
}
// Update is called once per frame
void Update () {
RaycastHit hit;
Vector3 forward = transform.TransformDirection(Vector3.forward) * 3f;
if (Physics.Raycast(transform.position, (forward), out hit, range, Weapon))
{if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.P))
{
dropWeapon();
if (HeldWeapon.childCount.Equals(0))
{
hold = transform.TransformVector(0,0,0);
Gun = hit.collider.gameObject;
collider = Gun.GetComponent<Collider>();
collider.enabled = false;
Gun.transform.parent = HeldWeapon;
Gun.transform.localRotation = Quaternion.Euler(0, 0, 0);
Gun.transform.localPosition = Vector3.zero;
HoldingWeapon = true;
Gun.GetComponent<Rigidbody>().is$$anonymous$$inematic = true;
}
}
}
}
void dropWeapon()
{
if (HoldingWeapon)
{
Gun.transform.parent = null;
collider = Gun.GetComponent<Collider>();
collider.enabled = true;
Gun.GetComponent<Rigidbody>().is$$anonymous$$inematic = false;
}
}
}
this is my script to make sure it is the local player: // Use this for initialization void Start () { if (!isLocalPlayer) { return; } }
// Update is called once per frame
void Update () {
if (!isLocalPlayer)
{
transform.GetChild(0).gameObject.SetActive(false);
return;
}
if (isLocalPlayer)
{
transform.GetChild(0).gameObject.SetActive(true);
}
}
}
this is my walking script:
bool IsDashing;
public float StandSize;
public float CrouchSize;
bool crouching;
Rigidbody rigidBody;
public float Speed;
public float BeforeSpeed;
public float DashSpeed;
public $$anonymous$$eyCode Forward;
public $$anonymous$$eyCode Back;
public $$anonymous$$eyCode Right;
public $$anonymous$$eyCode Left;
public $$anonymous$$eyCode DashButton;
// Use this for initialization
void Start()
{
if (!isLocalPlayer) return;
BeforeSpeed = Speed;
DashSpeed = Speed * 2;
}
// Update is called once per frame
void FixedUpdate()
{
if (!isLocalPlayer) return;
Vector3 fforward = transform.TransformDirection(Vector3.forward) * 3f;
Vector3 lleft = transform.TransformDirection(Vector3.left) * 3f;
Vector3 rright = transform.TransformDirection(Vector3.right) * 3f;
Vector3 bback = transform.TransformDirection(Vector3.back) * 3f;
if (!IsDashing)
DashSpeed = Speed * 2;
if (Input.Get$$anonymous$$ey(DashButton))
{
if (Input.Get$$anonymous$$ey(Forward))
{
if (!Input.Get$$anonymous$$ey(Left))
if (!Input.Get$$anonymous$$ey(Back))
if (!Input.Get$$anonymous$$ey(Right))
{
Speed = DashSpeed;
IsDashing = true;
}
else
{
IsDashing = false;
}
}
else { Speed = BeforeSpeed; }
}
else { Speed = BeforeSpeed; }
if (Input.Get$$anonymous$$ey($$anonymous$$eyCode.RightShift))
{
transform.localScale = new Vector3(1, CrouchSize, 1);
crouching = true;
}
else { transform.localScale = new Vector3(1, StandSize, 1); }
if (Input.Get$$anonymous$$ey(Forward))
{
transform.Translate(0, 0, Speed / 80);
}
if (Input.Get$$anonymous$$ey(Left))
{
Speed = BeforeSpeed;
transform.Translate(-Speed / 85, 0, 0);
}
if (Input.Get$$anonymous$$ey(Back))
{
Speed = BeforeSpeed;
transform.Translate(0, 0, -Speed / 85);
}
if (Input.Get$$anonymous$$ey(Right))
{
Speed = BeforeSpeed;
transform.Translate(Speed / 85, 0, 0);
}
}
}
Hey there, please try to reduce the amount of code that you posted. Try to boil it down to the necessary bits. (e.g. movement code is not needed here) 200 lines of code will not make people answer your question.
Thanks, but I didn't know if the movement script was or wasn't needed because when the client moves, the weapon spins around crazily. But I understand what your saying.
Answer by ray2yar · Jan 30, 2019 at 10:15 PM
That's a ton of code to weed through. But, from what I saw I don't see any functionality for the client to actually tell the server about weapon equips. When your player does an action like equip a weapon, then it must tell the server to do that action and the server must in turn tell the other clients.... something like this...
void playerEquips()
{
//I am the local player and have chosen to equip my weapon
CmdPlayerEquip();
}
[Command]
void CmdPlayerEquip()
{
//I am the server, a player has told me they'd like to equip a certain
//object... I will do stuff to make sure this is legal, if so, I'll do it
//if I did that stuff then I will tell the other players to show this as well
RpcPlayerEquip();
}
[ClientRpc]
void RpcPlayerEquip()
{
//some player on the network has equipped a weapon, I will show this
//on my end
//change objects, display new objects etc...
//I will also be called on the player that initially started the equipping
//process
}
I would add, that if this is your first network project... I highly recommend doing a smaller project to learn the networking mechanics first. That way all you're tackling is "simple" things first.
So on the RpcPlayerEquip() how would I show what happens, are their keywords that I should use or do I just find the game object and attach it to the player? Also, would SYNC VAR work for this?
A sync var with a hook will effectively do the same thing.
In the RPC I would put the actual code that updates the gameobject.
void Update () {
CmdPlayerEquip();
}
[Command]
void CmdPlayerEquip()
{
RaycastHit hit;
Vector3 forward = transform.TransformDirection(Vector3.forward) * 3f;
if (Physics.Raycast(transform.position, (forward), out hit, range, Weapon))
{
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.P))
{
dropWeapon();
if (HeldWeapon.childCount.Equals(0))
{
RpcPlayerEquip();
}
}
}
}
[ClientRpc]
void RpcPlayerEquip()
{
RaycastHit hit;
Vector3 forward = transform.TransformDirection(Vector3.forward) * 3f;
if (Physics.Raycast(transform.position, (forward), out hit, range, Weapon))
{
hold = transform.TransformVector(0, 0, 0);
Gun = hit.collider.gameObject;
collider = Gun.GetComponent<Collider>();
collider.enabled = false;
Gun.transform.parent = HeldWeapon;
Gun.transform.localRotation = Quaternion.Euler(0, 0, 0);
Gun.transform.localPosition = Vector3.zero;
HoldingWeapon = true;
Gun.GetComponent<Rigidbody>().is$$anonymous$$inematic = true;
}
}
So, I just tried this and got this
System.NullReferenceException: Object reference not set to an instance of an object at UnityEngine.Networking.NetworkBehaviour.get_isServer () [0x00007] in C:\buildslave\unity\build\Extensions\Networking\Runtime\NetworkBehaviour.cs:19 at Equip.CallCmdPlayerEquip () [0x00000] in :0 at Equip.Update () [0x00002] in C:\Users\Da$$anonymous$$ids\OneDrive\The Shapes' War\Assets\C# scripts\Equip.cs:34 occurred
Two questions, why am I getting this now when I wasn't before, and is my script correct?
Well, I'm going to say it probably won't do what you expect for two big reasons:
1) you call the command every update. You probably only want to if the local player attempts to do something
2) you have raycasting everywhere, I thought you had asked about changing weapons which basically is just changing a graphic. If you're doing hit detection you probably only want to do that on the server / host
I highly recommend watching the FPS tutorial by Brackeys on YouTube.
Yes, I do have ray casting everywhere because that's how I like to make "pick up" scripts, and the player is just running up to the weapon and attaching it to the "hand" making it go to local 0,0,0. So I guess changing was not the best word to describe it. In reply to your first suggestion, would checking if the "P" key is down help, or if it is in range?
So remember, the Rpc happens on EVERY client, which means you'll be raycasting on every client, which may or may not have the most up to date position of the player.... the results could be bazarr ... either raycast on the server or on the localPlayer. I thought you were talking about switching weapons in the same way one might change the color of a player texture.
For picking up weapons... you have to decide on who is going to have the final say... the localPlayer OR the server. Both have strengths and weaknesses. I recommend server side. So in the CO$$anonymous$$$$anonymous$$AND you do the raycasting to see if the player is "allowed" or "able" to pick up the weapon, IF SO, then you execute that on all the clients with Rpc (for example putting the weapon in the hand).
//pseudo code
void Update()
{
if(!isLocalPlayer) return;
//get input
if(key pressed) PickUp();
}
void PickUp()
{
//deter$$anonymous$$e where the player was looking for a raycast
//I'm not writing that code here
CmdPickUp(Vector3 direction)
}
[Command]
void CmdPickUp(Vector3 dir)
{
//use position and direction info to deter$$anonymous$$e the raycast
//perform the cast and see if the item can be picked up
//if the weapon has it's own network identity and network transform do
//the pickup here and you are done
//if it doesn't then do an Rpc
}
I guess for what you're describing you might get away with putting a network identity and network transform on your weapons and handling it all on the localPlayer.... possibily... idk... but you definitely do NOT want to be doing input checks and raycasts on the rpc call (or input checks in the command). Get all you input from the local player...
Thanks again for helping me out. All right, before just curious would it be better to have a network identity and transform with the client Rpc or just the Rpc?
I think you're looking at using at using a command/rpc. I don't think you can parent an object with a network identity onto something that has a network identity already... $$anonymous$$aybe network child? Honestly at this point I kinda would just need to see the project in it's entirety.
Answer by James_BadAlchemy · Jan 30, 2019 at 08:42 PM
That's a tad overwhelming.
I've just finished building an online top-down shooter with Photon.
Here's some important things I wish I understood going into it.
I see you're checking to see if the player IsLocal quite a lot. This is great. However, it's easy to lose track of what you're actually wanting to check for. Are you wanting to check to see if the player !IsLocal or if the player is a client connected to the host? They're not interchangeable and being watchful of this will save you lots of debugging time.
Generally, when you do something to LocalPlayer, you need to do the same thing to that player over the network for other players to see. In photon we use Remote Procedure Calls(RPC) to accomplish this. If player A changes their color from blue to red on client side. We now need to send a command FROM this local player over the network to each other player and change the color of the "target" player (which is Player A). Wrapping your brain around that general idea was super helpful for me.
Thanks, but this is actually my first networking experience I have only about two years of experience so I don't quite understand the intricacies of what your saying. Just to clarify you are basically saying that the local player is not the same as the client player, and when I tell the local player to do something then I also have to do it (separately) to the client. If I am correct how would I from the local player send the message to change the color. (You don't need to write a whole script out but if you could just point me in the right direction (in laymen terms)), also thanks a ton!
Check on my answer below.
In the example in your comment above the local player decides to change color... That local player is ALSO a client and possibly also the host. In any event, the local player most tell the host using a CO$$anonymous$$$$anonymous$$AND. Commands are executed on the server but can be called from any client. The server will then need to tell the other clients about the color change using an RPC, which is executed on every client, including the local player. You have to remember that every single person in thev game has every object and it is up to the server to sync all of it ... You but have to carefully plan it.
Consider the above instance, perhaps another v player connects to the game AFTER a player had changed their color. Upon that player connecting the server needs to give that new player the information. SYNC VAR can help with this immensely.