Ejemplo n.º 1
0
    void LaunchCallbacks(CharacterRay2D[] rays, RayType rayType)
    {
        m_curHits.Clear();
        for (int i = 0; i < rays.Length; i++)
        {
            CharacterRay2D curRay = rays[i];

            // do not send message twice for same collider & same raytype
            if (curRay.m_collider != null && !m_curHits.Contains(curRay.m_collider))
            {
                m_curHits.Add(curRay.m_collider);
                APHitable hitable = curRay.m_collider.GetComponent <APHitable>();
                if (hitable)
                {
                    hitable.OnCharacterTouch(m_character, rayType);
                }
            }
        }
    }
Ejemplo n.º 2
0
    bool MoveHorizontal(ref Vector2 moveOffset, bool bFront, bool bCorrectPos)
    {
        bool    bPenetrated      = false;
        float   fMinPenetration  = 0f;
        bool    bRayDirRight     = (m_faceRight && bFront) || (!m_faceRight && !bFront);
        Vector2 rayDir           = bRayDirRight ? transform.right : -transform.right;
        float   fMoveOffsetOnRay = Vector2.Dot(moveOffset, rayDir);
        bool    bCorrectVel      = fMoveOffsetOnRay > 0f;
        float   fMaxAllowedMove  = Mathf.Abs(fMoveOffsetOnRay);

        Vector2 v2SavedPos = transform.position;

        transform.position = m_curPos;

        // Launch ground cast from destination position
        CharacterRay2D[] rays = bFront ? m_RaysFront : m_RaysBack;
        foreach (CharacterRay2D curRay in rays)
        {
            float fRayPen       = curRay.m_penetration * Mathf.Abs(transform.lossyScale.x) * m_scale.x;
            float fCurRayLength = fRayPen + curRay.m_extraDistance;

            // add move offset if needed
            if (bCorrectVel)
            {
                fCurRayLength += Mathf.Abs(fMoveOffsetOnRay);
            }

            // launch ray
            Vector2 rayOrigin = GetRayPositionWs(curRay);

            if (m_DrawRays)
            {
                Debug.DrawLine(rayOrigin, rayOrigin + rayDir * fCurRayLength);
            }

            int   hitCount    = Physics2D.RaycastNonAlloc(rayOrigin, rayDir, m_rayResults, fCurRayLength, BuildRayLayer());
            float fClosestHit = float.MaxValue;
            for (int hitId = 0; hitId < hitCount; hitId++)
            {
                RaycastHit2D hit = m_rayResults[hitId];

                // ignore character himself
                if (hit.rigidbody == m_rigidBody)
                {
                    continue;
                }

                // ignore triggers & handle ignored colliders
                if (hit.collider != null)
                {
                    if (hit.collider.isTrigger || IsIgnoredCollider(hit.collider))
                    {
                        continue;
                    }
                }

                // compute distance of penetration
                float fHitLength = hit.fraction * fCurRayLength;
                float fPen       = fHitLength - fRayPen;

                // launch callback
                APMaterial hitMaterial = null;
                if (hit.collider != null)
                {
                    // handle onewayground here
                    if (IsOneWayGround(hit.collider))
                    {
                        HandleOneWayGround(fPen, fRayPen, hit.collider);

                        // ignore collision in all cases
                        continue;
                    }

                    // handle callbacks
                    hitMaterial = hit.collider.GetComponent <APMaterial>();
                    APHitable hitable = hit.collider.GetComponent <APHitable>();
                    if (hitable && m_character)
                    {
                        // ignore hit if requested
                        if (!hitable.OnCharacterTouch(m_character, bFront ? RayType.Front : RayType.Back, hit, fPen, hitMaterial))
                        {
                            continue;
                        }
                    }
                }

                // ignore insides (we don't know in which direction we should correct penetration)
                if (hit.fraction == 0f)
                {
                    continue;
                }

                // ignore non opposing normal against ray
                if (Vector2.Dot(rayDir, hit.normal) > 0f)
                {
                    continue;
                }

                if (m_DrawRays)
                {
                    Debug.DrawLine(hit.point, hit.point + hit.normal * 0.1f, Color.red);
                }

                // we are in skin width
                if (bFront)
                {
                    m_touchFront = true;
                }
                else
                {
                    m_touchBack = true;
                }

                // keep hit with maximum penetration (allow small penetration to prevent djitering)
                if ((fPen + m_allowedPenetration) < fMinPenetration)
                {
                    bPenetrated     = true;
                    fMinPenetration = fPen;
                }

                // save collider of closest hit in skin width
                if (hit.fraction < fClosestHit)
                {
                    fClosestHit = hit.fraction;
                    curRay.m_hitInfo.m_collider    = hit.collider;
                    curRay.m_hitInfo.m_normal      = hit.normal;
                    curRay.m_hitInfo.m_penetration = fPen;
                    curRay.m_hitInfo.m_material    = hitMaterial;
                }

                // Compute velocity correction
                if (bCorrectVel)
                {
                    fMaxAllowedMove = Mathf.Min(fMaxAllowedMove, fHitLength - fRayPen);
                    fMaxAllowedMove = Mathf.Max(0f, fMaxAllowedMove);                     // cannot be negative
                }
            }
        }

        Vector2 fPrevPos = m_curPos;

        // correction by velocity
        if (bCorrectVel)
        {
            moveOffset += rayDir * (fMaxAllowedMove - fMoveOffsetOnRay);

            // integrate
            m_curPos += rayDir * (fMaxAllowedMove);

            // prevent carrier velocity from adding inertia if we did not touch anything
            if (m_character.IsCarried())
            {
                float fDot = Vector2.Dot(m_carrierVel, rayDir);
                m_posError += rayDir * fDot * Time.deltaTime;
            }
        }

        // correction by position
        if (bPenetrated && bCorrectPos)
        {
            Vector2 v2PosErr = Mathf.Max(fMinPenetration, -m_PosErrorMaxVel * Time.deltaTime) * rayDir;
            m_curPos += v2PosErr;

            // keep this value
            m_posError += v2PosErr;
        }

        // fix all rays penetration
        if (bFront)
        {
            foreach (CharacterRay2D curRay in m_RaysFront)
            {
                if (curRay.m_hitInfo.m_collider)
                {
                    curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir);
                }
            }
        }
        else
        {
            foreach (CharacterRay2D curRay in m_RaysBack)
            {
                if (curRay.m_hitInfo.m_collider)
                {
                    curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir);
                }
            }
        }

        transform.position = v2SavedPos;
        return(bPenetrated);
    }
