< Summary

Class:DCLCharacterController
Assembly:CharacterController
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/CharacterController/DCLCharacterController.cs
Covered lines:194
Uncovered lines:62
Coverable lines:256
Total lines:578
Line coverage:75.7% (194 of 256)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
DCLCharacterController()0%110100%
Awake()0%44096.3%
SubscribeToInput()0%110100%
OnDestroy()0%110100%
OnWorldReposition(...)0%330100%
SetPosition(...)0%990100%
Teleport(...)0%110100%
Teleport(...)0%3.033085.71%
SetPosition(...)0%2100%
SetEnabled(...)0%110100%
Moved(...)0%220100%
LateUpdate()0%41.4629075.44%
SaveLateUpdateGroundTransforms()0%6200%
Jump()0%12300%
ResetGround()0%2.022083.33%
CheckGround()0%28.4112051.52%
CastGroundCheckingRays()0%220100%
CastGroundCheckingRays(...)0%220100%
CastGroundCheckingRay(...)0%2100%
CastGroundCheckingRays(...)0%660100%
CastGroundCheckingRay(...)0%220100%
ReportMovement()0%330100%
PauseGravity()0%110100%
ResumeGravity()0%2100%
OnRenderingStateChanged(...)0%110100%
IsLastCollisionGround()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/CharacterController/DCLCharacterController.cs

