Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

TilemapCollider2D pushing Sprite2D

Discussion in '2D' started by Thajocoth, Jul 28, 2019.

  1. Thajocoth

    Thajocoth

    Joined:
    Dec 10, 2014
    Posts:
    11
    I have a Sprite2D controlled by player input. When moving the player, the solid layer of Tilemap2D seems to pop the player away from it. For horizontal surfaces, this manifests as short hops. When jumping against a wall, the player receives a small force pushing them away from said wall.

    The player is made of 5 shapes. There's a BoxCollider2D taking up most of the sprite, with PolygonCollider2Ds on each of the 4 sides, each in the shape of a trapezoid, creating tapered edges. (Before the edges were tapered, instead of popping away from the surface, the player would simply stop moving, basically tripping on the geometry.)

    The 4 sides are subObjects off of the main Player object, so I can tell what part of the player is interacting with something. Here's the current code for the Player object:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Player : MonoBehaviour
    6. {
    7.     public float jumpForce;
    8.     public float floatForce;
    9.     public float walkForce;
    10.     public float maxSpeed;
    11.     public float maxFallSpeed;
    12.     public float friction;
    13.     public Feet myFeet;
    14.     bool isJumping = false;
    15.     Rigidbody2D myBody = null;
    16.  
    17.     void Start()
    18.     {
    19.         myBody = GetComponent<Rigidbody2D>();
    20.     }
    21.  
    22.     void Update()
    23.     {
    24.         if (!isJumping)
    25.         {
    26.             if (myFeet.AreOnGround())
    27.             {
    28.                 isJumping = false;
    29.                 if (Input.GetKeyDown(KeyCode.Space))
    30.                 {
    31.                     myBody.AddForce(new Vector2(0, jumpForce), ForceMode2D.Impulse);
    32.                     isJumping = true;
    33.                 }
    34.             }
    35.         }
    36.         else
    37.         {
    38.             if (Input.GetKey(KeyCode.Space))
    39.             {
    40.                 myBody.AddForce(new Vector2(0, floatForce * Time.deltaTime), ForceMode2D.Impulse);
    41.             }
    42.             else
    43.             {
    44.                 isJumping = false;
    45.             }
    46.         }
    47.         if (Input.GetKey(KeyCode.LeftArrow))
    48.         {
    49.             myBody.AddForce(new Vector2(-walkForce * Time.deltaTime, 0), ForceMode2D.Impulse);
    50.             myBody.velocity = new Vector2(Mathf.Clamp(myBody.velocity.x, -maxSpeed, maxSpeed), Mathf.Clamp(myBody.velocity.y, -maxFallSpeed, maxFallSpeed));
    51.         }
    52.         else if (Input.GetKey(KeyCode.RightArrow))
    53.         {
    54.             myBody.AddForce(new Vector2(walkForce * Time.deltaTime, 0), ForceMode2D.Impulse);
    55.             myBody.velocity = new Vector2(Mathf.Clamp(myBody.velocity.x, -maxSpeed, maxSpeed), Mathf.Clamp(myBody.velocity.y, -maxFallSpeed, maxFallSpeed));
    56.         }
    57.         else if (myBody.velocity.x > 0)
    58.         {
    59.             myBody.velocity = new Vector2(Mathf.Clamp(myBody.velocity.x - (friction * Time.deltaTime), 0, maxSpeed), Mathf.Clamp(myBody.velocity.y, -maxFallSpeed, maxFallSpeed));
    60.         }
    61.         else if (myBody.velocity.x < 0)
    62.         {
    63.             myBody.velocity = new Vector2(Mathf.Clamp(myBody.velocity.x + (friction * Time.deltaTime), -maxSpeed, 0), Mathf.Clamp(myBody.velocity.y, -maxFallSpeed, maxFallSpeed));
    64.         }
    65.         else
    66.         {
    67.             myBody.velocity = new Vector2(Mathf.Clamp(myBody.velocity.x, -maxSpeed, maxSpeed), Mathf.Clamp(myBody.velocity.y, -maxFallSpeed, maxFallSpeed));
    68.         }
    69.     }
    70. }
    71.  
    Any ideas?
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,333
    Assuming I'm following what you're describing, this comes up again and again it's related to how physics engines solve stuff. Each "tile" there is a separate physics shape(s). These are solved indivdually. For instance, placing BoxCollider2D aligned and next to each other does not produce a continuous surface, anything moving across those boxes solves contacts with each individually so it's still possible to contact corners due to forces like gravity pushing down. Indeed, during solving a collider on a surface will continually move slightly in and out of overlap but it's so small that you won't see it however this can easily cause a contact with a corner which produces a collision normal at an angle and an unexpected collision response.

    To help solve this situation, the CompositeCollider2D can not only blend multiple colliders together but it can also produce an "Outline" of those colliders. This outline is identical to the EdgeCollider2D in which it produces continuous edges.

    Try adding the CompositeCollider2D and setting to outline mode to test to see if this is your problem.

    I always thought this was a great explanation here.
     
    Thajocoth and LiterallyJeff like this.
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,333
    Tilemaps are not special at all though. Two boxes produced by a tilemap or two produced by two boxcollider2D are the same to the physics system (they are both two polygons) which only sees circles, polygons, capsules and edges. It cares nothing for how they got there. The CompositeCollider2D can blend polygons which means BoxCollider2D, PolygonCollider2D or TilemapCollider2D and produce more polygons or edges.

    You mean using discrete collision detection means you can step over the line "into" the hollow tilemap as you're using composite outline mode? Don't forget, outline mode is just that, there's no "inside" or overlap when moving across the line, same as an EdgeCollider2D and this isn't a bug. You are free to use continuous collision detection to stop this.
     
    LiterallyJeff and Thajocoth like this.
  4. Thajocoth

    Thajocoth

    Joined:
    Dec 10, 2014
    Posts:
    11
    That seems to have fixed it, but "Polygons" works better than "Outlines". The problem with outlines is that a higher speed collision will partially embed the player into the impacted tiles with no way to move them back out.

    Thanks for your help.

    I don't really see any case where somebody would want a TilemapCollider2D without a CompositeCollider2D with it. I think it would be wise for Unity to make that more automatic...
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,333
    For the reason I detailed above. There is no "out" as there's no inside, simply a line. Did you use continuous collision detection as I suggested above because if you get the same issue then there's a serious problem with your movement code as it should be impossible for continuous to move into a line.

    Choice and adding a single component (CompositeCollider2D) isn't hard. In the case where you're adding/removing tiles in a dynamic tilemap, using a composite can be very expensive. Separating tilemaps into static/dynamic this way is important.
     
    LiterallyJeff likes this.