Search Unity

Question PlatformEffector2D + BoxCast seems to not work in OneWay scenario

Discussion in 'Physics' started by spider853, Nov 28, 2023.

  1. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    Hello,

    I have an EdgeCollider2D + PlatformEffector (One Way setting)
    I want to make a prediction where the player will jump, so I'm doing boxcasting for projectile calculus to top the trajectory when there is a collision.

    When the player jumps from under the platform the boxcasting seem to stop at the oneway platform from under even if the direction is up.

    Does BoxCast works with Platform Effector?
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,450
    The two are nothing to do with each other. Those queries detect colliders, not behaviours of other types of objects such as effectors which override contacts.
     
  3. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    Thanks for the response, Is it possible to interact with the Platform Effector to check, or to filter contacts? I don't see any api in the docs for this.
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,450
    There's no API on the Platform Effector to ask it if a contact would be ignored.

    The thing is, a PlatformEffector2D defines a surface by a surface normal and you can do that in any query that uses a ContactFilter2D which is most of them. If you filter by surface normal angle range then you can achieve the same thing.
     
  5. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    Still it stops at wrong point, I might be doing something wrong, but maybe platform effector does a bit more to the side collision depending on which side is currently collision happening?

    The player physics pass trough the platform as intended but the trajectory with BoxCast doesn't
    Also tried values like -89 -> 89, -91 -> 91

    Here are some screens from my tests:
    upload_2023-12-1_22-12-17.png

    with OutsideAngle off
    upload_2023-12-1_22-12-38.png

    with OutsideAngle on

    upload_2023-12-1_22-12-53.png

    upload_2023-12-1_22-13-51.png
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,450
    Well I'd have no idea what the pictures are supposed to be showing TBH, I just see white boxes.
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,450
    FYI: Note, you're using Vector3 rather than Vector2 for you calculations and are referring to 3D physics gravity.

    Try dumping what the BoxCast with filtering off gives you. In the end, the filter is doing what you ask it.
     
    spider853 likes this.
  8. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    Oh sorry, the white boxes are the bounding boxes of the players at particular time stamps, for the trajectory, you can see the location of the boxes generated in the loop for trajectory point generation.
    The box cast gives the right collisions except I would want to skip them the same as Platform Effector does.

    Feels like I would need to go manually trough normal and do the calculations my self? collider normal vs max allowed angle + sides.

    I'm thinking now that the velocity might be an issue? As the contact filter/collision might resolve the collision when the box is still intersecting the edge and it seems like it just passthru, while the boxcast is reporting the collision as it needs because the box is generating a normal up at that point.

    Ok I guess I'll need to come up with a solution considering the velocity and trajectory orientation at the moment, because I was expecting the contact filter will behave the same (visually) as the Platform Effector

    "FYI: Note, you're using Vector3 rather than Vector2 for you calculations and are referring to 3D physics gravity."
    - Ah thanks, didn't notice that
     
  9. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    ok, fixed like this:

    Code (CSharp):
    1. void DrawTrajectory()
    2.     {
    3.         // x0 + x*t, y0 + y*t - g*t^2/2
    4.         if (!TrajectoryView.gameObject.activeSelf)
    5.             TrajectoryView.gameObject.SetActive(true);
    6.         int sampleCount = 20;
    7.         float timeIncrement = 0.1f;
    8.         TrajectoryView.positionCount = sampleCount;
    9.         float jumpPower = GetJumpPower() * MaxJumpPower;
    10.         Vector3 lastPos = Vector3.zero;
    11.         for (int i = 0; i < sampleCount; i++)
    12.         {
    13.             float t = i * timeIncrement;
    14.             Vector3 pos = new Vector3(transform.position.x + jumpPower * t * transform.localScale.x * JumpXRatio,
    15.                                                 transform.position.y + jumpPower * t + Physics2D.gravity.y * t * t / 2.0f, 0f);
    16.  
    17.             Vector3 diff = pos - lastPos;
    18.             float angle = Vector3.Angle(Vector3.up, diff.normalized);
    19.             if (i > 0)
    20.             {
    21.                 RaycastHit2D hit = Physics2D.BoxCast(lastPos, GetComponent<BoxCollider2D>().size, 0f, diff.normalized, pos.Distance(lastPos), LayerMask.NameToLayer("Player"));
    22.                 if (hit.collider != null)
    23.                 {
    24.                     float hitNormalAngle = Vector2.Angle(Vector2.up, hit.normal);
    25.                     float cosAngle = Vector2.Dot(diff, hit.normal);
    26.                     // Test for trajectory direction and hit normal (filter 180 deg up only) + edge detection of 1 deg and the normal direction needs to be opposite to the trajectory (as it can hit on exit)
    27.                     if (hitNormalAngle < 90.0f && angle > 90.0f || Mathf.Abs(hitNormalAngle - 90.0f) < 1.0f && cosAngle < 0.0f)
    28.                     {
    29.                         TrajectoryView.SetPosition(i, hit.centroid);
    30.                         TrajectoryView.positionCount = i + 1;
    31.                         break;
    32.                     }
    33.                 }
    34.             }
    35.  
    36.             TrajectoryView.SetPosition(i, pos);
    37.  
    38.             lastPos = pos;
    39.         }
    40.     }
    Still if I can leave a suggestion, to expose the Effectors interface to be able to filter your contacts through them
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,450
    You might have fixed it in your code above but there's nothing wrong with filtering in physics queries by normal angle so no idea why you need to do all that heavy lifting there.

    If your query starts inside an existing collider then it'll either return the start point you specified for your query or won't return any hit at all and that has always been controlled with: https://docs.unity3d.com/ScriptReference/Physics2D-queriesStartInColliders.html

    I just quickly did this as a test and it works fine: https://gyazo.com/ef3f01485b533c81f201356afa3fbf5f

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. [ExecuteAlways]
    5. public class BoxCastFilter : MonoBehaviour
    6. {
    7.     public Vector2 Direction = Vector2.up;
    8.     public float Distance = 100f;
    9.     public Vector2 BoxSize = new Vector2(1f, 1f);
    10.     public ContactFilter2D Filter;
    11.  
    12.     private readonly List<RaycastHit2D> m_Results = new List<RaycastHit2D>();
    13.  
    14.     // Update is called once per frame
    15.     void Update()
    16.     {
    17.         Physics2D.BoxCast(transform.position, BoxSize, 0f, Direction, Filter, m_Results);
    18.     }
    19.  
    20.     private void OnDrawGizmos()
    21.     {
    22.         var position = transform.position;
    23.    
    24.         Gizmos.color = Color.yellow;
    25.         Gizmos.DrawWireCube(position, BoxSize);
    26.  
    27.         Gizmos.color = Color.magenta;
    28.         if (m_Results.Count > 0)
    29.         {
    30.             var hitCenter = position + (Vector3)Direction * m_Results[0].distance;
    31.             Gizmos.DrawWireCube(hitCenter, BoxSize);
    32.             Gizmos.DrawLine(position, hitCenter);
    33.         }
    34.         else
    35.         {
    36.             Gizmos.DrawLine(position, position + (Vector3)Direction * Distance);
    37.         }
    38.     }
    39. }
     
  11. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    So in your case the filter works only from the up down, not from the sides:

    So this is good:
    upload_2023-12-3_2-17-26.png

    The settings
    upload_2023-12-3_2-17-41.png

    This is not good (it doesn't hit the sides like Platform Effector 2D)

    upload_2023-12-3_2-18-15.png

    Also another case when the check point is inside the collider and it shoots up (imagine the player jumping from under through the platform), the line should go up and right, but it collides because it registers the collision of an up normal as the filter.

    upload_2023-12-3_2-20-8.png


    Here is a video explaining the differences (The green line one is done with my code above), the magenta one is the code you posted:
    Animation.gif

    So because I want to display the trajectory the player will take when jumping, and creating a point for the line trajectory at each time step, the box might land inside the collider and get stuck while the player will never get stuck there and it will continue it's motion