Non-Player Obj - Different Behavior between Server and Client

This #C script attached to a Non Player Obj name “SelfDestruct_Creep”. The purpose of this script is trying to achieve “Approach Any Player within a Range”.

In this Script contains key functions of:-

  • OnTriggerEnter behaviour to get object in Range.
  • Only “Player” Tag will get to store in a variable called “TargetedPlayer”.
  • Order this Non Player Object(SelfDestruct_Creep) to move towards TargetedPlayer.

Current Issues I’m now facing is:

  • Getting any “Player” object in range its working, but when checking on both of my Server and Client the so called “Targeting Player” function. SelfDestruct_Creep seems to target the different Player on both Server and Client.

Example Scenario:

  • [In Scene]one SelfDestruct_Creep and two Players(Player 1 and Player 2).
  • [Server] SelfDestruct_Creep approach Player 1.
  • [Client] SelfDestruct_Creep approach Player 2.
    Above scenario happens at the same timing. But SelfDestruct_Creep approaches different Player which causes both player DEAD instead of the one.

For what I can think of now is that, I need to tell the client from the server this SelfDestruct_Creep is targeting “Example_Player”. But I do not know how to achieve that on an “Non-Player Object”. So if any of you understand my problem, please do enlighten me. I’ll be appreciate it! Thank you gent! :slight_smile:

Below is the script I have mentioned:

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class ExampleScript : NetworkBehaviour
{
    public float Speed = 5f;
    public GameObject TargetedPlayer;

    void Update()
    {
        if(TargetedPlayer!=null)
        {
            Movement(TargetedPlayer);
        }
        else
        {
            Destroy(this.gameObject);
        }
    }
     
    public void Movement(GameObject _TargetedPlayer)
    {
        transform.LookAt(_TargetedPlayer.transform.position);
        transform.Rotate(new Vector3(0,-90,0),Space.Self);
        if (Vector3.Distance(transform.position,_TargetedPlayer.transform.position)> 0f)
        {
            transform.Translate(new Vector3(Speed*2* Time.deltaTime,0,0));
        } 
    }
     
    void OnTriggerEnter2D (Collider2D col)
    {
        if(col.gameObject.tag == "Player")
        {
            TargetedPlayer = col.gameObject;
        }
    }
}

Assuming your players have NetworkIdentities, you probably want to add a SyncVar holding the target’s netId. You can add a hook function to the SyncVar to update TargetedPlayer on the client whenever the SyncVar changes on the server. Then you just need to make sure only the server changes the SyncVar and TargetedPlayer.

SyncVars are explained at http://docs.unity3d.com/Manual/UNetStateSync.html.

1 Like

@JirinCrawford Hey budds! Thanks for your reply. From your suggested solution, I have derived my latest codes.

[Applied in Script]

  • Using networkIdentitties to get the NetId is working.
  • Search for the Player’ GameObject using NetId is working. However its only On the server, but not the client…again :frowning:

I’m using SyncVar Hook but it does not sync to the client. May I know why…? :slight_smile:
This Non-Player-Obj really kills me :frowning:

Below is the latest code Im working on. If you understand what Im missing please enlighten me :slight_smile:

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class ExampleScriptForSelfDestructCreep : NetworkBehaviour
{
    public float Speed = 5f;
    public GameObject TargetedPlayer;
    [SyncVar(hook = "Movement")]
    private NetworkInstanceId TargetedId;

    void Update()
    {
        if(TargetedPlayer!=null)
        {
            if(TargetedId != null)
            {
                Movement(TargetedId);   
            }
        }
        else
        {
            Destroy(this.gameObject);
        }
    }


    public void Movement(NetworkInstanceId _TargetedId)
    {
        GameObject _TargetedPlayer = NetworkServer.FindLocalObject(_TargetedId); // Suspect this function on invoke on server not on client?
        Debug.Log("Targeted Player =" + _TargetedPlayer);
        transform.LookAt(_TargetedPlayer.transform.position);
        transform.Rotate(new Vector3(0,-90,0),Space.Self);
        if (Vector3.Distance(transform.position,_TargetedPlayer.transform.position)> 0f)
        {
            transform.Translate(new Vector3(Speed*2* Time.deltaTime,0,0)); 
        }   
    }

    void OnTriggerEnter2D (Collider2D col)
    {
        if(col.gameObject.tag == "Player")
        {
            TargetedPlayer = col.gameObject;
            TargetedId = TargetedPlayer.GetComponent<NetworkIdentity>().netId;

            Movement(TargetedId);
        }
    }
}

