Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

It looked really simple or maybe i'm dumb

Discussion in 'Scripting' started by Baalhug, Apr 5, 2019.

  1. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    Hi:

    I'm trying to move objects programatically through a random generated maze. The objects movement is working ok because the center of every cell in this maze can be reached (they have 3 walls around max), so in every cell the objects check (raycast x, -x, z, -z) where there are walls around and pick a direction free to go, where there is no wall. Then I move the object by transform.position = Vector3.Lerp(iniPos, destPos, delta) where destPos is free direction * distance. Distance is always 10 (the distance between the cells).

    This is working FINE.

    Now the problem comes when objects collide with each other. When this happens I want both to come back to the cell where they come from, this is easy by doing:
    Vector3 aux = destPos;
    destPos = iniPos;
    iniPos = aux;
    delta = 1 - delta;
    This works good too, but collision detection between objects is being a pain in the ass last few days. I tried 2 solutions with lots of modifications but I can't get it working nicely. The worst of all is I'm not new to unity or programming :d

    So, as I wanted full control on objects movement I discarded physics collisions management at first. When the lerp between cells is happening I cast smaller rays (a little bigger than objects themselves) to check for other objects around, the problem is they get tangled because they change direction multiple times due to multiple raycast hits (one object with the other and viceversa). I tried disabling raycasting temporarily (0.1 seconds) after a positive hit, and even shortening the distance of rays temporarily but it keeps happening or the objects start moving together one inside the other, even if I change the movement of both objects by the object who detects the hit first.
    So I rewrote everything using kinematic rigidbodies and Rigidbody.MovePosition and managing collisions with OnCollisionEnter, but then I came with a lot of other problems because the objects are children of the own maze and localPosition is kinda impossible to manage with lerp, which i need for a clean movement between cells. I can't get deviations from the center of the cells due to unaccurate calculations (and physics movement tend to this), or to check wether the object is near to the center of the destination cell.
    So I came back to the raycast solution but I can't get rid of tangleing behaviour of objects no matter what.

    Have you face a similar problem or know a better solution? I add my code so you can visualize where I am now:

    Code (CSharp):
    1.  
    2. IEnumerator MovingObj()
    3.     {
    4.         yield return null;  // this yield here waits for the first update to wait for the walls to be completed and can be hit
    5.  
    6.         speed = 0.5f; // Random.Range(2, 10);
    7.  
    8.         while (true)
    9.         {
    10.             bool[] freeDirs = FreeDirections(10);      // gets directions with no walls
    11.             myDirection = NewDirection(freeDirs, myDirection);     // choose direction from free directions array
    12.             iniPos = transform.localPosition;
    13.             destPos = transform.localPosition + myDirection * 10;
    14.  
    15.             if (myDirection != Vector3.zero)
    16.             {
    17.                 delta = 0;
    18.                 while (delta < 1)
    19.                 {
    20.                     if (detectionEnabled)
    21.                     {
    22.                         DetectOtherObjs();
    23.                     }
    24.                     transform.localPosition = Vector3.Lerp(iniPos, destPos, delta);
    25.                     delta += speed * Time.deltaTime;
    26.                     yield return null;
    27.                 }
    28.             }
    29.             yield return null;
    30.         }
    31.     }
    32.  
    33.  
    34. void DetectOtherObjs()
    35.     {
    36.         float dist = transform.lossyScale.x + 0.3f;
    37.         LayerMask lay = LayerMask.GetMask("Objs");
    38.         RaycastHit[] hits;
    39.         bool colision = false;
    40.         for (int i=0; i < 360; i = i + 15)
    41.         {
    42.             if (colision) break;
    43.             transform.Rotate(new Vector3(0, i, 0));
    44.             hits = Physics.RaycastAll(transform.position, Vector3.forward, dist, lay);
    45.             foreach (RaycastHit hit in hits)
    46.             {
    47.                 if (hit.transform.gameObject != transform.gameObject)
    48.                 {
    49.                     aux = destPos;
    50.                     destPos = iniPos;
    51.                     iniPos = aux;
    52.                     delta = 1 - delta;
    53.                     myDirection = -myDirection;
    54.  
    55.                     Obj otherObjScript = hit.transform.GetComponent<Obj>();
    56.                     otherObjScript.aux = otherObjScript.destPos;
    57.                     otherObjScript.destPos = otherObjScript.iniPos;
    58.                     otherObjScript.iniPos = otherObjScript.aux;
    59.                     otherObjScript.delta = 1 - otherObjScript.delta;
    60.                     //otherObjScript.StartCoroutine(DisableDetectionTemp());
    61.                
    62.                     otherObjScript.myDirection = -otherObjScript.myDirection;
    63.  
    64.                     colision = true;
    65.                     //StartCoroutine(DisableDetectionTemp());
    66.                     break;
    67.                 }
    68.             }
    69.         }
    70.     }
    71.  
     
    Last edited: Apr 5, 2019
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Can you simplify the problem by instead of trying to correct an error, never let the error happen in the first place? Check the wanted movement direction 2 tiles ahead, and if there's a moving object there, choose a new direction instead of continuing along the path? You could expand it further by evaluating the object's chosen direction to see if they both want to move to the same tile as well.
     
    SparrowGS likes this.
  3. dontdiedevelop

    dontdiedevelop

    Joined:
    Sep 18, 2018
    Posts:
    68
    Raycast is a one directional cast so i think you need to cast with Physics.OverlapSphere or Physics.OverlapBox?
     
    Baalhug likes this.
  4. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    Grozzler, you think it's easy too! That approach of your have 2 problems anyway:
    - That situation won't fix the problem, why that detection would avoid the tangleing effect? I'm doing the same thing in all directions, when the first hit is possitive no more hits will be cast.
    - If you only check "ahead" you will collide with objects coming perpendicular to you, actually you may never detect them and you will pass through them.

    dontdiedevelop, your idea is interesting though the premise is false, I'm casting 24 rays in all directions, 15 degrees delta between them. But again, it's an interesting idea because may be an intermediate solution between collision of rigidbodies and raycasting. I'm gonna read the docs, I just hope it's not the same as scale the collider and use normal collisions.
     
  5. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    I'm doing it now with objects with same speed, but the final solution will involve every object will have a different speed, so I'd have to do a lot of maths and I don't like the approach, but if I can´t find another one I will be forced to do that, so thanks for the suggestion.
     
  6. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    Let's clarify this:
    I understand (as you do) the logical way to do this is to cast a ray in every frame detecting objects around you. I'm doing that. The problem is, for any reason, maybe handleing this from a coroutine, maybe a different thing I can´t figure when I debug step by step (everything looks fine there), the objects get tangled or bypass each other, and this happens kinda randomly so I suppose this is due the position of every object in each frame (actually the coroutine yield return). I'm gonna try to move all that to fixedupdate and i will feedback.
     
  7. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    A better solution, since you're already using grid based movement anyways, is to ditch the raycasting entirely and switch to a grid as your underlying data structure. Then you could mark each tile in the grid as occupied/available as objects move about, and put special states like "object is moving toward here" as well.
     
  8. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    After moving the coroutine to fixedupdate function the results are exactly the same, once two objects enter in range of raycast detection they get tangled by each other, start vibrating and stop. The vibration indicates when the first detection is possitive and the direction of the object is changed to opposite, right after that there is another detection which makes the object turns around again and again and again... But after the first positive detection and direction change I'm calling a coroutine which disables further detections for 1 frame, but the problem persists. While debugging I dont see anything strange. The trace does not enter the coroutine but it is disabling the detection properly. Next frame it does not cast rays, but when I run the game the objects get tangled again. There has to be a programming error somewhere.
     
  9. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    That definetly would work, but it has some problems too. If I mark a cell as "occupied" when an object enters and "free" when an object leaves, I must handle again at least 2 border situations:
    - An object is about to enter an occupied cell (it hasn´t entered yet), and another object is about to leave in the opposite direction. In that moment both objects are touching each other, actually overlapping almost a half. I'd have to include the dimensions of every object in the calculation of "occupied" cell, so it becomes occupied when the object touches it, and not when the pivot is inside.
    - Now the other situation is an object about to touch a cell which has been tagged as occupied last frame (another object has just touched it). First object will turn around at a long distance (1 tile) and I dont like that effect very much. Still may be the only solution if I dont find another. Well, by "only solution" I mean a solution that does not take another 3 or 4 days to behave properly.
     
  10. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    I think I know what's going on. The problem MUST be the order of execution of all the coroutines (or fixedupdates). As I need both objects to change their directions opposite ways (turn around and go back) I'm changing the direction of the script of both objects from the script of the object who finds a positive detection first, to avoid that object to turn around and the other object continue following the first and maybe making more collisions. BUT at that frame every object will execute its code. So in a previous frame obj1 and obj2 were both far from detecting each other, so they both move delta1 + delta2 to get closer. In this frame obj1 detects obj2, change both directions and move delta1 in the opposite direction. When obj2 casts for detection (before moving, of course) it still detects obj1 because it only separated delta1 from obj2, instead of delta 1 + delta2. Then obj2 change both directions again and the dance begins.
    I'm calling coroutine to avoid more detections in obj1 and in obj2 from obj1, but that coroutine will execute AFTER fixedupdate of obj2.
     
  11. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    Well, it wasn't the case. Same behaviour. I'm gonna rebuild again using
     
  12. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    Well, the problem had to be in raycast, because now using Physics.OverlapSphere I got it working. Instead of disabling collisions what I do now after a collision is to increment delta by 2*time.deltatime so objects will move apart a little bit faster on the frame they collide. Thank you, guys.
     
  13. Baalhug

    Baalhug

    Joined:
    Aug 12, 2013
    Posts:
    32
    Nah, it keeps happening
     
  14. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    You're going at this all wrong imo, you need to seperate your game state from the visual world, change the game state from the input and update the visual representation to reflect the new information.