Help needed, following Unity multiplayer Health-Bar tutorial. Only working on Server Host instance of the game

by Super MegaBroBro   Last Updated September 14, 2017 16:13 PM - source

was really hoping someone can be good enough to look over my code, I've been trying to fix it for several days. I followed the Unity tutorial which is fairly basic but the bit that confuses me (and potentially where the problem lies) is the:

 if (!server){return;}

part at the top of my TakeDamage() function.

The problem im seeing is that once I get to that part of the tutorial, the bullet fired by the Host is registered against the other player and I can see his health has gone down (great so far!) , but when I load the instance in the editor (i did running another standalone on my laptop and connect to internet matchmaking but the same thing occurs) his bullets are not registered (even though my debug log tells me the Ray did hit the player.

The tutorial tells me to make it return if not server, it then uses [SyncVar] and a hook that is supposed to call the method "OnChangeHealth" whenever currentHealth is changed. This DOES NOT appear to be happening, and the TakeDamage code is only happening on the Server-Client-Host instance.

I was trying to adapt the tutorial to my own game, so there are some slight differences in the function and class names, but to me it looks to be exactly the same functionality-wise. I will have to just post my full code here as I don't know which part is causing the issue:

(MANY THANKS FOR READING AND/OR HELPING):

My Player Class:

public class Player : NetworkBehaviour {
private GameObject startScreen;
public const int maxHealth = 100;
[SyncVar(hook = "OnChangeHealth")] public int currentHealth = maxHealth;
private float playerHeight;
private AudioSource audioSource;
public AudioClip painBodySound;
public AudioClip painHeadSound;
public AudioClip painHeadSound2;
public RectTransform healthBar;


void Start () {
    startScreen = GameObject.FindGameObjectWithTag("StartScreen");
    if (startScreen != null)
    {
        startScreen.SetActive(false);
    }
    playerHeight = GetComponent<Collider>().bounds.size.y;
    currentHealth = maxHealth;
    audioSource = GetComponent<AudioSource>();
}

void Update () {

    if (!isLocalPlayer)
    {
        return;
    }
    if (Input.GetKey(KeyCode.LeftControl))
    {
        transform.localScale = new Vector3(1, 0.5f, 1);
    }
    else
    {
        transform.localScale = new Vector3(1, 1f, 1);
    }
}

public void TakeDamage(Vector3 hitPoint, int bulletDamage)
{
    // check that its Server so that damage only gets applied on the server
    if (!isServer)
    {
        return;
    }
    currentHealth -= bulletDamage;
    audioSource.PlayOneShot(painBodySound);
    if (currentHealth <= 0)
    {
        currentHealth = 0;
        Debug.Log("Dead!");
    }
}



private void OnChangeHealth(int health)
{
    healthBar.sizeDelta = new Vector2(health, healthBar.sizeDelta.y);
}

}

The Bullet class:

public class Bullet : MonoBehaviour {
[HideInInspector] public UnityStandardAssets.Characters.FirstPerson.RigidbodyFirstPersonController rbCont;
[HideInInspector] public int bulletDamage;
public float bulletLifespan = 5f;
private float bulletLifetime = 0f;

private AudioSource audioSource;
public AudioClip bulletFleshSound;

private Ray ray;
private RaycastHit hit;
private Vector3 bulletSpawnPos, bulletSpawnDirection;
public float bulletRayDistance = 100f;



// Use this for initialization
void Start () {
    rbCont = GameObject.FindGameObjectWithTag("LocalPlayer").GetComponent<UnityStandardAssets.Characters.FirstPerson.RigidbodyFirstPersonController>();
    bulletDamage = (int)rbCont.bulletDamage;
    bulletSpawnPos = rbCont.bulletSpawnPoint.transform.position;
    bulletSpawnDirection = rbCont.bulletSpawnPoint.transform.forward;
}

private void Update()
{
    bulletLifetime += Time.deltaTime;
    if (bulletLifetime >= bulletLifespan)
    {
        Destroy(gameObject);
    }

    CastBulletRay();
}

private void CastBulletRay()
{
    ray = new Ray(bulletSpawnPos, bulletSpawnDirection);
    if (Physics.Raycast(ray, out hit, bulletRayDistance))
    {
        if (hit.collider.tag == "Baddie")
        {
            Debug.Log("ray has hit a baddie");
            Destroy(gameObject);
        }
        if (hit.collider.tag == "Player" || hit.collider.tag == "LocalPlayer")
        {
            Debug.Log("ray has hit a player");
            hit.collider.gameObject.GetComponent<Player>().TakeDamage(hit.point, bulletDamage);
            Destroy(gameObject);
        }

    }
}
}

Unity Standard RigidbodyFirstPersonController (which i've made small additions to):

namespace UnityStandardAssets.Characters.FirstPerson

{ [RequireComponent(typeof (Rigidbody))] [RequireComponent(typeof (CapsuleCollider))] public class RigidbodyFirstPersonController : NetworkBehaviour {

    [Serializable]
    public class MovementSettings
    {
        public float ForwardSpeed = 8.0f;   // Speed when walking forward
        public float BackwardSpeed = 4.0f;  // Speed when walking backwards
        public float StrafeSpeed = 4.0f;    // Speed when walking sideways
        public float RunMultiplier = 2.0f;   // Speed when sprinting
        public KeyCode RunKey = KeyCode.LeftShift;
        public float JumpForce = 30f;
        public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f));
        [HideInInspector] public float CurrentTargetSpeed = 8f;

if !MOBILE_INPUT

        private bool m_Running;

endif

        public void UpdateDesiredTargetSpeed(Vector2 input)
        {
            if (input == Vector2.zero) return;
            if (input.x > 0 || input.x < 0)
            {
                //strafe
                CurrentTargetSpeed = StrafeSpeed;
            }
            if (input.y < 0)
            {
                //backwards
                CurrentTargetSpeed = BackwardSpeed;
            }
            if (input.y > 0)
            {
                //forwards
                //handled last as if strafing and moving forward at the same time forwards speed should take precedence
                CurrentTargetSpeed = ForwardSpeed;
            }

if !MOBILE_INPUT

            if (Input.GetKey(RunKey))
            {
                CurrentTargetSpeed *= RunMultiplier;
                m_Running = true;
            }
            else
            {
                m_Running = false;
            }

endif

        }

if !MOBILE_INPUT

        public bool Running
        {
            get { return m_Running; }
        }

endif

    }


    [Serializable]
    public class AdvancedSettings
    {
        public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this )
        public float stickToGroundHelperDistance = 0.5f; // stops the character
        public float slowDownRate = 20f; // rate at which the controller comes to a stop when there is no input
        public bool airControl; // can the user control the direction that is being moved in the air
        [Tooltip("set it to 0.1 or more if you get stuck in wall")]
        public float shellOffset; //reduce the radius by that ratio to avoid getting stuck in wall (a value of 0.1f is nice)
    }


    public Camera cam;
    public MovementSettings movementSettings = new MovementSettings();
    public MouseLook mouseLook = new MouseLook();
    public AdvancedSettings advancedSettings = new AdvancedSettings();

    //my added variables:
    public GameObject bulletPrefab;
    public GameObject bulletSpawnPoint;
    public float bulletFireRate;
    float bulletFireTimer = 999f;
    public float bulletDamage;
    public GameObject muzzleFlashObject;


    private Rigidbody m_RigidBody;
    private CapsuleCollider m_Capsule;
    private float m_YRotation;
    private Vector3 m_GroundContactNormal;
    private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded;


    private Camera camComponent;
    private AudioListener audioListenerComponent;
    private AudioSource audioSource;
    public AudioClip gunFireSound;

    [HideInInspector] public bool isLocal;

    public Vector3 Velocity
    {
        get { return m_RigidBody.velocity; }
    }

    public bool Grounded
    {
        get { return m_IsGrounded; }
    }

    public bool Jumping
    {
        get { return m_Jumping; }
    }

    public bool Running
    {
        get
        {

#if !MOBILE_INPUT return movementSettings.Running;

else

            return false;

endif

        }
    }

    private void Awake(){
        camComponent = cam.GetComponent<Camera> ();
        audioListenerComponent = cam.GetComponent<AudioListener> ();
        audioSource = GetComponent<AudioSource>();


    }

    private void Start()
    {

        if (!isLocalPlayer) {
            camComponent.enabled = false;
            audioListenerComponent.enabled = false;
            return;
        }

        m_RigidBody = GetComponent<Rigidbody>();
        m_Capsule = GetComponent<CapsuleCollider>();
        mouseLook.Init (transform, cam.transform);

    }


    private void Update()
    {
        muzzleFlashObject.SetActive(false);
        isLocal = isLocalPlayer;
        if (!isLocalPlayer)
        {
            return;
        }
        tag = "LocalPlayer";

        RotateView();

        if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump)
        {
            m_Jump = true;
        }

        muzzleFlashObject.SetActive(false);
        if (Input.GetMouseButton(0))
        {
            bulletFireTimer += Time.deltaTime;
            if (bulletFireTimer >= bulletFireRate)
            {
                CmdFireBullets();
                bulletFireTimer = 0f;
            }
        }
    }


    private void FixedUpdate()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        GroundCheck();
        Vector2 input = GetInput();

        if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded))
        {
            // always move along the camera forward as it is the direction that it being aimed at
            Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x;
            desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized;

            desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed;
            desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed;
            desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed;
            if (m_RigidBody.velocity.sqrMagnitude <
                (movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed))
            {
                m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse);
            }
        }

        if (m_IsGrounded)
        {
            m_RigidBody.drag = 5f;

            if (m_Jump)
            {
                m_RigidBody.drag = 0f;
                m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z);
                m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse);
                m_Jumping = true;
            }

            if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f)
            {
                m_RigidBody.Sleep();
            }
        }
        else
        {
            m_RigidBody.drag = 0f;
            if (m_PreviouslyGrounded && !m_Jumping)
            {
                StickToGroundHelper();
            }
        }
        m_Jump = false;
    }


    private float SlopeMultiplier()
    {
        float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up);
        return movementSettings.SlopeCurveModifier.Evaluate(angle);
    }


    private void StickToGroundHelper()
    {
        RaycastHit hitInfo;
        if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
                               ((m_Capsule.height/2f) - m_Capsule.radius) +
                               advancedSettings.stickToGroundHelperDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
        {
            if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f)
            {
                m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal);
            }
        }
    }


    private Vector2 GetInput()
    {

        Vector2 input = new Vector2
            {
                x = CrossPlatformInputManager.GetAxis("Horizontal"),
                y = CrossPlatformInputManager.GetAxis("Vertical")
            };
        movementSettings.UpdateDesiredTargetSpeed(input);
        return input;
    }


    private void RotateView()
    {
        //avoids the mouse looking if the game is effectively paused
        if (Mathf.Abs(Time.timeScale) < float.Epsilon) return;

        // get the rotation before it's changed
        float oldYRotation = transform.eulerAngles.y;

        mouseLook.LookRotation (transform, cam.transform);

        if (m_IsGrounded || advancedSettings.airControl)
        {
            // Rotate the rigidbody velocity to match the new direction that the character is looking
            Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up);
            m_RigidBody.velocity = velRotation*m_RigidBody.velocity;
        }
    }

    /// sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom
    private void GroundCheck()
    {
        m_PreviouslyGrounded = m_IsGrounded;
        RaycastHit hitInfo;
        if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
                               ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
        {
            m_IsGrounded = true;
            m_GroundContactNormal = hitInfo.normal;
        }
        else
        {
            m_IsGrounded = false;
            m_GroundContactNormal = Vector3.up;
        }
        if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping)
        {
            m_Jumping = false;
        }
    }

    [Command]
    void CmdFireBullets()
    {
        muzzleFlashObject.SetActive(true);
        audioSource.PlayOneShot(gunFireSound, 0.2f);
        //Create a new bullet
        GameObject newBullet = Instantiate(bulletPrefab, bulletSpawnPoint.transform.position, bulletSpawnPoint.transform.rotation) as GameObject;

        //Spawn the bullet on the clients:
        NetworkServer.Spawn(newBullet);
    }
}
}


Related Questions


Gear synchronization in open world multiplayer game

Updated September 06, 2018 11:13 AM



clock synchronization

Updated October 08, 2018 12:13 PM