Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

(SOLVED) Issue with converting tutorial code

Discussion in 'Scripting' started by Denisowator, Oct 1, 2017.

  1. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    I decided to make a new thread for this, since the last thread's title was very specific, and I didn't want to confuse new readers.

    Like I said in my previous thread, I'm following this tutorial (by now I've finished following it), and I had some errors. I got those solved, but there are things I still need to to with it.

    Basically I need to integrate finding a closet object into it, for which I've been following this tutorial. The tutorial looks at 3D, and I need to have it work for 2D.

    Here are both my scripts (altered from the tutorial ones):
    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3.  
    4. public class FindClosestObject : IComparer {
    5.  
    6.     private Transform compareTransform;
    7.  
    8.     public FindClosestObject (Transform compTransform) {
    9.         compareTransform = compTransform;
    10.     }
    11.  
    12.     public int Compare (object x, object y) {
    13.         var xCollider = x as Collider2D;
    14.         var yCollider = y as Collider2D;
    15.  
    16.         Vector3 offset = xCollider.transform.position - compareTransform.position;
    17.         float xDistance = offset.sqrMagnitude;
    18.  
    19.         offset = yCollider.transform.position - compareTransform.position;
    20.         float yDistance = offset.sqrMagnitude;
    21.  
    22.         return xDistance.CompareTo(yDistance);
    23.     }
    24. }
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class Sense : MonoBehaviour {
    5.  
    6.     public Vector3 boxSize = new Vector3 (0.6875f, 2.25f, 5f);
    7.     public LayerMask checkLayers;
    8.  
    9.     private void Update () {
    10.         if (Input.GetKeyDown (KeyCode.Space)) {
    11.             Collider2D[] colliders = Physics2D.OverlapBoxAll(transform.position, boxSize, checkLayers);
    12.             Array.Sort(colliders, new FindClosestObject(transform));
    13.  
    14.             foreach (Collider2D item in colliders) {
    15.                 Debug.Log(item.name);
    16.             }
    17.         }
    18.     }
    19.  
    20.     private void OnDrawGizmos() {
    21.         Gizmos.DrawCube(transform.position, boxSize);
    22.     }
    23. }
    The person in the tutorial doesn't talk much, and doesn't comment the code btw.
    The way I understand it, is it gets the transform or collider of objects within the "Overlap" object, and also checks that the targeted object is in a specific Layer. It sorts them within the array in order of range to the object holding the Sense script. And then prints the objects' names to the console.

    Before trying to convert it to 2D, I put in some 3D objects in the scene, and attached the Sense script to a cube, and it works exactly like in the video.

    But I need it to work for 2D colliders.

    I tried converting it by simply changing all Collider into Collider2D, Physics into Physics2D and changing from an OverlapSphere to OverlapBoxAll (I did All because otherwise it's not compatible with the "Collider2D" array for some reason, and gives me an error saying "Cannot convert from UnityEngine.Collider2D to UnityEngine.Collider2D[]").

    It does seem to work somewhat, but instead of outputting only the objects in the layer I set (which is "Interactible"), it outputs a very specific set of any objects. What I mean by that is, I have a lot of sprite elements in my scene (all with 2D colliders), some have scripts attached and some don't. And they are all set out in multiple sorting layers (of the Sprite Renderer) like Background, Foreground, and Midground.

    And it doesn't seem to only detect ones with either scripts or no scripts, or ones on specific Sorting Layers. It also doesn't detect random objects.

    To make this clearer, here's a list of some of the objects it detects:
    • LeftDoor
    • House(Outside)
    • House(Inside)
    • BackDoor
    • Stairs
    • Shelf
    All these objects are different, in that they aren't all in the same Sorting Layer, neither are they all in the same Layer. And only some of them have scripts attached (not the two scripts I put above, but just regular scripts that have to do with their functionality in the game). And of course, there are some objects it doesn't detect, which are also all in different layers and such.

    So even though it seems to have picked a random assortment of objects, it's not completely random. And I have no idea why it's happening.
     
    Last edited: Oct 6, 2017
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,340
    First of all:
    This is the correct thing to do, and it makes sense. Spheres doesn't exist in 2D, after all! If you want exactly the same as a sphere in 3D (ie. see as far in any direction), use a circle. There's overlapCircle methods.

    What's causing the error is automatic casts. Here's the signature of OverlapBoxAll:

    Code (csharp):
    1. public static Collider2D[] OverlapBoxAll(Vector2 point, Vector2 size, float angle, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity);
    That's a mouthful, but note that the third argument is angle. That means, it's used to determine the angle of the box used to overlap things. You're calling the method like this:

    Code (csharp):
    1. Physics2D.OverlapBoxAll(transform.position, boxSize, checkLayers);
    Which means that you're using checkLayers as the argument to angle.

    You'd expect that to simply not compile, since angle is supposed to be a float, and checkLayers is a LayerMask. But LayerMask has an implicit conversion to int, which again has an implicit conversion to float, so your checkLayers is interpreted as an angle. Which is all kinds of wrong.

    To fix it, just give an argument for the angle:

    Code (csharp):
    1. Physics2D.OverlapBoxAll(transform.position, boxSize, 0f, checkLayers);
    Although, again, I suggest using a circle unless your game really needs senses to work in a rectangular fashion.
     
    Denisowator likes this.
  3. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Thank you. I'm using a code editor that actually lets me insert that structure into the code, and for some reason there's a version without the "float angle" in it. I must've used that one.

    And yes, I do need it to be a box, since I want it to only check things at the player's position, and the player uses a box collider.

    btw, do you know how how I could make it only output the very closest object, and not all objects in order?

    I'm planing on using this detection system for dialogue execution control (executing dialogue for the closest interactible object), so I don't really need an array, and multiple objects to be detected at once. I just need the the one closest to the player.

    Unless I can just make my dialogue system call for the first object in the array.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,340
    Since the array gets sorted, the closest element is colliders[0]. That is, if there's anything in there at all.


    For interaction detection, I recommend not only using distance, but also angle. If the player's standing next to a sign and looking at an NPC, they'll expect to talk to the NPC, not look at the sign, even if the sign's technically closer.
     
    Denisowator likes this.
  5. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Thanks. :)