Search Unity

Colliders at wrong position after building the game?

Discussion in '2D' started by gsm84, Aug 10, 2018.

  1. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    Hi everyone! Currently I'm working on my first Unity game and I've ran into some weird stuff, and I wasn't able to find a solution on my own.
    So.. the game is a 2D endless runner, there's a character who can jump over or slide under the obstacles. In the editor, all collisions are working fine, but when I'm trying to build the game, the colliders are in wrong position.
    For example: there's an item, with an X position: 25. I can jump over it, everything's fine.
    But when I'm testing the built version (no matter if it's on Windows or on Android) I'm running into an invisible wall at position ~20 (I don't know exactly, but before the item). I can't jump over it, I can't slide under it, nothing.
    After disabling that item's collider, I can run past it in the built version too. What can be the problem?
    Thanks in advance for your answers!
     
  2. jc-drile77

    jc-drile77

    Joined:
    Jul 1, 2014
    Posts:
    230
    That´s indeed weird.
    Could we see some code? Perhaps the part that handles the jump
     
  3. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    That part is really basic, in the character's Update function I'm using:

    Code (CSharp):
    1. if (isDead == false) {
    2.     if (Input.GetMouseButtonDown (0) && AnimatorIsPlaying ("CharRun")) {
    3.         anim.Play ("CharJump");
    4.     }
    5. }
    AnimatorIsPlaying is a function that returns true, if "anim.GetCurrentAnimatorStateInfo (0).IsName (stateName)" returns true (and false if it returns false).
    I'm not using AddForce or anything else to modify the character's Y position, because the animation comes from a png image sequence, where the character jumps and leaves the floor. So when I'm playing the animation, it looks fine.
    To check collisions, I'm using a script which creates a PolygonCollider2D in every frame, so it follows the character's movement.
    And as I've said, when I'm testing the game in the editor, the colliders are working fine. If I'm not jumping, then I'm running into the enemy, and I'm dead. But when I'm jumping over it, it's fine. But after building and running the game I'm running into walls which I can't get past, no matter if I'm running, jumping or sliding.
    I'm not sure if these informations will help you or not.
     
  4. jc-drile77

    jc-drile77

    Joined:
    Jul 1, 2014
    Posts:
    230
    There must be something wrong somewhere in your code. Let´s try to find it together. :)

    How do you apply x speed to your character?

    I believe the problem might be that If you are generating a new PolygonCollider2D in every frame the one which gets generated when you are about to collide with the wall is different (in each device or mode) because it is frame dependent.

    For instance if your game is running at 60fps at a certain time you will get a collider that´s different than if it were running at 30fps.The collider that is colliding at 60 fps would be generated 30 frames later if the game were running at 30fps.
    Do you see where I am trying to get?

    The solution would be to move the PolygonCollider2D generation code from Update() to FixedUpdate() which will make it frame independent and constant (every 0.02s by default), furthermore everything related to physics should be inside FixedUpdate(), and being PolygonCollider2D a physics thing thats his place :D

    Let me now if this has helped you
     
    Last edited: Aug 11, 2018
  5. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    Technically, my character is standing (so I'm not moving it's X position). I'm moving the background and the obstacles with this script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ScrollingObject : MonoBehaviour {
    6.  
    7.     private Rigidbody2D rb2d;
    8.  
    9.     void Start ()
    10.     {
    11.         rb2d = GetComponent<Rigidbody2D> ();
    12.     }
    13.    
    14.     void Update ()
    15.     {
    16.         if (GameControl.instance.gameOver == true)
    17.         {
    18.             rb2d.velocity = Vector2.zero;
    19.         }
    20.         else if (!Hero.AnimatorIsPlaying ("CharStandUp")) {
    21.             rb2d.velocity = new Vector2 (GameControl.instance.scrollSpeed, 0);
    22.         }
    23.     }
    24. }
    And I'm generating the PolygonCollider2D with this script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CreateCollider : MonoBehaviour {
    6.  
    7.     public bool iStrigger ;
    8.  
    9.     private SpriteRenderer spriteRenderer;
    10.     private List<Sprite> spritesList;
    11.     private Dictionary<int, PolygonCollider2D> spriteColliders;
    12.     private bool _processing ;
    13.  
    14.     private int _frame ;
    15.     public int Frame {
    16.         get { return _frame; }
    17.         set {
    18.             if (value != _frame) {
    19.                 if (value > -1) {
    20.                     spriteColliders [_frame].enabled = false;
    21.                     _frame = value;
    22.                     spriteColliders [_frame].enabled = true;
    23.                 } else {
    24.                     _processing = true;
    25.                     StartCoroutine (AddSpriteCollider (spriteRenderer.sprite));
    26.                 }
    27.             }
    28.         }
    29.     }
    30.  
    31.     private IEnumerator AddSpriteCollider (Sprite sprite)
    32.     {
    33.         spritesList.Add (sprite);
    34.         int index = spritesList.IndexOf (sprite);
    35.         PolygonCollider2D spriteCollider = gameObject.AddComponent<PolygonCollider2D> ();
    36.         spriteCollider.isTrigger = iStrigger;
    37.         spriteColliders.Add (index, spriteCollider);
    38.         yield return new WaitForEndOfFrame ();
    39.         Frame = index;
    40.         _processing = false;
    41.     }
    42.  
    43.     private void OnEnable ()
    44.     {
    45.         spriteColliders [Frame].enabled = true;
    46.     }
    47.  
    48.     private void OnDisable ()
    49.     {
    50.         spriteColliders [Frame].enabled = false;
    51.     }
    52.  
    53.     private void Awake ()
    54.     {
    55.         spriteRenderer = this.GetComponent<SpriteRenderer> ();
    56.  
    57.         spritesList = new List<Sprite> ();
    58.  
    59.         spriteColliders = new Dictionary<int, PolygonCollider2D> ();
    60.  
    61.         Frame = spritesList.IndexOf (spriteRenderer.sprite);
    62.     }
    63.  
    64.     private void LateUpdate ()
    65.     {
    66.         if (!_processing)
    67.             Frame = spritesList.IndexOf (spriteRenderer.sprite);
    68.     }
    69.  
    70. }
    Changing the LateUpdate to FixedUpdate doesn't seem to help.
     
  6. jc-drile77

    jc-drile77

    Joined:
    Jul 1, 2014
    Posts:
    230
    It does not matter whether you are moving the character or the obstacles. I am still pretty sure it is what I told you.
    This piece of code should also be on FixedUpdate.

    Code (CSharp):
    1.    void Update ()
    2.     {
    3.         if (GameControl.instance.gameOver == true)
    4.         {
    5.             rb2d.velocity = Vector2.zero;
    6.         }
    7.         else if (!Hero.AnimatorIsPlaying ("CharStandUp")) {
    8.             rb2d.velocity = new Vector2 (GameControl.instance.scrollSpeed, 0);
    9.         }
    10.     }
    There you set the objects x speed to the given scrolling speed, but if there have been n physics steps between two consequent frame the speed will have slowed down because physics work that way ( friction, gravity...).

    As I stated above everything related to physics should be placed in FixedUpdate.

    Anyways @jeffreyschoch is the Jon Skeet of Unity´s 2D forum, so if he can confirm this would be really thankful :)
     
  7. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    I've tried it, but still no success.
     
  8. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    Now I've tried to limit FPS to 30, but it didn't help either.
     
  9. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    That is the highest praise I've ever received lol.

    Yeah in general it's best to put physics updates in FixedUpdate to stay synced with the physics step, but it only really becomes problematic when your function compounds, things like AddForce, because that can build up over multiple iterations. In the case of setting velocity, it would just set it many times to the same value, which doesn't really hurt anything but isn't necessary. Consistency and organization is important.

    @gsmpcgame your implementation is a bit counter-intuitive to how Unity is used in most cases, so it's difficult to tell where the problem lies just from reading your descriptions.

    Traditionally the world and obstacles stay still while your player moves through it, which means you only have to worry about one object having velocity and constant simulation. You may be making things harder for yourself by going this route.

    What is the reason to generate new colliders at runtime? Do they really need to be polygon colliders, and not a single capsule or something?

    Could you create them at edit time and have a list of sprites and a list of colliders, with a single index to access both lists?

    Suggestions aside, you should start at the root of your problem to make sure you're following the right trail.

    You said that you're hitting an invisible wall? Could you add some kind of visual representation to your colliders so that you can see them in the editor and build? Maybe upload a gif or video of the problem happening?
     
    Last edited: Aug 13, 2018
    jc-drile77 likes this.
  10. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    As I've said, I'm brand new to the world of Unity. I followed a tutorial from Unity's Youtube channel and this was the way he solved the movement.
    But I think it's practical for me, because there will be 3 layers of background: a distant one, a not too distant one and a close one. I'm adding the modified ScrollingObject script to them (with a different scrollSpeed), so the distant things will move slower than the closer things. If I'd move only my character and not the world, how could I achieve this?

    I have about 100 frames of animation, so creating the colliders for them one by one manually would be inconvenient for me.

    I've made two videos to show you what I'm talking about. One from the editor, and one after building the game.
    I guess it won't help you identifying the problem, but you'll see it in action.
    If it's possible, I'm willing to send you the project folder, maybe it would be easier to find the mistake I've made.


     
  11. jc-drile77

    jc-drile77

    Joined:
    Jul 1, 2014
    Posts:
    230
    The other way is using a perspective camera.
    Here you can see how it works (and review your method too)

    It´s quite common to just use a CapsuleCollider2D because at the bottom its smooth as a circle but can be as tall as needed. Generating so many colliders and destroying them in such a small time might affect performance in the bad way.

    Having said that let´s see if jeffrey can spot something in your videos because I cannot. :(
     
  12. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Based on your videos, I can only guess that the snake collider is a larger collider than is intended (the player seems to die at the extent of the snake's attack animation), but that's a shot in the dark.

    Yeah generally speaking if you don't need very accurate shape-collision results, you can just use a capsule. Having a consistent physics shape actually makes the game feel more consistent to the player, because the player can get a feel for how big the character is, how to time jumps etc. without animation frame number being a factor. (Not to mention how inefficient creating/using tons of polygon colliders can be)

    I just quickly whipped up this script that should be able to highlight your polygon vertices at runtime. This isn't optimized very well, but it should get the job done I hope. (it may lag your game at first, or throughout, depending on your collider vertex count)

    Add this component next to your CreateCollider component, give it a small sprite to use as a vertex icon, and if it works in the editor, make a build and take a look.
    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Linq;
    4. using System.Collections.Generic;
    5.  
    6. public class VisualizePolygonCollider2D : MonoBehaviour
    7. {
    8.     public Sprite vertexSprite; // sprite to put on each vertex
    9.     public float spriteScalar = 1;
    10.  
    11.     private List<SpriteRenderer> renderers = new List<SpriteRenderer>();
    12.     private Transform rendererParent;
    13.  
    14.     private PolygonCollider2D activeCollider;
    15.     private SpriteRenderer thisRenderer;
    16.  
    17.     private void Awake()
    18.     {
    19.         rendererParent = new GameObject("DebugRenderers").transform;
    20.         rendererParent.SetParent(transform);
    21.         rendererParent.localPosition = Vector3.zero;
    22.         thisRenderer = GetComponent<SpriteRenderer>();
    23.     }
    24.  
    25.     private void LateUpdate()
    26.     {
    27.         if (activeCollider == null || !activeCollider.isActiveAndEnabled)
    28.         {
    29.             activeCollider = GetActiveCollider();
    30.             RecycleRenderers();
    31.             RenderVertices(activeCollider);
    32.         }
    33.     }
    34.  
    35.     private PolygonCollider2D GetActiveCollider()
    36.     {
    37.         return GetComponents<PolygonCollider2D>().FirstOrDefault(col => col.isActiveAndEnabled);
    38.     }
    39.  
    40.     private void RenderVertices(PolygonCollider2D polyCollider)
    41.     {
    42.         int vertCount = polyCollider.points.Count();
    43.         while (vertCount > renderers.Count())
    44.         {
    45.             renderers.Add(CreateNewRenderer());
    46.         }
    47.  
    48.         for (int i = 0; i < vertCount; i++)
    49.         {
    50.             SpriteRenderer renderer = renderers[i];
    51.             renderer.transform.localPosition = polyCollider.points[i];
    52.             renderer.gameObject.SetActive(true);
    53.         }
    54.     }
    55.  
    56.     private void RecycleRenderers()
    57.     {
    58.         foreach (SpriteRenderer renderer in renderers)
    59.         {
    60.             renderer.gameObject.SetActive(false);
    61.         }
    62.     }
    63.  
    64.     private SpriteRenderer CreateNewRenderer()
    65.     {
    66.         SpriteRenderer newRenderer = new GameObject("VertexSprite").AddComponent<SpriteRenderer>();
    67.         newRenderer.sprite = vertexSprite;
    68.  
    69.         if (thisRenderer != null)
    70.         {
    71.             newRenderer.sortingLayerID = thisRenderer.sortingLayerID;
    72.             newRenderer.sortingOrder = thisRenderer.sortingOrder + 1;
    73.         }
    74.         newRenderer.transform.SetParent(rendererParent);
    75.         newRenderer.transform.localScale = Vector3.one * spriteScalar;
    76.         return newRenderer;
    77.     }
    78. }

    I do work a full-time job so I'll check back when I can.
     
    jc-drile77 likes this.
  13. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    Thanks for the answer and for the script too!
    I might be super lame, but I'm not sure how to do this: "give it a small sprite to use as a vertex icon".
     
  14. jc-drile77

    jc-drile77

    Joined:
    Jul 1, 2014
    Posts:
    230
    Just drag and drop the sprite you want to use to represent vertices in the slot.

    Code (CSharp):
    1.     public Sprite vertexSprite; // sprite to put on each vertex
    In the inspector of course.
     
  15. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    Oh, thanks, I got it.

    I've made 2 new videos which are showing the result. As it seems, in the editor the CreateCollider script works more or less fine, but in the built version it doesn't work as it should be.


     
  16. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Thanks @jc-drile77

    Cool, thanks for the new videos. I think I may have an idea what's happening.

    Your script is relying on the fact that when you add a polygon collider in the editor, it will automagically use the sprite's outline to form the shapes if a SpriteRenderer is present. I'm not 100% certain, but I have a suspicion that doing that in a build won't work (maybe it used to?). I think it will only work in the editor. So in a build you just get empty colliders unless you assign them vertices.

    I came across this free asset which seems to be a great solution for the approach you're trying to take here:

    https://assetstore.unity.com/packag...on-collider-52265?aid=1011lGbg&utm_source=aff


    If you watch the video, you'll see that asset can generate your polygon colliders for you in the editor during play for your animations, and save them for later use in builds without having to generate them at runtime.

    I realize you're very new to Unity so the thought of integrating a new asset might seem overwhelming, but it should be as simple as downloading the asset package, importing it into your project, and adding the AdvancedPolygonCollider component onto your object. You won't need to create colliders with your CreateColliders script anymore. Just watch the video and listen to the dude explain what each option does, you should be set.
     
    jc-drile77 likes this.
  17. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    Thanks! I'll give it a try tomorrow (it's 11 PM here), and I'll report back with the result.
     
  18. gsm84

    gsm84

    Joined:
    Aug 10, 2018
    Posts:
    16
    @jc-drile77 and @jeffreyschoch Thank you very much for your help!

    The AdvancedPolygonCollider asset works fine, so finally I can continue working on the game. I can't tell how grateful I am to you guys for your help.

     
    LiterallyJeff likes this.