Exemple #1
0
 /// <summary>
 /// Grounds the player, resets all the gliding/flying/boosting states and player gravity
 /// </summary>
 private void Ground()
 {
     if (flying && onFlightEnd)
     {
         onFlightEnd.SendCustomEvent(onFlightEndEvent);
     }
     if (gliding && onGlideEnd)
     {
         onGlideEnd.SendCustomEvent(onGlideEndEvent);
     }
     flying   = false;
     boosting = false;
     gliding  = false;
     boostEnd = 0f;
     rSpeed   = 0f;
     player.SetGravityStrength();
 }
 private void SendCustomEventToTarget(string eventName)
 {
     if (sentToOwner)
     {
         eventTarget.SendCustomNetworkEvent(NetworkEventTarget.Owner, eventName);
     }
     else
     {
         eventTarget.SendCustomEvent(eventName);
     }
 }
 private void Trigger(UdonSharpBehaviour target, string eventName)
 {
     if (target)
     {
         target.SendCustomEvent(eventName);
     }
     if (audioSource && audioClip)
     {
         audioSource.PlayOneShot(audioClip);
     }
 }
Exemple #4
0
 public override void OnPickupUseDown()
 {
     gun.Fire();
     if (onFireTarget != null)
     {
         onFireTarget.SendCustomEvent(onFireEvent);
     }
     if (onReadyTarget != null)
     {
         onReadyTarget.SendCustomEventDelayedSeconds(onReadyEvent, gun.GetIntervalSeconds());
     }
 }
        public void RenderAllProbes()
        {
            foreach (var probe in probes)
            {
                probe.RenderProbe();
            }

            if (eventTarget == null)
            {
                return;
            }
            eventTarget.SendCustomEvent(eventName);
        }
 public void CallEvent()
 {
     spawnerBehaviour.SendCustomEvent("SpawnEvent");
 }
    void _u_ReceiveResponse()
    {
        responsesReceived++;

        // Requies convention public variable and event names
        UdonSharpBehaviour usb = connectionRequesters[currentID];

        if (connectionIsWebSocket[currentID])
        {
            // First byte is the text/binary flag, with highest bit indiciating
            // that this is a dummy 'connection closed' response.  Second highest
            // bit indicates a websocket opened message.
            if ((connectionData[currentID][0] & 0x40) == 0x40)
            {
                usb.SetProgramVariable("connectionID", currentID);
                usb.SendCustomEvent("_u_WebSocketOpened");
            }
            else if ((connectionData[currentID][0] & 0x80) == 0x80)
            {
                usb.SetProgramVariable("connectionID", currentID);
                usb.SendCustomEvent("_u_WebSocketClosed");
                connectionRequesters[currentID] = null;
                connectionsOpen--;
                _u_SendCallback("_u_OnUdonMIDIWebHandlerConnectionCountChanged");
            }
            else
            {
                usb.SetProgramVariable("connectionID", currentID);
                bool   messageIsText = (connectionData[currentID][0] & 0x1) == 0x0;
                byte[] messsageData  = new byte[connectionData[currentID].Length - 1];
                Array.Copy(connectionData[currentID], 1, messsageData, 0, connectionData[currentID].Length - 1);
                usb.SetProgramVariable("messageIsText", messageIsText);
                if (messageIsText && connectionReturnsStrings[currentID])
                {
                    usb.SetProgramVariable("connectionString", new string(connectionDataChars[currentID]));
                }
                else
                {
                    usb.SetProgramVariable("connectionData", messsageData);
                }
                usb.SendCustomEvent("_u_WebSocketReceive");
            }
        }
        else
        {
            // Non-websocket response
            // First 4 bytes of data are an int for HTTP response code or request error code
            int    responseCode = _u_BitConverterToInt32(connectionData[currentID], 0);
            byte[] responseData = new byte[connectionData[currentID].Length - 4];
            Array.Copy(connectionData[currentID], 4, responseData, 0, connectionData[currentID].Length - 4);
            // Process loopback connection
            if (currentID == LOOPBACK_CONNECTION_ID)
            {
                string connectionString = new string(connectionDataChars[currentID]);
                // responseCode determines what kind of message it is, rather than typical HTTP
                // or extra Udon handler response codes.
                if (responseCode == 0) // ping response
                {
                    if (online == false)
                    {
                        online = true;
                        playersOnline++;
                        _u_SendCallback("_u_OnUdonMIDIWebHandlerOnlineChanged");
                    }
                    awaitingPong            = false;
                    framesSinceAwaitingPong = 0;
                    string[] split = connectionString.Split(' ');
                    queuedResponsesCount = Int32.Parse(split[0]);
                    queuedBytesCount     = Int32.Parse(split[1]);
                }
                else if (responseCode == 1) // avatar change
                {
                    string[] split = connectionString.Split('\n');
                    _u_SendAvatarChangedCallback(split[0], split[1]);
                }
            }
            else
            {
                usb.SetProgramVariable("connectionID", currentID);
                usb.SetProgramVariable("responseCode", responseCode);
                if (connectionReturnsStrings[currentID])
                {
                    usb.SetProgramVariable("connectionString", new string(connectionDataChars[currentID]));
                }
                else
                {
                    usb.SetProgramVariable("connectionData", responseData);
                }
                usb.SendCustomEvent("_u_WebRequestReceived");
                connectionRequesters[currentID] = null;
                connectionsOpen--;
                _u_SendCallback("_u_OnUdonMIDIWebHandlerConnectionCountChanged");
            }
        }

        // Reset response arary
        connectionData[currentID]        = null;
        connectionDataOffsets[currentID] = 0;
    }
    void Update()
    {
        secondsSinceLastPing += Time.deltaTime;
        if (secondsSinceLastPing > PING_INTERVAL_SECONDS)
        {
            // Ping the helper program using a dedicated command, on a dedicated connection ID.
            // The loopback response from this has priority over all 255 other connections.
            // This is done on a dedicated connectionID to ensure it is received like all other
            // MIDI frames, and to make sure there's always a free, prioritized line for auxiliary data.
            Debug.Log("[Udon-MIDI-Web-Helper] PING");
            commandsSent++;
            secondsSinceLastPing = 0;
            awaitingPong         = true;
        }

        if (awaitingPong)
        {
            // Measure time since ping response in number of frames, as the response should be
            // nearly instantaneous.  VRChat may lag and drop connection for a while, but the
            // helper program never should.  If VRChat is running successfully and pong isn't
            // received, its safe to assume the connection is dead.
            if (++framesSinceAwaitingPong > PONG_TIMEOUT_FRAMES && online)
            {
                online = false;
                playersOnline--;
                _u_SendCallback("_u_OnUdonMIDIWebHandlerOnlineChanged");
                // If the handler goes offline, there is no hope of reconnecting until restart.
                // Cancel/close all open connections with a failure response code.
                for (int i = 0; i < MAX_USABLE_CONNECTIONS; i++)
                {
                    if (connectionRequesters[i] != null)
                    {
                        UdonSharpBehaviour usb = connectionRequesters[i];
                        usb.SetProgramVariable("connectionID", i);
                        if (connectionIsWebSocket[i])
                        {
                            usb.SendCustomEvent("_u_WebSocketClosed");
                        }
                        else
                        {
                            usb.SetProgramVariable("responseCode", RESPONSE_CODE_DISCONNECTED);
                            usb.SendCustomEvent("_u_WebRequestReceived");
                        }
                        connectionRequesters[i] = null;
                        connectionsOpen--;
                        _u_SendCallback("_u_OnUdonMIDIWebHandlerConnectionCountChanged");
                    }
                }
            }
        }

        // VRChat's update order for Udon appears to go: Update(), LateUpdate(), MidiNoteX(), at least
        // with VRC Midi Listener after Udon Behaviour on the same gameobject.
        // Therefore, it should be safe to print a READY or ACK each frame without waiting a game tick.
        // (This whole protocol is built around VRChat crashing if more than 1 midi frame of data is recevied per game tick)
        // Hopefully this means the Log-MIDI communication is fast enough to send a new frame
        // before the game loops around again, ideally resulting in one MIDI frame every game tick.
        if (currentFrameOffset == usableBytesThisFrame)
        {
            Debug.Log("[Udon-MIDI-Web-Helper] ACK");
            currentFrameOffset    = 0;
            secondsSinceLastReady = 0; // Don't send a ready, ACK doubles as a ready
        }

        secondsSinceLastReady += Time.deltaTime;
        if (secondsSinceLastReady > READY_TIMEOUT_SECONDS)
        {
            // Only send a RDY exactly READY_TIMEOUT_SECONDS after at least one connection was opened
            Debug.Log("[Udon-MIDI-Web-Helper] READY"); // Ready message lets the helper know its safe to send a new frame OR if a frame was dropped and never received
            secondsSinceLastReady = 0;
        }
    }