You’re on the right track. As you’ve noticed, NetworkServer.FindLocalObject doesn’t work on clients. You need to use ClientScene.FindLocalObject instead.

At the moment you’re calling Movement when your SyncVar changes. You’d probably be better off creating a different function which just finds the player and stores it in TargetedPlayer instead (something like OnTargetedIdChange) to use as your hook function. That way you’d only need to look up TargetedPlayer once each time TargetedID changes, instead of every time you move.

OnTriggerEnter2D can happen on both the client and the server at the moment, but you probably want it to only happen on the server (otherwise the client could overwrite values it gets from the server). You can do this by checking that this.isServer is true at the start of OnTriggerEnter2D before doing anything.

Hope that helps!

1 Like

@JirinCrawford I followed your solutions, which I think might reasonable to make it work but eventually I still missing some minor thing. Let me break it down.

[Applied in Script]

  • Split Movement Function and GetTarget Function.
  • SyncVar Hook on NetworkInstanceId to GetTarget Function.
  • OnTriggerEnter2D will only happen on Server. (This cause client doesn’t get this information but I learnt that we can use Hook, but I dont understand why it doesn’t work?)

[Things I’ve Tried]

  • Tried to call synchronization on “void Start”. (Failed)
  • Changing Network Identity of Selfdestruct to “Server Only” from “Local Player Auth”. (Currently remain untick for both option). (Failed)

Current issue is that I still couldn’t manage to send information like " i.e. Telling Client that this (Non-PlayerObj)Selfdestruct Creep is chasing Player 1 only." Which I checked from client that, the client is unable to spot this Selfdestruct Creep is making any movement, because it does not have TargetedPlayer and TargetedId(which im still trying to Sync from Server to client). I think it could be related to making this NonPlayerObj as Server Only? Let me know what do you think :). Or if any viewer understand my issue please do enlighten me. Appreciate your time with me;)

Below is my latest Script(still keep trying!! Its worth it!!)

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class Example_SelfDestructCreep : NetworkBehaviour
{
    #region Variables
    public float Speed = 5f;

    public GameObject TargetedPlayer;

    [SyncVar(hook = "GetTarget")] // Somehow this hook doesnt send to other client when this SelfDestruct_Creep got it's target.
    public NetworkInstanceId TargetedId;
    #endregion

    void Update()
    {
        if(TargetedPlayer!=null)
        {
            Movement(TargetedPlayer);
        }
        else
        {
            return;
        }
    }

    void GetTarget(NetworkInstanceId _TargetedId) // Getting Targeted Player
    {
        GameObject _TargetedPlayer = ClientScene.FindLocalObject(_TargetedId);

        Movement(_TargetedPlayer);
    }

    public void Movement(GameObject _TargetedPlayer) // Movement
    {
        transform.LookAt(_TargetedPlayer.transform.position);
        transform.Rotate(new Vector3(0,-90,0),Space.Self);
        if (Vector3.Distance(transform.position,_TargetedPlayer.transform.position)> 0f)
        {
            transform.Translate(new Vector3(Speed*2* Time.deltaTime,0,0));
        }   
    }

    void OnTriggerEnter2D (Collider2D col)
    {
        if(isServer) // Passing Details only on Server but Not Client
        {
            if(col.gameObject.tag == "Player")
            {
                Debug.LogError("Got a Player");
                TargetedPlayer = col.gameObject;

                TargetedId = TargetedPlayer.GetComponent<NetworkIdentity>().netId;

                GetTarget(TargetedId);
            }       
        }
    }
}

[SOLVED] - Solution

  • Replace _TargetedPlayer with TargetedPlayer.
  • Remove TargetedPlayer’s line under void OnTriggerEnter2D.
    [Reason]
  • If TargetedPlayer variable assign under OnTriggerEnter2D, this will cause only targeted gameobject pass to the Server and it does not pass to the client(this is okay). But previous code TargetedPlayer variable clashed with _TargetedPlayer under void GetTarget functions, it keeps overriding the Network Synchronizing NetworkInstanceId from the Update function. So we must only pass NetworkInstanceId from OnTriggerEnter2D not the Targeted GameObject itself. Later on, then we only get the targeted gameobject under GetTarget function using NetworkInstanceId which got from OnTriggerEnter2D function.