Ejemplo n.º 3
0
    bool MoveVertical(ref Vector2 moveOffset, bool bDown, bool bCorrectPos)
    {
        bool    bPenetrated       = false;
        float   fMinPenetration   = 0f;
        Vector2 rayDir            = bDown ? -transform.up : transform.up;
        float   fMoveOffsetOnRay  = Vector2.Dot(moveOffset, rayDir);
        float   fMaxAllowedMove   = Mathf.Abs(fMoveOffsetOnRay);
        bool    bCorrectVel       = fMoveOffsetOnRay > 0f;
        float   fGroundBounciness = 0f;

        float   fClosestGroundHit = float.MaxValue;
        Vector2 v2SavedPos        = transform.position;

        transform.position = m_curPos;

        if (bDown)
        {
            m_touchGround      = false;
            m_groundGameObject = null;
            m_distToGround     = 0f;
        }

        // Launch ground cast from destination position
        CharacterRay2D[] rays = bDown ? m_RaysGround : m_RaysUp;
        foreach (CharacterRay2D curRay in rays)
        {
            float fRayPen       = curRay.m_penetration * Mathf.Abs(transform.lossyScale.y) * m_scale.y;
            float fCurRayLength = fRayPen + (Mathf.Max(curRay.m_extraDistance, 1f));             // make sure ground is detected far enough for slope down snapping

            // add move offset in raycast if needed
            if (bCorrectVel)
            {
                fCurRayLength += Mathf.Abs(fMoveOffsetOnRay);
            }

            // launch ray
            Vector2 rayOrigin = GetRayPositionWs(curRay);

            if (m_DrawRays)
            {
                Debug.DrawLine(rayOrigin, rayOrigin + rayDir * fCurRayLength);
            }

            int   hitCount    = Physics2D.RaycastNonAlloc(rayOrigin, rayDir, m_rayResults, fCurRayLength, BuildRayLayer());
            float fClosestHit = float.MaxValue;
            for (int hitId = 0; hitId < hitCount; hitId++)
            {
                RaycastHit2D hit = m_rayResults[hitId];

                // ignore character himself
                if (hit.rigidbody == m_rigidBody)
                {
                    continue;
                }

                // ignore triggers & handle ignored colliders
                if (hit.collider != null)
                {
                    if (hit.collider.isTrigger || IsIgnoredCollider(hit.collider))
                    {
                        continue;
                    }
                }

                // compute distance of penetration
                float fHitLength = hit.fraction * fCurRayLength;
                float fPen       = fHitLength - fRayPen;

                // launch callback
                APMaterial hitMaterial = null;
                if (hit.collider != null)
                {
                    // Handle oneway ground here
                    if (IsOneWayGround(hit.collider))
                    {
                        HandleOneWayGround(fPen, fRayPen, hit.collider);

                        // ignore if we are colliding under or if high penetrattion occurred with this collider
                        if (!bDown || (bDown && ((hit.normal.y < 0f) || m_oneWayColliders.Exists(x => x.m_collider == hit.collider))))
                        {
                            continue;
                        }
                    }

                    // Handle callbacks
                    hitMaterial = hit.collider.GetComponent <APMaterial>();
                    APHitable hitable = hit.collider.GetComponent <APHitable>();
                    if (hitable && m_character)
                    {
                        if (!hitable.OnCharacterTouch(m_character, bDown ? RayType.Ground : RayType.Up, hit, fPen, hitMaterial))
                        {
                            continue;
                        }
                    }
                }

                // ignore insides (we don't know in which direction we should correct penetration)
                if (hit.fraction == 0f)
                {
                    continue;
                }

                // ignore non opposing normal against ray
                if (Vector2.Dot(rayDir, hit.normal) > 0f)
                {
                    continue;
                }

                if (m_DrawRays)
                {
                    Debug.DrawLine(hit.point, hit.point + hit.normal * 0.1f, Color.red);
                }

                // keep hit with maximum penetration
                if ((fPen + m_allowedPenetration) < fMinPenetration)
                {
                    bPenetrated     = true;
                    fMinPenetration = fPen;
                }

                // Compute velocity correction
                if (bCorrectVel)
                {
                    fMaxAllowedMove = Mathf.Min(fMaxAllowedMove, fHitLength - fRayPen);
                    fMaxAllowedMove = Mathf.Max(0f, fMaxAllowedMove);                     // cannot be negative
                }

                // we are in skin width
                if (bDown)
                {
                    m_touchGround = true;

                    // keep ground normal with most penetration
                    if (fPen < fClosestGroundHit)
                    {
                        m_groundNormal     = hit.normal;
                        fClosestGroundHit  = fPen;
                        m_groundGameObject = hit.collider.gameObject;
                        m_distToGround     = fPen;

                        // save ground bounciness
                        if (hitMaterial != null)
                        {
                            fGroundBounciness = Mathf.Max(fGroundBounciness, hitMaterial.m_groundBounciness);
                        }
                    }
                }
                else
                {
                    m_touchHead = true;
                }

                // save collider of closest hit for this ray
                if (hit.fraction < fClosestHit)
                {
                    fClosestHit = hit.fraction;
                    curRay.m_hitInfo.m_collider    = hit.collider;
                    curRay.m_hitInfo.m_normal      = hit.normal;
                    curRay.m_hitInfo.m_penetration = fPen;
                    curRay.m_hitInfo.m_material    = hitMaterial;
                }
            }
        }

        Vector2 fPrevPos = m_curPos;

        // prevent velocity along ray axis
        if (bCorrectVel)
        {
            moveOffset += rayDir * (fMaxAllowedMove - fMoveOffsetOnRay);

            // integrate
            m_curPos += rayDir * (fMaxAllowedMove);

            // prevent carrier velocity from adding inertia if we did not touch anything
            if (m_character.IsCarried())
            {
                float fDot = Vector2.Dot(m_carrierVel, rayDir);
                m_posError += rayDir * fDot * Time.deltaTime;
            }
        }



        // correction by position
        if (bPenetrated && bCorrectPos)
        {
            Vector2 v2PosErr = Mathf.Max(fMinPenetration, -m_PosErrorMaxVel * Time.deltaTime) * rayDir;
            m_curPos += v2PosErr;

            // keep this value so it is removed from velocity computing later
            m_posError += v2PosErr;
        }

        // fix distance to ground
        if (bDown && m_touchGround)
        {
            m_groundNormalLs = transform.InverseTransformDirection(m_groundNormal);
            m_distToGround  += Vector2.Dot(fPrevPos - m_curPos, rayDir);
        }

        // fix all rays penetration
        if (bDown)
        {
            foreach (CharacterRay2D curRay in m_RaysGround)
            {
                if (curRay.m_hitInfo.m_collider)
                {
                    curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir);
                }
            }
        }
        else
        {
            foreach (CharacterRay2D curRay in m_RaysUp)
            {
                if (curRay.m_hitInfo.m_collider)
                {
                    curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir);
                }
            }
        }

        // Handle ground bounciness here
        if (m_distToGround < 0.01f && fGroundBounciness > 0f)
        {
            Vector2 v2ImpulseDir = m_groundNormal;
            m_character.AddImpulse(v2ImpulseDir * fGroundBounciness * 30f);
        }

        transform.position = v2SavedPos;
        return(bPenetrated);
    }