Jump to content
  • Advertisement

Recommended Posts

I can't seem to get jumping synched. sometimes the remote player jumps too soon and doesn't make it to where the client is at. Here's what I'm trying.. The remote player jumps when he gets close enough to the place where the client pressed the jump button... I'm thinking I should change it to timing based but I haven't been able to figure out how to add that to my code... Any suggestions would be appreciated.

 

Client script:

public class NetMoveNG : MonoBehaviour
{
    float m_moveSpeed;            //Max character move speed
    float m_curSpeed;             //Current character move speed
    float m_jumpForce;
    float m_jumpHeight;
    float m_travDist;
    float m_rX;                   //Character rotation based on mouse input
    byte m_frameSkip;
    bool m_collide;
    Vector3 m_lmdir;              //Last yaw direction, updated every physics tick
    Vector3 m_lp;                 //Last Character position, updated every second
    Vector3 m_prevPhys;
    Vector3 m_curPhys;
    Vector3 m_jumpLoc;

    // Use this for initialization
    void Start()
    {
        m_moveSpeed = 6.0f;
        m_curSpeed = 6.0f;
        m_jumpForce = 0;
        m_jumpHeight = 8f;
        m_travDist = 0;
        m_rX = 0;
        m_frameSkip = 0;
        m_collide = false;
        m_lmdir = Vector3.zero;
        m_lp = Vector3.zero;
        m_curPhys = Vector3.zero;
        m_prevPhys = Vector3.zero;
        m_jumpLoc = new Vector3(9000, 9000, 9000);
    }

    // Update is called once per frame
    void Update()
    {
        float alpha = (Time.time - Time.fixedTime) / Time.fixedDeltaTime;
        Vector3 lerpState = Vector3.Lerp(m_prevPhys, m_curPhys, alpha);
        transform.position = lerpState;
    }
    void FixedUpdate()
    {

        m_rX += Input.GetAxis("Mouse X") * 256.0f * Time.fixedDeltaTime * 5;
        Vector3 mdir = Vector3.zero;
        Vector3 cdir = Vector3.zero;

        if (Input.GetAxis("Vertical") > 0.0f)
        { mdir += transform.TransformDirection(cdir = Vector3.forward); }

        if (Input.GetAxis("Vertical") < 0.0f)
        { mdir += transform.TransformDirection(cdir = Vector3.back); }

        if (Input.GetAxis("Horizontal") > 0.0f)
        { mdir += transform.TransformDirection(cdir = Vector3.right); }

        if (Input.GetAxis("Horizontal") < 0.0f)
        { mdir += transform.TransformDirection(cdir = Vector3.left); }
        mdir.Normalize();

        //transform.rotation = Quaternion.Euler(new Vector3(0, (short)rX, 0));

        if (GetComponent<CharacterController>().isGrounded)
        {
            m_jumpForce = 0;
            if (Input.GetButton("Jump"))
            {
                m_jumpForce = m_jumpHeight;
                SendMessage("SendJumpLoc", m_curPhys);
            }
        }
        m_jumpForce -= 9.8f * Time.fixedDeltaTime * 2.0f;

        if (m_collide || m_travDist >= m_moveSpeed || m_lmdir != cdir || (m_frameSkip==1 && !GetComponent<CharacterController>().isGrounded))
        {
            if (new Vector3(m_curPhys.x, 0, m_curPhys.z) != new Vector3(m_lp.x, 0, m_lp.z))
            SendMessage("SendClientPosition", new Vector3(m_curPhys.x, 0, m_curPhys.z));

            m_lp = new Vector3(m_curPhys.x, 0, m_curPhys.z);

            if (m_travDist >= m_moveSpeed) m_travDist = 0;
        }
        GetComponent<CharacterController>().Move(mdir * m_moveSpeed * Time.fixedDeltaTime);
        GetComponent<CharacterController>().Move(Vector3.up * m_jumpForce * Time.fixedDeltaTime);

        m_travDist += m_curSpeed * Time.deltaTime;

        m_lmdir = cdir;
        m_prevPhys = m_curPhys;
        m_curPhys = transform.position;
        m_frameSkip ^= 1;
    }


    void OnCollisionEnter(Collision clsn)
    {
        if (clsn.gameObject.CompareTag("Collision")) m_collide = true;
    }

    void OnCollisionStay(Collision clsn)
    {
        if (clsn.gameObject.CompareTag("Collision")) m_collide = true;
    }

    void OnCollisionExit(Collision clsn)
    {
        if (clsn.gameObject.CompareTag("Collision")) m_collide = false;
    }
}

Remote movement:

public class ServerPlayer : MonoBehaviour
{
    Vector3 m_inPosition;       //Position received remotely from player
    Vector3 m_jumpLoc;
    Vector3 m_curPhys;
    Vector3 m_prevPhys;