Exemple #9
0
    /// <summary>
    /// Calculates corrected forward flight velocity
    /// </summary>
    /// <param name="handDirection">Combined left + right hand forward vector</param>
    /// <param name="lerpedSpeed">Target Speed</param>
    /// <returns></returns>
    private Vector3 ForwardFlight(Vector3 handDirection, float lerpedSpeed)
    {
        var groundDistance = GetGroundDistance();

        rAltitude = groundDistance;
        // skip slowdown or glide when we're more than 10 units above the ground
        if (!(groundDistance <= 10))
        {
            return(handDirection * lerpedSpeed);
        }

        // rest of the code is Glide and takeoff / Land only

        var upDot = Vector3.Dot(handDirection, head.up);

        if (allowGliding && IsGliding(groundDistance, upDot))
        {
            if (!gliding)
            {
                gliding = true;
                if (onGlideStart)
                {
                    onGlideStart.SendCustomEvent(onGlideStartEvent);
                }
            }
            // gliding keeps the player ~1 unit above the ground
            var correctionDistance = 1f - groundDistance;

            // desktop players have issues with slopes as they can't move arms independently
            // so we provide extra slope checks to maintain their speed along slopes
            if (isDesktop)
            {
                var groundAngle = CheckGroundAngle();
                handDirection = Quaternion.AngleAxis(groundAngle, head.right) * handDirection;
            }

            // for VR we just correct the players vertically and leave the rest for the player to handle
            handDirection = new Vector3(handDirection.x, correctionDistance, handDirection.z);
            return(handDirection * lerpedSpeed);
        }

        if (gliding)
        {
            gliding = false;
            if (onGlideEnd)
            {
                onGlideEnd.SendCustomEvent(onGlideEndEvent);
            }
        }

        // takeoff and Landing

        // we make sure that the landing and takeoff speed doesnt fall below 20%
        // then we scale player's speed cap over 10 units above the ground collider for smooth takeoff / landing
        var remapped = Mathf.Max(minGroundSpeedModifier, groundDistance / groundSlowMaxHeight);

        // only slow us down when going above 20% speed
        // this makes sure the minimum speed is maintained at all times unless the player wants to stop
        if (lerpedSpeed / maxSpeed > minGroundSpeedModifier)
        {
            lerpedSpeed = Mathf.Min(lerpedSpeed, maxSpeed * remapped);
        }
        return(handDirection * lerpedSpeed);
    }