#LineLine coverage
 1using DCL;
 2using DCL.Configuration;
 3using DCL.Helpers;
 4using UnityEngine;
 5using Cinemachine;
 6
 7public class DCLCharacterController : MonoBehaviour
 8{
 09    public static DCLCharacterController i { get; private set; }
 10
 11    private const float CONTROLLER_DRIFT_OFFSET = 0.15f;
 12
 13    [Header("Movement")]
 61014    public float minimumYPosition = 1f;
 15
 61016    public float groundCheckExtraDistance = 0.1f;
 61017    public float gravity = -55f;
 61018    public float jumpForce = 12f;
 61019    public float movementSpeed = 8f;
 61020    public float runningSpeedMultiplier = 2f;
 21
 22    public DCLCharacterPosition characterPosition;
 23
 24    [Header("Collisions")]
 25    public LayerMask groundLayers;
 26
 27    [Header("Additional Camera Layers")]
 28    public LayerMask cameraLayers;
 29
 30    [System.NonSerialized]
 31    public bool initialPositionAlreadySet = false;
 32
 33    [System.NonSerialized]
 61034    public bool characterAlwaysEnabled = true;
 35
 36    [System.NonSerialized]
 37    public CharacterController characterController;
 38
 39    FreeMovementController freeMovementController;
 40
 41    new Collider collider;
 42
 43    float lastUngroundedTime = 0f;
 44    float lastJumpButtonPressedTime = 0f;
 45    float lastMovementReportTime;
 46    float originalGravity;
 47    Vector3 lastLocalGroundPosition;
 48
 49    Vector3 lastCharacterRotation;
 50    Vector3 lastGlobalCharacterRotation;
 51
 61052    Vector3 velocity = Vector3.zero;
 53
 054    public bool isWalking { get; private set; } = false;
 055    public bool isMovingByUserInput { get; private set; } = false;
 056    public bool isJumping { get; private set; } = false;
 057    public bool isGrounded { get; private set; }
 058    public bool isOnMovingPlatform { get; private set; }
 59
 60    internal Transform groundTransform;
 61
 62    Vector3 lastPosition;
 63    Vector3 groundLastPosition;
 64    Quaternion groundLastRotation;
 65    bool jumpButtonPressed = false;
 66
 67    [Header("InputActions")]
 68    public InputAction_Hold jumpAction;
 69
 70    public InputAction_Hold sprintAction;
 71
 72    public Vector3 moveVelocity;
 73
 74    private InputAction_Hold.Started jumpStartedDelegate;
 75    private InputAction_Hold.Finished jumpFinishedDelegate;
 76    private InputAction_Hold.Started walkStartedDelegate;
 77    private InputAction_Hold.Finished walkFinishedDelegate;
 78
 1035079    private Vector3NullableVariable characterForward => CommonScriptableObjects.characterForward;
 80
 81    public static System.Action<DCLCharacterPosition> OnCharacterMoved;
 82    public static System.Action<DCLCharacterPosition> OnPositionSet;
 83    public event System.Action<float> OnUpdateFinish;
 84
 85    public GameObject avatarGameObject;
 86    public GameObject firstPersonCameraGameObject;
 87
 88    [SerializeField]
 89    private InputAction_Measurable characterYAxis;
 90
 91    [SerializeField]
 92    private InputAction_Measurable characterXAxis;
 93
 998594    private Vector3Variable cameraForward => CommonScriptableObjects.cameraForward;
 2795    private Vector3Variable cameraRight => CommonScriptableObjects.cameraRight;
 96
 61097    private readonly DataStore_Player dataStorePlayer = DataStore.i.player;
 98
 99    [System.NonSerialized]
 100    public float movingPlatformSpeed;
 101    private CollisionFlags lastCharacterControllerCollision;
 102
 103    public event System.Action OnJump;
 104    public event System.Action OnHitGround;
 105    public event System.Action<float> OnMoved;
 106
 107    void Awake()
 108    {
 596109        if (i != null)
 110        {
 23111            Destroy(gameObject);
 23112            return;
 113        }
 114
 573115        i = this;
 573116        originalGravity = gravity;
 117
 573118        SubscribeToInput();
 573119        CommonScriptableObjects.playerUnityPosition.Set(Vector3.zero);
 573120        dataStorePlayer.playerWorldPosition.Set(Vector3.zero);
 573121        CommonScriptableObjects.playerCoords.Set(Vector2Int.zero);
 573122        dataStorePlayer.playerGridPosition.Set(Vector2Int.zero);
 573123        CommonScriptableObjects.playerUnityEulerAngles.Set(Vector3.zero);
 124
 573125        characterPosition = new DCLCharacterPosition();
 573126        characterController = GetComponent<CharacterController>();
 573127        freeMovementController = GetComponent<FreeMovementController>();
 573128        collider = GetComponent<Collider>();
 129
 573130        CommonScriptableObjects.worldOffset.OnChange += OnWorldReposition;
 131
 573132        lastPosition = transform.position;
 573133        transform.parent = null;
 134
 573135        CommonScriptableObjects.rendererState.OnChange += OnRenderingStateChanged;
 573136        OnRenderingStateChanged(CommonScriptableObjects.rendererState.Get(), false);
 137
 573138        if (avatarGameObject == null || firstPersonCameraGameObject == null)
 139        {
 0140            throw new System.Exception("Both the avatar and first person camera game objects must be set.");
 141        }
 142
 573143        var worldData = DataStore.i.Get<DataStore_World>();
 573144        worldData.avatarTransform.Set(avatarGameObject.transform);
 573145        worldData.fpsTransform.Set(firstPersonCameraGameObject.transform);
 146
 573147        dataStorePlayer.lastTeleportPosition.OnChange += Teleport;
 573148    }
 149
 150    private void SubscribeToInput()
 151    {
 573152        jumpStartedDelegate = (action) =>
 153        {
 0154            lastJumpButtonPressedTime = Time.time;
 0155            jumpButtonPressed = true;
 0156        };
 573157        jumpFinishedDelegate = (action) => jumpButtonPressed = false;
 573158        jumpAction.OnStarted += jumpStartedDelegate;
 573159        jumpAction.OnFinished += jumpFinishedDelegate;
 160
 573161        walkStartedDelegate = (action) => isWalking = true;
 573162        walkFinishedDelegate = (action) => isWalking = false;
 573163        sprintAction.OnStarted += walkStartedDelegate;
 573164        sprintAction.OnFinished += walkFinishedDelegate;
 573165    }
 166
 167    void OnDestroy()
 168    {
 596169        CommonScriptableObjects.worldOffset.OnChange -= OnWorldReposition;
 596170        jumpAction.OnStarted -= jumpStartedDelegate;
 596171        jumpAction.OnFinished -= jumpFinishedDelegate;
 596172        sprintAction.OnStarted -= walkStartedDelegate;
 596173        sprintAction.OnFinished -= walkFinishedDelegate;
 596174        CommonScriptableObjects.rendererState.OnChange -= OnRenderingStateChanged;
 596175        dataStorePlayer.lastTeleportPosition.OnChange -= Teleport;
 596176        i = null;
 596177    }
 178
 179    void OnWorldReposition(Vector3 current, Vector3 previous)
 180    {
 2181        Vector3 oldPos = this.transform.position;
 2182        this.transform.position = characterPosition.unityPosition; //CommonScriptableObjects.playerUnityPosition;
 183
 2184        if (CinemachineCore.Instance.BrainCount > 0)
 185        {
 2186            CinemachineCore.Instance.GetActiveBrain(0).ActiveVirtualCamera?.OnTargetObjectWarped(transform, transform.po
 187        }
 2188    }
 189
 190    public void SetPosition(Vector3 newPosition)
 191    {
 192        // failsafe in case something teleports the player below ground collisions
 5368193        if (newPosition.y < minimumYPosition)
 194        {
 153195            newPosition.y = minimumYPosition + 2f;
 196        }
 197
 5368198        lastPosition = characterPosition.worldPosition;
 5368199        characterPosition.worldPosition = newPosition;
 5368200        transform.position = characterPosition.unityPosition;
 5368201        Environment.i.platform.physicsSyncController?.MarkDirty();
 202
 5368203        CommonScriptableObjects.playerUnityPosition.Set(characterPosition.unityPosition);
 5368204        dataStorePlayer.playerWorldPosition.Set(characterPosition.worldPosition);
 5368205        Vector2Int playerPosition = Utils.WorldToGridPosition(characterPosition.worldPosition);
 5368206        CommonScriptableObjects.playerCoords.Set(playerPosition);
 5368207        dataStorePlayer.playerGridPosition.Set(playerPosition);
 5368208        dataStorePlayer.playerUnityPosition.Set(characterPosition.unityPosition);
 209
 5368210        if (Moved(lastPosition))
 211        {
 4948212            if (Moved(lastPosition, useThreshold: true))
 4947213                ReportMovement();
 214
 4948215            OnCharacterMoved?.Invoke(characterPosition);
 216
 4948217            float distance = Vector3.Distance(characterPosition.worldPosition, lastPosition) - movingPlatformSpeed;
 218
 4948219            if (distance > 0f && isGrounded)
 25220                OnMoved?.Invoke(distance);
 221        }
 222
 5368223        lastPosition = transform.position;
 5368224    }
 225
 226    public void Teleport(string teleportPayload)
 227    {
 40228        var payload = Utils.FromJsonWithNulls<Vector3>(teleportPayload);
 40229        dataStorePlayer.lastTeleportPosition.Set(payload, notifyEvent: true);
 40230    }
 231
 232    private void Teleport(Vector3 newPosition, Vector3 prevPosition)
 233    {
 41234        ResetGround();
 235
 41236        SetPosition(newPosition);
 237
 41238        if (OnPositionSet != null)
 239        {
 0240            OnPositionSet.Invoke(characterPosition);
 241        }
 242
 41243        if (!initialPositionAlreadySet)
 244        {
 37245            initialPositionAlreadySet = true;
 246        }
 41247    }
 248
 249    [System.Obsolete("SetPosition is deprecated, please use Teleport instead.", true)]
 0250    public void SetPosition(string positionVector) { Teleport(positionVector); }
 251
 2290252    public void SetEnabled(bool enabled) { this.enabled = enabled; }
 253
 254    bool Moved(Vector3 previousPosition, bool useThreshold = false)
 255    {
 10316256        if (useThreshold)
 4948257            return Vector3.Distance(characterPosition.worldPosition, previousPosition) > 0.001f;
 258        else
 5368259            return characterPosition.worldPosition != previousPosition;
 260    }
 261
 262    internal void LateUpdate()
 263    {
 5328264        if(!dataStorePlayer.canPlayerMove.Get())
 2265            return;
 266
 5326267        if (transform.position.y < minimumYPosition)
 268        {
 0269            SetPosition(characterPosition.worldPosition);
 0270            return;
 271        }
 272
 5326273        if (freeMovementController.IsActive())
 274        {
 0275            velocity = freeMovementController.CalculateMovement();
 0276        }
 277        else
 278        {
 5326279            velocity.x = 0f;
 5326280            velocity.z = 0f;
 5326281            velocity.y += gravity * Time.deltaTime;
 282
 5326283            bool previouslyGrounded = isGrounded;
 284
 5326285            if (!isJumping || velocity.y <= 0f)
 5326286                CheckGround();
 287
 5326288            if (isGrounded)
 289            {
 35290                isJumping = false;
 35291                velocity.y = gravity * Time.deltaTime; // to avoid accumulating gravity in velocity.y while grounded
 35292            }
 5291293            else if (previouslyGrounded && !isJumping)
 294            {
 4295                lastUngroundedTime = Time.time;
 296            }
 297
 5326298            if (characterForward.HasValue())
 299            {
 300                // Horizontal movement
 27301                var speed = movementSpeed * (isWalking ? runningSpeedMultiplier : 1f);
 302
 27303                transform.forward = characterForward.Get().Value;
 304
 27305                var xzPlaneForward = Vector3.Scale(cameraForward.Get(), new Vector3(1, 0, 1));
 27306                var xzPlaneRight = Vector3.Scale(cameraRight.Get(), new Vector3(1, 0, 1));
 307
 27308                Vector3 forwardTarget = Vector3.zero;
 309
 27310                if (characterYAxis.GetValue() > CONTROLLER_DRIFT_OFFSET)
 0311                    forwardTarget += xzPlaneForward;
 27312                if (characterYAxis.GetValue() < -CONTROLLER_DRIFT_OFFSET)
 0313                    forwardTarget -= xzPlaneForward;
 314
 27315                if (characterXAxis.GetValue() > CONTROLLER_DRIFT_OFFSET)
 0316                    forwardTarget += xzPlaneRight;
 27317                if (characterXAxis.GetValue() < -CONTROLLER_DRIFT_OFFSET)
 0318                    forwardTarget -= xzPlaneRight;
 319
 27320                if (forwardTarget.Equals(Vector3.zero))
 27321                    isMovingByUserInput = false;
 322                else
 0323                    isMovingByUserInput = true;
 324
 325
 27326                forwardTarget.Normalize();
 27327                velocity += forwardTarget * speed;
 27328                CommonScriptableObjects.playerUnityEulerAngles.Set(transform.eulerAngles);
 329            }
 330
 5326331            bool jumpButtonPressedWithGraceTime = jumpButtonPressed && (Time.time - lastJumpButtonPressedTime < 0.15f);
 332
 5326333            if (jumpButtonPressedWithGraceTime) // almost-grounded jump button press allowed time
 334            {
 0335                bool justLeftGround = (Time.time - lastUngroundedTime) < 0.1f;
 336
 0337                if (isGrounded || justLeftGround) // just-left-ground jump allowed time
 338                {
 0339                    Jump();
 340                }
 341            }
 342
 343            //NOTE(Mordi): Detecting when the character hits the ground (for landing-SFX)
 5326344            if (isGrounded && !previouslyGrounded && (Time.time - lastUngroundedTime) > 0.4f)
 345            {
 19346                OnHitGround?.Invoke();
 347            }
 348        }
 349
 5326350        if (characterController.enabled)
 351        {
 352            //NOTE(Brian): Transform has to be in sync before the Move call, otherwise this call
 353            //             will reset the character controller to its previous position.
 5326354            Environment.i.platform.physicsSyncController?.Sync();
 5326355            lastCharacterControllerCollision = characterController.Move(velocity * Time.deltaTime);
 356        }
 357
 5326358        SetPosition(PositionUtils.UnityToWorldPosition(transform.position));
 359
 5326360        if ((DCLTime.realtimeSinceStartup - lastMovementReportTime) > PlayerSettings.POSITION_REPORTING_DELAY)
 361        {
 38362            ReportMovement();
 363        }
 364
 5326365        if (isOnMovingPlatform)
 366        {
 0367            SaveLateUpdateGroundTransforms();
 368        }
 5326369        OnUpdateFinish?.Invoke(Time.deltaTime);
 0370    }
 371
 372    private void SaveLateUpdateGroundTransforms()
 373    {
 0374        lastLocalGroundPosition = groundTransform.InverseTransformPoint(transform.position);
 375
 0376        if (CommonScriptableObjects.characterForward.HasValue())
 377        {
 0378            lastCharacterRotation = groundTransform.InverseTransformDirection(CommonScriptableObjects.characterForward.G
 0379            lastGlobalCharacterRotation = CommonScriptableObjects.characterForward.Get().Value;
 380        }
 0381    }
 382
 383    void Jump()
 384    {
 0385        if (isJumping)
 0386            return;
 387
 0388        isJumping = true;
 0389        isGrounded = false;
 390
 0391        ResetGround();
 392
 0393        velocity.y = jumpForce;
 394        //cameraTargetProbe.damping.y = dampingOnAir;
 395
 0396        OnJump?.Invoke();
 0397    }
 398
 399    public void ResetGround()
 400    {
 10645401        if (isOnMovingPlatform)
 0402            CommonScriptableObjects.playerIsOnMovingPlatform.Set(false);
 403
 10645404        isOnMovingPlatform = false;
 10645405        groundTransform = null;
 10645406        movingPlatformSpeed = 0;
 10645407    }
 408
 409    void CheckGround()
 410    {
 5326411        if (groundTransform == null)
 5310412            ResetGround();
 413
 5326414        if (isOnMovingPlatform)
 415        {
 0416            Physics.SyncTransforms();
 417            //NOTE(Brian): This should move the character with the moving platform
 0418            Vector3 newGroundWorldPos = groundTransform.TransformPoint(lastLocalGroundPosition);
 0419            movingPlatformSpeed = Vector3.Distance(newGroundWorldPos, transform.position);
 0420            transform.position = newGroundWorldPos;
 421
 0422            Vector3 newCharacterForward = groundTransform.TransformDirection(lastCharacterRotation);
 0423            Vector3 lastFrameDifference = Vector3.zero;
 0424            if (CommonScriptableObjects.characterForward.HasValue())
 425            {
 0426                lastFrameDifference = CommonScriptableObjects.characterForward.Get().Value - lastGlobalCharacterRotation
 427            }
 428
 429            //NOTE(Kinerius) CameraStateTPS rotates the character between frames so we add the difference.
 430            //               if we dont do this, the character wont rotate when moving, only when the platform rotates
 0431            CommonScriptableObjects.characterForward.Set(newCharacterForward + lastFrameDifference);
 432        }
 433
 5326434        Transform transformHit = CastGroundCheckingRays();
 435
 5326436        if (transformHit != null)
 437        {
 32438            if (groundTransform == transformHit)
 439            {
 14440                bool groundHasMoved = (transformHit.position != groundLastPosition || transformHit.rotation != groundLas
 441
 14442                if (!characterPosition.RepositionedWorldLastFrame()
 443                    && groundHasMoved)
 444                {
 0445                    isOnMovingPlatform = true;
 0446                    CommonScriptableObjects.playerIsOnMovingPlatform.Set(true);
 0447                    Physics.SyncTransforms();
 0448                    SaveLateUpdateGroundTransforms();
 449
 0450                    Quaternion deltaRotation = groundTransform.rotation * Quaternion.Inverse(groundLastRotation);
 0451                    CommonScriptableObjects.movingPlatformRotationDelta.Set(deltaRotation);
 452                }
 0453            }
 454            else
 455            {
 18456                groundTransform = transformHit;
 18457                CommonScriptableObjects.movingPlatformRotationDelta.Set(Quaternion.identity);
 458            }
 18459        }
 460        else
 461        {
 5294462            ResetGround();
 463        }
 464
 5326465        if (groundTransform != null)
 466        {
 32467            groundLastPosition = groundTransform.position;
 32468            groundLastRotation = groundTransform.rotation;
 469        }
 470
 5326471        isGrounded = IsLastCollisionGround() || groundTransform != null && groundTransform.gameObject.activeInHierarchy;
 5326472    }
 473
 474    public Transform CastGroundCheckingRays()
 475    {
 476        RaycastHit hitInfo;
 477
 5326478        var result = CastGroundCheckingRays(transform, collider, groundCheckExtraDistance, 0.9f, groundLayers, out hitIn
 479
 5326480        if ( result )
 481        {
 32482            return hitInfo.transform;
 483        }
 484
 5294485        return null;
 486    }
 487
 488    public bool CastGroundCheckingRays(float extraDistance, float scale, out RaycastHit hitInfo)
 489    {
 5291490        if (CastGroundCheckingRays(transform, collider, extraDistance, scale, groundLayers | cameraLayers , out hitInfo)
 325491            return true;
 492
 4966493        return IsLastCollisionGround();
 494    }
 495
 496    public bool CastGroundCheckingRay(float extraDistance, out RaycastHit hitInfo)
 497    {
 0498        Bounds bounds = collider.bounds;
 0499        float rayMagnitude = (bounds.extents.y + extraDistance);
 0500        bool test = CastGroundCheckingRay(transform.position, out hitInfo, rayMagnitude, groundLayers);
 0501        return IsLastCollisionGround() || test;
 502    }
 503
 504    // We secuentially cast rays in 4 directions (only if the previous one didn't hit anything)
 505    public static bool CastGroundCheckingRays(Transform transform, Collider collider, float extraDistance, float scale, 
 506    {
 10617507        Bounds bounds = collider.bounds;
 508
 10617509        float rayMagnitude = (bounds.extents.y + extraDistance);
 10617510        float originScale = scale * bounds.extents.x;
 511
 10617512        if (!CastGroundCheckingRay(transform.position, out hitInfo, rayMagnitude, groundLayers) // center
 513            && !CastGroundCheckingRay( transform.position + transform.forward * originScale, out hitInfo, rayMagnitude, 
 514            && !CastGroundCheckingRay( transform.position + transform.right * originScale, out hitInfo, rayMagnitude, gr
 515            && !CastGroundCheckingRay( transform.position + -transform.forward * originScale, out hitInfo, rayMagnitude,
 516            && !CastGroundCheckingRay( transform.position + -transform.right * originScale, out hitInfo, rayMagnitude, g
 517        {
 10260518            return false;
 519        }
 520
 521        // At this point there is a guaranteed hit, so this is not null
 357522        return true;
 523    }
 524
 525    public static bool CastGroundCheckingRay(Vector3 origin, out RaycastHit hitInfo, float rayMagnitude, int groundLayer
 526    {
 51657527        var ray = new Ray();
 51657528        ray.origin = origin;
 51657529        ray.direction = Vector3.down * rayMagnitude;
 530
 51657531        var result = Physics.Raycast(ray, out hitInfo, rayMagnitude, groundLayers);
 532
 533#if UNITY_EDITOR
 51657534        if ( result )
 357535            Debug.DrawLine(ray.origin, hitInfo.point, Color.green);
 536        else
 51300537            Debug.DrawRay(ray.origin, ray.direction, Color.red);
 538#endif
 539
 51300540        return result;
 541    }
 542
 543    void ReportMovement()
 544    {
 4985545        float height = 0.875f;
 546
 4985547        var reportPosition = characterPosition.worldPosition + (Vector3.up * height);
 4985548        var compositeRotation = Quaternion.LookRotation(characterForward.HasValue() ? characterForward.Get().Value : cam
 4985549        var playerHeight = height + (characterController.height / 2);
 4985550        var cameraRotation = Quaternion.LookRotation(cameraForward.Get());
 551
 552        //NOTE(Brian): We have to wait for a Teleport before sending the ReportPosition, because if not ReportPosition e
 553        //             When the spawn point is being selected / scenes being prepared to be sent and the Kernel gets cra
 554
 555        //             The race conditions that can arise from not having this flag can result in:
 556        //                  - Scenes not being sent for loading, making ActivateRenderer never being sent, only in WSS m
 557        //                  - Random teleports to 0,0 or other positions that shouldn't happen.
 4985558        if (initialPositionAlreadySet)
 46559            DCL.Interface.WebInterface.ReportPosition(reportPosition, compositeRotation, playerHeight, cameraRotation);
 560
 4985561        lastMovementReportTime = DCLTime.realtimeSinceStartup;
 4985562    }
 563
 564    public void PauseGravity()
 565    {
 40566        gravity = 0f;
 40567        velocity.y = 0f;
 40568    }
 569
 0570    public void ResumeGravity() { gravity = originalGravity; }
 571
 2290572    void OnRenderingStateChanged(bool isEnable, bool prevState) { SetEnabled(isEnable); }
 573
 574    bool IsLastCollisionGround()
 575    {
 0576        return (lastCharacterControllerCollision & CollisionFlags.Below) != 0;
 577    }
 578}