- Home /
[C#] UNET Client raycast not the same as Server raycast
I'm trying to fire a ray and then spawn an object at that location. It works perfectly fine on the server, but on any clients the placement is offset and I can't figure out why for the life of me. Here's a picture to describe what I mean in more detail.
And here's my code:
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class Building_MP : NetworkBehaviour {
[SerializeField]
GameObject GhostGO;
[SerializeField]
LayerMask LM;
bool snap = false;
int ghostID;
// Use this for initialization
void Start () {
ghostID = Random.Range(0, 10000);
}
// Update is called once per frame
void Update () {
if(Input.GetButtonDown("Fire2") && isLocalPlayer)
{
CmdSpawnGhostBrick();
}
if(isLocalPlayer)
{
//CmdMoveGhostBrick();
if (Input.GetKeyDown(KeyCode.B))
{
snap = !snap;
}
}
}
[Command]
void CmdSpawnGhostBrick()
{
RaycastHit hit;
Ray ray = GetComponentInChildren<Camera>().ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 200.0f, LM))
{
GameObject ghostBrick = Instantiate(GhostGO, new Vector3(RoundToHalf(hit.point.x), RoundToHalf(hit.point.y), RoundToHalf(hit.point.z)), Quaternion.identity) as GameObject;
ghostBrick.transform.name = "GhostBrick (" + ghostID + ")";
ghostBrick.transform.tag = "Ghost";
ghostBrick.layer = 10;
NetworkServer.Spawn(ghostBrick);
Debug.Log("Hit at " + hit.point.ToString());
Debug.Log("BrickPos " + ghostBrick.transform.position);
}
}
[Command]
void CmdMoveGhostBrick()
{
GameObject ghostBrick = GameObject.Find("GhostBrick (" + ghostID + ")");
if(ghostBrick != null)
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 100.0f, LM))
{
if (!snap)
{
ghostBrick.transform.position = new Vector3(RoundToHalf(hit.point.x), RoundToHalf(hit.point.y) + ghostBrick.GetComponent<Renderer>().bounds.size.y / 2, RoundToHalf(hit.point.z));
}
else
{
ghostBrick.transform.position = new Vector3(hit.transform.position.x, hit.transform.position.y + ghostBrick.GetComponent<Renderer>().bounds.size.y, hit.transform.position.z);
}
}
if(Input.GetButtonDown("Fire1"))
{
GameObject newBrick = Instantiate(GhostGO, ghostBrick.transform.position, ghostBrick.transform.rotation) as GameObject;
NetworkServer.Spawn(newBrick);
}
}
}
[Command]
void CmdDestroyGhost()
{
GameObject ghostBrick = GameObject.Find("GhostBrick (" + ghostID + ")");
if(ghostBrick != null)
{
NetworkServer.Destroy(ghostBrick);
}
}
public static float RoundToHalf(float f)
{
return f = Mathf.Round(f * 4f) * 0.25f;
}
}
Thanks a ton in advance, I'm at my wit's end right now.
Something I forgot to note is that Cmd$$anonymous$$oveGhostBrick is irrelevant to the problem, since I commented it out. All that's being used right now is CmdSpawnGhostBrick. Just figured I'd save anyone whose reading it the extra confusion
Yes, it has a networktransform. When the client places a brick, it does place it on the server as well, it's just offset from where the player was looking for whatever reason. This inaccuracy happens when any client tries to place a brick, but not when the server does.
I would used raycast results i wanted, passing them throgh Cmd/Rpc. In this case it would not matter if they are different on server and clients. I did not paid attention to your code btw.
Could you post an example of this? I'm not 100% positive what you mean.
Answer by ThaiCat · Jan 06, 2016 at 08:17 PM
I used this to destroy specific block, that was result of raycast on client. It is not 100% server-authoritative unfortunately, but it is okay for me. You can also send to server additional parameters as hit.point (vector3) or something like that. Note there is network Identity component on that block also.
void DeleteOnMouseDown()
{
//can only destroy components with myParent script attached to them
if (Input.GetAxis("Fire3") > 0f)
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
if (hit.collider != null)
{
nearbyColliderParentScript = GetBlockByColliderHit(hit).GetComponent<MyParent>();
if (nearbyColliderParentScript != null)
{
blockToDestroy = nearbyColliderParentScript.myParent.gameObject;
if (blockToDestroy.GetComponent<MountPoints>().markedForDestroy == false)
{
CmdMarkBlockForDestruction(blockToDestroy);
blockToDestroy.GetComponent<MountPoints>().markedForDestroy = true;
}
}
}
}
}
}
[Command]
void CmdMarkBlockForDestruction(GameObject block)
{
GameObject shipRoot = block.GetComponent<MountPoints>().rootFragment.gameObject;
shipRoot.GetComponent<ShipIntegrity>().AddBlockToDestroy(block);
}
I noticed that you weren't running the raycast on the server (the [command]), is there any particular reason to not do this?
After i did raycast on client, i know the object on which i want to perfom actions. I send that object to server and server does whatever i want. I see no reason why i should to perform raycast twice.
I dont think it is even possible to make raycast results on every client and server the same in fully dinamic environment (this is my case). But if your scene has static objects, you can still get the same results for them, if you sync camera position&rotation and cursor position right before that.
I think that may be part of my problem then, I'm perfor$$anonymous$$g the raycast on the server. When I get back to my computer I'll give it a shot
Yep, as soon as I moved the raycast from being done on the server to being done on the client and then passed to the server, everything worked fine- thanks!
Answer by maracepoc · Dec 06, 2017 at 12:58 PM
Hi, Following your discussion on Ray casting I have a related problem. Maybe it's a question that @slimabob could answer: how did you pass the raycast hit collider position to the server as mentioned in your last answer? I'd really appreciate your help. Thank you.
Your answer
Follow this Question
Related Questions
Unity networking tutorial? 6 Answers
UNET Health isn't syncing 1 Answer
Multiplayer desynchronize when grabbing an object by code. 0 Answers
(C#) Collider to collider2d 0 Answers
raycasting without colliders 0 Answers