Exemple #10
0
    private void Update()
    {
        CheckBoost();
        // check if we entered flight restriction zone
        if (flightRestricted)
        {
            Ground();
            return;
        }
        if (player.IsPlayerGrounded())
        {
            Ground();
            return;
        }

        if (!flying && onFlightStart)
        {
            onFlightStart.SendCustomEvent(onFlightStartEvent);
        }

        flying = true;
        // VRChat has weird inconsistencies when setting gravity to 0 and keeps you floating down
        player.SetGravityStrength(0.0001f);

        var leftForward  = leftHand.forward;
        var rightForward = rightHand.forward;
        var headForward  = head.forward;
        var currVelocity = player.GetVelocity();

        // use hands dot product to control forward speed
        handsDot = Vector3.Dot(leftForward, rightForward);
        // remap from -1 1 to 0 1
        handsDot = (handsDot + 1f) / 2f;
        var currSpeed = currVelocity.magnitude;

        // use the trigger press to control speed in one handed mode
        if (oneHanded)
        {
            handsDot = 1 - Mathf.Max(Input.GetAxisRaw("Oculus_CrossPlatform_PrimaryIndexTrigger"),
                                     Input.GetAxisRaw("Oculus_CrossPlatform_SecondaryIndexTrigger"));
        }
        // determine target speed as current dot product (how much hands are aligned) * current maximum
        var newSpeed = handsDot * maxSpeed;

        // get the smoothing factor for deceleration
        var lerpSpeedTime = decelerationSmoothFactor * Time.deltaTime;

        // get the smoothing factor for acceleration
        if (newSpeed > currSpeed)
        {
            lerpSpeedTime = accelerationSmoothFactor * Time.deltaTime;
        }

        // final smoothed speed value
        var lerpedSpeed = Mathf.Lerp(currSpeed, newSpeed, lerpSpeedTime);

        // we use left + right combined vector to get the direction
        var handDirection = Vector3.Normalize(leftForward + rightForward);

        // for desktop - we use the head vector instead
        if (isDesktop)
        {
            handDirection = headForward;
        }

        // allow to select tracking source for one handed
        if (oneHanded)
        {
            if (oneHandedHead)
            {
                handDirection = headForward;
            }

            if (oneHandedRightHand)
            {
                handDirection = rightForward;
            }

            if (oneHandedLeftHand)
            {
                handDirection = leftForward;
            }
        }

        // check if we are pointing forwards or backwards
        var     headDot = Vector3.Dot(handDirection, headForward);
        Vector3 forwardVelocity;

        // get the  direction amount
        var downDot = Vector3.Dot(handDirection, Vector3.down);

        if (downDot < 0f)
        {
            downDot = 0f;
        }

        // one handed flight does not have any special backward flight logic due to control limitations
        if (headDot < 0f && !oneHanded)
        {
            forwardVelocity = BackwardFlight(handDirection, currSpeed, lerpedSpeed, downDot);
        }
        else
        {
            forwardVelocity = ForwardFlight(handDirection, lerpedSpeed);
        }

        rSpeed    = lerpedSpeed;
        rSpeedDot = handsDot;

        // hovering on desktop
        if (isDesktop && Input.GetKeyDown(freezeInPlaceKey) && allowHover)
        {
            hovering = !hovering;
            if (hovering)
            {
                if (onHoverStart)
                {
                    onHoverStart.SendCustomEvent(onHoverStartEvent);
                }
                return;
            }
            else
            {
                if (onHoverEnd)
                {
                    onHoverEnd.SendCustomEvent(onHoverEndEvent);
                }
            }
        }

        // hovering in vr
        if (!isDesktop && !oneHanded)
        {
            if (handsDot < -0.9 || headDot < 0f)
            {
                if (!hovering)
                {
                    hovering = true;
                    if (onHoverStart)
                    {
                        onHoverStart.SendCustomEvent(onHoverStartEvent);
                    }
                }
            }
            else if (hovering)
            {
                hovering = false;
                if (onHoverEnd)
                {
                    onHoverEnd.SendCustomEvent(onHoverEndEvent);
                }
            }
        }

        // reset back to start
        if (Input.GetKeyDown(forceRespawnKey))
        {
            if (resetPosition)
            {
                player.TeleportTo(new Vector3(0, 0.2f, 0), Quaternion.identity);
                Ground();
                return;
            }
        }

        // we do not want to prevent the players from descending while hovering
        // so we only lock them in place when they're mostly t-posing
        if (hovering || hovering && !isDesktop && downDot < 0.1)
        {
            SetVelocity(new Vector3(0, 0, 0));
            return;
        }

        SetVelocity(forwardVelocity);
    }