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

Problem with collision areas

Discussion in 'Scripting' started by Henchazila, Jan 11, 2020.

  1. Henchazila

    Henchazila

    Joined:
    Oct 23, 2019
    Posts:
    2
    Hi,

    I want to create a topdown sliding puzzle similar to the ice cave puzzles in pokemon. See example:


    In order to emulate this behavior, I made the following script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System;
    5.  
    6. public class SoldierController : MonoBehaviour
    7. {
    8.     public Rigidbody2D rb;
    9.     public float speed = 1.0f;
    10.     public Vector2 movement;
    11.     public string lastCollName = "";
    12.     bool isMoving = false;
    13.     int xMove = 0;
    14.     int yMove = 0;
    15.     string lastDirection = "";
    16.     public float Distance = 50.0f;
    17.  
    18.  
    19.  
    20.  
    21.     void Start()
    22.     {
    23.         rb = this.GetComponent<Rigidbody2D>();
    24.     }
    25.  
    26.     void Update()
    27.     {
    28.         if (!this.isMoving)
    29.         {
    30.             if (Input.GetKey("d") && this.lastDirection != "d")
    31.             {
    32.                 this.xMove = 1;
    33.                 this.yMove = 0;
    34.                 this.lastDirection = "d";
    35.                 this.isMoving = true;
    36.             }
    37.             if (Input.GetKey("a") && this.lastDirection != "a")
    38.             {
    39.                 this.xMove = -1;
    40.                 this.yMove = 0;
    41.                 this.lastDirection = "a";
    42.                 this.isMoving = true;
    43.             }
    44.             if (Input.GetKey("w") && this.lastDirection != "w")
    45.             {
    46.                 this.xMove = 0;
    47.                 this.yMove = 1;
    48.                 this.lastDirection = "w";
    49.                 this.isMoving = true;
    50.             }
    51.             if (Input.GetKey("s") && this.lastDirection != "s")
    52.             {
    53.                 this.xMove = 0;
    54.                 this.yMove = -1;
    55.                 this.lastDirection = "s";
    56.                 this.isMoving = true;
    57.             }
    58.         }
    59.  
    60.         Vector2 position = this.rb.position;
    61.         position.x = position.x + this.speed * this.xMove * Time.deltaTime;
    62.         position.y = position.y + this.speed * this.yMove * Time.deltaTime;
    63.  
    64.         this.rb.MovePosition(position);
    65.     }
    66.  
    67.     void OnCollisionEnter2D(Collision2D collision)
    68.     {
    69.         Collider2D collider = collision.collider;
    70.         Vector2 contactPoint = collision.contacts[0].point;
    71.         Vector2 center = collider.bounds.center;
    72.         float collisionDistance = (float) Math.Pow(contactPoint.x - center.x, 2.0) +
    73.                                   (float) Math.Pow(contactPoint.y - center.y, 2.0);
    74.  
    75.         if (this.lastDirection == "w" || this.lastDirection == "s")
    76.         {
    77.             Debug.Log("X-axis collision. Distance is: " + collisionDistance);
    78.             if (collisionDistance > this.Distance)
    79.             {
    80.                 return;
    81.             }
    82.         }
    83.         if (this.lastDirection == "a" || this.lastDirection == "d")
    84.         {
    85.             Debug.Log("Y-axis collision. Distance is: " + collisionDistance);
    86.             if (collisionDistance > this.Distance)
    87.             {
    88.                 return;
    89.             }
    90.         }
    91.         this.isMoving = false;
    92.         this.lastCollName = collision.gameObject.name;
    93.         this.xMove = 0;
    94.         this.yMove = 0;
    95.         Debug.Log("Collision with " + this.lastCollName);
    96.     }
    97.  
    98.     private void OnCollisionExit2D(Collision2D collision)
    99.     {
    100.         Debug.Log("out of " + this.lastCollName);
    101.     }
    102. }
    103.  

    Basically, I give the unit controller perpetual motion in a direction (and store the last input direction). As long as the unit is moving, it is unaffected by keyboard input. Once the unit collides with an object, it is stopped and can once again be affected by keyboard input.

    I have two problems with this:

    1. Going close to objects can still trigger collisions with the corner of an object, then the script stops the unit's movement entirely. I tried to distinguish between "true" collisions and these corner cases by checking the combined XY distance (in a corner case both X and Y of the contact point should be further away from the collider's center), but the distances I get are inconsistent every time, so I can't make a real distinction.

    2. The unit goes slightly in the collision zone of the other object before stopping completely. This messes with the 2D isometric "square" feel of the ice cave puzzle and is the main reason I got the problems mentioned above. I thought about making the unit move a bit backwards everytime it collides with something, but it's unesthetic, and I can't tell the unit how much to move back after it enters a collision zone, because it is also inconsistent.

    Does anyone have an idea of how to deal with this? I feel like I'm missing something fundamental about how the system works.

    Thanks in advance.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,971
    You probably want to control this behavior entirely in code, using a 2-dimensional array of cells representing your tiles, then output it using the sprites you have above. Using colliders for the logic is possible but finnicky, and can result in exactly the finnickyness you see. When your world is a perfect grid, just store that grid data in a 2D array and reliably process it yourself to determine how far to go, etc.
     
  3. Henchazila

    Henchazila

    Joined:
    Oct 23, 2019
    Posts:
    2
    Is there a native way to split the scene to a predefined grid of some square size? Alternatively, can I measure grid length/width in the scene viewer and trust it will remain consistent through game runs?

    Also, is there a native way to move towards a predefined point?

    Thanks again for all your help
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,971
    Unity's Tilemap system is basically made exactly for this purpose:

    https://docs.unity3d.com/Manual/class-Tilemap.html

    There are plenty of great Youtube tutorials on Tilemaps. The concept of a tilemap has been in use since the late 1970s in games.

    You can just do the calculations yourself, or you can use a helper method like:

    https://docs.unity3d.com/ScriptReference/Vector2.MoveTowards.html