    float m_moveSpeed;
    float m_curSpeed;
    float m_jumpForce;
    float m_jumpHeight;
    uint m_maxTick;
    // Start is called before the first frame update
    void Start()
    {
        m_inPosition = Vector3.zero;
        m_curPhys = Vector3.zero;
        m_prevPhys = Vector3.zero;
        m_jumpLoc = new Vector3(9000, 9000, 9000);
        m_moveSpeed = 6f;
        m_curSpeed = 6f;
        m_jumpForce = 0;
        m_jumpHeight = 8f;
        m_maxTick = 20;
    }

    void FixedUpdate()
    {
        Vector3 dir = new Vector3(m_inPosition.x, 0, m_inPosition.z) - new Vector3(transform.position.x, 0, transform.position.z);
        dir.Normalize();

        if (Vector3.Distance(new Vector3(transform.position.x,0,transform.position.z),new Vector3(m_inPosition.x,0,m_inPosition.z)) < (m_moveSpeed * Time.fixedDeltaTime))
            m_curSpeed = Vector3.Distance(new Vector3(transform.position.x, 0, transform.position.z), new Vector3(m_inPosition.x, 0, m_inPosition.z)) * m_maxTick;
        else
            m_curSpeed = m_moveSpeed;

        m_jumpForce -= 9.8f * Time.fixedDeltaTime * 2.0f;
        if (Vector3.Distance(m_curPhys, m_jumpLoc) <= ((m_moveSpeed*2f) * Time.fixedDeltaTime) && GetComponent<CharacterController>().isGrounded) { m_jumpForce = m_jumpHeight; m_jumpLoc = new Vector3(9000, 9000, 9000); }

        GetComponent<CharacterController>().Move(dir * m_curSpeed * Time.fixedDeltaTime);
        GetComponent<CharacterController>().Move(Vector3.up * m_jumpForce * Time.fixedDeltaTime);

        m_prevPhys = m_curPhys;
        m_curPhys = transform.position;
     
    }

    public void SetInPosition(Vector3 pos) { m_inPosition = pos; }
    public void SetJumpLocation(Vector3 jmploc) {m_jumpLoc = jmploc; }

    // Update is called once per frame
    void Update()
    {
        float alpha = (Time.time - Time.fixedTime) / Time.fixedDeltaTime;
        Vector3 lerpState = Vector3.Lerp(m_prevPhys, m_curPhys, alpha);
        transform.position = lerpState;
    }
}

 

 

Share this post


Link to post
Share on other sites
Advertisement

You'll need to count each simulation step (or perhaps each simulation millisecond if you use variable time step.)

Then, tag the "jump" command with the step-number (or time-since-start) when it happens.

Play remote players back "behind actual time" by whatever the transmission delay from server to your client is.

Send commands "ahead of time" by whatever the transmission delay from client to server is.

This will end up with the somewhat-standard "everyone else is behind and you are locally ahead" simulation model, that is a good starting point for them implementing optimizations and work-arounds and interpolations of various kinds.

Share this post


Link to post
Share on other sites

I switched to a fixed time step to simplify things. I'm having another problem now though... sometimes when my remote player starts moving from a stopped position there is a slight stop after taking a step. Not sure what to do to fix that. It's not that noticeable but still annoying.

Share this post


Link to post
Share on other sites
Posted (edited)

Came up with a solution to both problems that seem to work... Here's latest code:

 

Client:

public class NetMoveNG : MonoBehaviour
{
    float m_moveSpeed;              //Max character move speed
    float m_jumpForce;              //Current jump force
    float m_jumpHeight;             //Max jump height             
    float m_rX;                     //Character rotation based on mouse input

    byte m_frameSkip;               //Used for sending the players x,z position, flips between 1 and 0 everyframe and sends x,z position at 1
    byte m_jumpFrame;               //Set to 0 when the player presses jump and accumulates each frame until it reaches m_jumpDelay
    byte m_jumpDelay;               //Determines how many frames pass before jump message is sent to server
    bool m_jumped;                  //Set to true when player presses jump in Update
    
    Vector3 m_prevPhys;             //Previous position set in Fixedupdate, used to lerp in Update  
    Vector3 m_curPhys;              //Current position set in FixedUpate, used to lerp in Update

    // Use this for initialization
    void Start()
    {
        m_moveSpeed = 6.0f;
        m_jumpForce = 0;
        m_jumpHeight = 8f;
        m_rX = 0;
        m_frameSkip = 0;
        m_jumpFrame = 5;
        m_jumpDelay = 4;
        m_jumped = false;
        m_curPhys = Vector3.zero;
        m_prevPhys = Vector3.zero;
    }

    // Update is called once per frame
    void Update()
    {
        float alpha = (Time.time - Time.fixedTime) / Time.fixedDeltaTime;
        Vector3 lerpState = Vector3.Lerp(m_prevPhys, m_curPhys, alpha);
        transform.position = lerpState;

        if(GetComponent<CharacterController>().isGrounded)
            if (Input.GetButton("Jump")) m_jumped = true;
    }
    void FixedUpdate()
    {
        m_rX += Input.GetAxis("Mouse X") * 256.0f * Time.fixedDeltaTime * 5;
        Vector3 mdir = Vector3.zero;

        if (Input.GetAxis("Vertical") > 0.0f)
        { mdir += transform.TransformDirection(Vector3.forward); }

        if (Input.GetAxis("Vertical") < 0.0f)
        { mdir += transform.TransformDirection(Vector3.back); }

        if (Input.GetAxis("Horizontal") > 0.0f)
        { mdir += transform.TransformDirection(Vector3.right);  }

        if (Input.GetAxis("Horizontal") < 0.0f)
        { mdir += transform.TransformDirection(Vector3.left);}
        mdir.Normalize();

        if (GetComponent<CharacterController>().isGrounded)
        {
            m_jumpForce = 0;
            if(m_jumped)
            {
                m_jumpForce = m_jumpHeight;
                m_jumpFrame = 0;
                m_jumped = false;
            }
        }

        if (m_frameSkip == 1)
            SendMessage("SendClientPosition", new Vector3(m_curPhys.x, 0, m_curPhys.z));

        GetComponent<CharacterController>().Move(mdir * m_moveSpeed * Time.fixedDeltaTime);
        GetComponent<CharacterController>().Move(Vector3.up * m_jumpForce * Time.fixedDeltaTime);

        m_jumpForce -= 9.8f * Time.fixedDeltaTime * 2.0f;
        if (m_jumpFrame == m_jumpDelay) { SendMessage("SendJumpLoc", m_prevPhys); m_jumpFrame = 5; }
        else
            m_jumpFrame++;

        if (m_jumpFrame > 5) m_jumpFrame = 5;
      
        m_prevPhys = m_curPhys;
        m_curPhys = transform.position;
        m_frameSkip ^= 1;
    }
}

Remote:

public class ServerPlayer : MonoBehaviour
{
    Vector3 m_inPosition;               //Position received remotely from player  
    Vector3 m_lastInPos;                //Previous position received from player
    Vector3 m_curPhys;                  //Current position set in FixedUpate, used to lerp in Update
    Vector3 m_prevPhys;                 //Previous position set in Fixedupdate, used to lerp in Update  

    float m_moveSpeed;                  //Max player speed
    float m_curSpeed;                   //Current player speed
    float m_jumpForce;                  //Current jump force, set to m_jumpHeight when player presses jump and decreases over time
    float m_jumpHeight;                 //Max player jump height
    float m_maxTick;                    //Maximum physics frames in a second                

    bool m_shouldJump;                  //Set to true when receiving a jump command from the player
    bool m_isStopped;                   //Used to control movement when the player shouldn't move at m_moveSpeed

    // Start is called before the first frame update
    void Start()
    {
        m_inPosition = Vector3.zero;
        m_curPhys = Vector3.zero;
        m_prevPhys = Vector3.zero;
        m_moveSpeed = 6f;
        m_curSpeed = 6f;
        m_jumpForce = 0;
        m_jumpHeight = 9f;
        m_maxTick = 10f;
        m_shouldJump = false;
        m_isStopped = true;
    }

    void FixedUpdate()
    {
        Vector3 dir = new Vector3(m_inPosition.x, 0, m_inPosition.z) - new Vector3(transform.position.x, 0, transform.position.z);
        dir.Normalize();

        if (Vector3.Distance(new Vector3(m_curPhys.x, 0, m_curPhys.z), new Vector3(m_lastInPos.x, 0, m_lastInPos.z)) <= (m_maxTick * Time.fixedDeltaTime))
        {
            if(m_isStopped == false)
                m_curSpeed = Vector3.Distance(new Vector3(m_curPhys.x, 0, m_curPhys.z), new Vector3(m_lastInPos.x, 0, m_lastInPos.z))*m_maxTick;
            else
                m_curSpeed = Vector3.Distance(new Vector3(m_curPhys.x, 0, m_curPhys.z), new Vector3(m_lastInPos.x, 0, m_lastInPos.z));
        }
        else
        { m_curSpeed = m_moveSpeed; }

        if (m_curSpeed >= m_moveSpeed) m_curSpeed = m_moveSpeed;

        m_jumpForce -= 9.8f * Time.fixedDeltaTime * 2.0f;

        if (m_shouldJump && GetComponent<CharacterController>().isGrounded) { m_jumpForce = m_jumpHeight;m_shouldJump = false; }

        GetComponent<CharacterController>().Move(dir * m_curSpeed * Time.fixedDeltaTime);
        GetComponent<CharacterController>().Move(Vector3.up * m_jumpForce * Time.fixedDeltaTime);

        m_prevPhys = m_curPhys;
        m_curPhys = transform.position;

        if (m_curPhys == m_prevPhys) m_isStopped = true;
        else m_isStopped = false;
    }

    public void SetInPosition(Vector3 pos) { m_lastInPos = m_inPosition; m_inPosition = pos; }
    public void SetShouldJump(bool sj) { m_shouldJump = sj; }

    // Update is called once per frame
    void Update()
    {

        float alpha = (Time.time - Time.fixedTime) / Time.fixedDeltaTime;
        Vector3 lerpState = Vector3.Lerp(m_prevPhys, m_curPhys, alpha);
        transform.position = lerpState;
    }
}

 

Edited by Turbo14

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!