Search Unity

How to convert manual movement to automatic movement (aka Snake) in a 2D array grid and turn if coll

Discussion in 'Scripting' started by coolbird22, Sep 17, 2014.

  1. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    I'm moving my player object through the maparray grid manually using the WASD keys. How do I make the player object move in a straight line automatically (horizontal or vertical) and change directions based on the WASD keys, very much like snake. How to make the object to be unable to move in reverse of whatever direction it is going in ? Eg. If going right, unable to go left. Lastly, every tile inside the entire grid is assigned a value of either 0 or 1, with 0 being a walkable tile, and 1 being an unwalkable tile. Since the object is moving continuously in a direction now, how do I make it turn whenever the next tile that it detects is the unwalkable tile. Eg. If moving upwards, the object detects a tile with a value of 1 and takes a left turn by itself, if the tile on the left has a value of 0, and if that tile has a value of 1 as well, it turns to its' right.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using DG.Tweening;
    4. using System.Collections.Generic;
    5. public class PlayerMove : MonoBehaviour {
    6.     public GameObject levelCreator_;
    7.     levelCreator temp;
    8.     Vector3 playerPos;
    9.     int[,] mapArray = new int[13,17];
    10.     bool inhibitPlayerInput;
    11.     void Start () {
    12.         DOTween.Init(true, true, LogBehaviour.Verbose).SetCapacity(6000, 6000);
    13.         levelCreator_ = GameObject.Find ("LevelCreatorGameObject");
    14.         temp = levelCreator_.gameObject.GetComponent<levelCreator>();
    15.         mapArray = temp.mapArray;
    16.         for(int i = 1; i<12; i++)
    17.         {
    18.             for(int ii = 1; ii < 16; ii++)
    19.             {
    20.                 if( mapArray[i,ii] == 2)
    21.                 {
    22.                     playerPos = new Vector3(i,ii,0);
    23.                 }
    24.             }
    25.         }
    26.         transform.position = new Vector3(playerPos.x,playerPos.y,0);
    27.     }
    28.     void Update () {
    29.         if (inhibitPlayerInput || rewinding) return;
    30.         getInput();
    31.     }
    32.     void getInput()
    33.     {
    34.         bool inputPressed = false;
    35.         Vector3 newPlayerPos = playerPos;
    36.         if(Input.GetKey(KeyCode.W))
    37.         {
    38.             inputPressed = true;
    39.             newPlayerPos += new Vector3(-1,0,0);
    40.         }
    41.         else if (Input.GetKey(KeyCode.S))
    42.         {
    43.             inputPressed = true;
    44.             newPlayerPos += new Vector3(1,0,0);
    45.         }
    46.         else if(Input.GetKey(KeyCode.A))
    47.         {
    48.             inputPressed = true;
    49.             newPlayerPos += new Vector3(0,-1,0);
    50.         }
    51.         else if(Input.GetKey(KeyCode.D))
    52.         {
    53.             inputPressed = true;
    54.             newPlayerPos += new Vector3(0,1,0);
    55.         }
    56.         if (!inputPressed) return;
    57.         if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
    58.         {
    59.             // Check for unwalkable tile
    60.             return;
    61.         }
    62.         playerPos = newPlayerPos;
    63.         if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0)
    64.         {
    65.             transform.DOMove(playerPos, 0.15f).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
    66.             return;
    67.         }
    68.     }
    69. }
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Make a direction property that keeps track of which way the player is going. You could use an enum, or an integer (and just define 0=north, 1=east, etc.), or you could keep dx and dy separately; do whatever makes most sense to you.

    Then, keep track of how long it's been since you last moved. In the Update method, if it's been more than however long you want (say 0.5f to move every half second), look at your direction property and move accordingly. Use a switch, or just a series of if statements.

    Finally, you already know how to tell when a key is pressed. Simply add a direction check to the key check. In other words, instead of just saying "if (Input.GetKey(KeyCode.D)", you'll say "if (Input.GetKey(KeyCode.D) && direction != 0)". That will ignore inputs that would cause the player to reverse direction.

    More if statements! You've already laid out the code in pretty clear English; just write that same logic as code, and Bob's your uncle.
     
  3. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    Thanks for the reply. I'll look into this.
     
  4. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    Hi @JoeStrout ,
    It took me quite a while to get this working however it is still not at all optimal. I'd need more help !
    The straight direction that it moves in, happens very fast. The object stops when it comes against an unwalkable grid instead of turning. Only when the input key is pressed then, that it detects the unwalkable grid and takes a turn. If possible, it would be great if you could take a look at my project here - https://dl.dropboxusercontent.com/u/3010784/GridMovement.zip

    or if you wish to check my code, here it is
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using DG.Tweening;
    4. using System.Collections.Generic;
    5. public class Player : MonoBehaviour {
    6.     const float TweenWpDuration = 0.2f;
    7.     public GameObject levelCreator_;
    8.     levelCreator temp;
    9.     Vector3 playerPos;
    10.     int[,] mapArray = new int[13,17];
    11.     public bool inhibitPlayerInput;
    12.     void Start () {
    13.         DOTween.Init(true, true, LogBehaviour.Verbose).SetCapacity(6000, 6000);
    14.         levelCreator_ = GameObject.Find ("LevelCreatorGameObject");
    15.         temp = levelCreator_.gameObject.GetComponent<levelCreator>();
    16.         mapArray = temp.mapArray;
    17.         for(int i = 1; i<12; i++)
    18.         {
    19.             for(int ii = 1; ii < 16; ii++)
    20.             {
    21.                 if( mapArray[i,ii] == 2)
    22.                 {
    23.                     playerPos = new Vector3(i,ii,0);
    24.                 }
    25.             }
    26.         }
    27.         Debug.Log (GhostManager.ghostTweens.Count);
    28.         transform.position = new Vector3(playerPos.x,playerPos.y,0);
    29.     }
    30.     void Update () {
    31.         if (inhibitPlayerInput) return;
    32.         getInput();
    33.     }
    34.     void getInput()
    35.     {
    36.         bool inputPressed = false;
    37.         if(Input.GetKey(KeyCode.W) && inhibitPlayerInput == false)
    38.         {
    39.             inputPressed = true;
    40.             walkup();
    41.         }
    42.         else if (Input.GetKey(KeyCode.S))
    43.         {
    44.             inputPressed = true;
    45.             walkdown();
    46.         }
    47.         else if(Input.GetKey(KeyCode.A))
    48.         {
    49.             inputPressed = true;
    50.             walkleft();
    51.         }
    52.         else if(Input.GetKey(KeyCode.D))
    53.         {
    54.             inputPressed = true;
    55.             walkright();
    56.         }
    57.     }
    58.     void walkup ()
    59.     {
    60.         Vector3 newPlayerPos = playerPos;
    61.         newPlayerPos += new Vector3(-1,0,0);
    62.         if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
    63.         {
    64.             if (mapArray[(int)playerPos.x,(int)playerPos.y + 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y + 1] >= 2)
    65.                 walkright();
    66.             else
    67.                 walkleft();
    68.             return;
    69.         }
    70.         playerPos = newPlayerPos;
    71.         if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
    72.         {
    73.             inhibitPlayerInput = true;
    74.             transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
    75.         }
    76.         if (mapArray[(int)playerPos.x - 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x - 1,(int)playerPos.y] >= 2)
    77.         {
    78.             playerPos = newPlayerPos;
    79.             walkup();
    80.         }
    81.         return;
    82.     }
    83.     void walkright ()
    84.     {
    85.         Vector3 newPlayerPos = playerPos;
    86.         newPlayerPos += new Vector3(0,1,0);
    87.         if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
    88.         {
    89.             if (mapArray[(int)playerPos.x + 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x + 1,(int)playerPos.y] >= 2)
    90.                 walkdown();  
    91.             else
    92.                 walkup();
    93.             return;
    94.         }
    95.         playerPos = newPlayerPos;
    96.         if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
    97.         {
    98.             inhibitPlayerInput = true;
    99.             transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
    100.         }
    101.         if (mapArray[(int)playerPos.x,(int)playerPos.y + 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y + 1] >= 2)
    102.         {
    103.             playerPos = newPlayerPos;
    104.             walkright();
    105.         }
    106.         return;
    107.     }
    108.     void walkleft ()
    109.     {
    110.         Vector3 newPlayerPos = playerPos;
    111.         newPlayerPos += new Vector3(0,-1,0);
    112.         if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
    113.         {
    114.             if (mapArray[(int)playerPos.x + 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x + 1,(int)playerPos.y] >= 2)
    115.                 walkdown();
    116.             else
    117.                 walkup();
    118.             return;
    119.         }
    120.         playerPos = newPlayerPos;
    121.         if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
    122.         {
    123.             inhibitPlayerInput = true;
    124.             transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
    125.         }
    126.         if (mapArray[(int)playerPos.x,(int)playerPos.y - 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y - 1] >= 2)
    127.         {
    128.             playerPos = newPlayerPos;
    129.             walkleft();
    130.         }
    131.         return;
    132.     }
    133.     void walkdown ()
    134.     {
    135.         Vector3 newPlayerPos = playerPos;
    136.         newPlayerPos += new Vector3(1,0,0);
    137.         if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
    138.         {
    139.             if (mapArray[(int)playerPos.x,(int)playerPos.y + 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y + 1] >= 2)
    140.                 walkright();
    141.             else
    142.                 walkleft();
    143.             return;
    144.         }
    145.         playerPos = newPlayerPos;
    146.         if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
    147.         {
    148.             inhibitPlayerInput = true;
    149.             transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
    150.         }
    151.         if (mapArray[(int)playerPos.x + 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x + 1,(int)playerPos.y] >= 2)
    152.         {
    153.             playerPos = newPlayerPos;
    154.             walkdown();
    155.         }
    156.         return;
    157.     }
    158. }
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    You haven't followed my suggestions at all. Where's your "direction" variable that keeps track of which way you're going? You've got your Update method doing nothing much, and your key-response methods actually moving the player. Instead, when you detect a key, you should just set the direction; and then the Update method should be actually moving the player.

    I'll take a look at your project and try to be more helpful this weekend if I get the chance. But in the meantime, please try again... it should be a simple matter of "if W is pressed set direction to Up; if A is pressed, set it to Left; etc." and then in Update, "when direction is Up and the square above is clear, move up; when direction is Left and the square to the left is clear, move left; etc." (You don't have to move an entire square at once, of course; control your speed by how much you move each frame.)
     
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hey, I just downloaded your project... but it looks to me like you got it, right? The code is quite different from the above, and seems to do what you wanted (unless I've misunderstood that).

    Well done!
     
  7. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    Hi @JoeStrout , the code that you see in the project was not written by me. A user on the Unity Answers offered to help, though it doesn't work the way I want it to. In that code, after every move to a new tile, the object pauses due to the yield WaitForSeconds code, and then moves again and pauses. And if I reduce the time for the wait to make a smoother move possible, all the inputs get ignored due to a tinier time window for the same. Also, there is some glitch that makes the object go a tile in reverse, and sometimes, the object goes into a corner and keeps moving to 2 adjacent tiles in a forever loop. I'll admit that I couldn't understand what the code was either.

    I followed through your instructions and I got somewhere and got stuck. The object moves really fast, until it reaches a tile it cannot pass, and waits for an input. I changed the code and updated it in a different project that you can find here -- https://dl.dropboxusercontent.com/u/3010784/GridMovement02.zip
    The tweening statement that makes the movement possible, has a tween duration parameter for each move to each tile, which seems to being called each frame, hence speeding up the movement. Also, I haven't gotten the auto-turn to work. When I added the code to change the direction to the right of current direction (right turn rule), when bumping into an unwalkable tile, the object goes into a very fast moving oval loop that never stops. I believe it has something to do with the tweening statement being called each frame, though I may be wrong.

    Thanks a lot for taking the time to check the project out. I've been stuck at this very issue for three weeks now with no end in sight :(
     
  8. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    OK, I'll have a look at this today. I'm probably not going to use the tweening code — I think it just makes the code more complex in this case. Instead I think of it as "just keep moving until you hit something or get an input, and then turn."

    I'm still a little fuzzy on how you want the turning to work, though. I can picture something like Pac-Man, where if you push "up" when there's currently no way to go up, then that just gets stored until the next opportunity to go up, and then it turns up. But it sounds like you also want the agent to turn automatically when it hits a wall, instead of stopping like Pac-Man. Is that right?

    And if so, which way should it automatically turn? Does this depend on which way the player last turned?
     
  9. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    You're pretty much bang on target ! If the object is moving right, and I pressed UP, but there was a wall above the object at that time, it will continue to move onto the next tile on the right, and then turn upwards and continue moving upwards, like Pac-Man). That is the first requirement, and the other requirement is that, suppose that object continues to move upwards by itself, and no input was pressed, then instead of stopping at the horizontal border at the very top, it will take a turn to a side automatically, and continue moving in that direction, until either an input is pressed, or it collides against the vertical border and turns again.
    Every time such a scenario happens, the object should turn to its' right.
    So for brevity purposes:
    If colliding with a wall, while travelling upwards should make it turn screen right;
    If colliding with a wall, while travelling screen right should make it turn downwards.
    If colliding with a wall, while travelling downwards should make it turn screen left.
    If colliding with a wall, while travelling screen left should make it turn upwards.
    Thanks a bunch for taking the time to do this, Joe. I really appreciate it ! :)
     
  10. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Got it. I'll get back to you shortly.
     
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    OK, please try this. I tried to comment everything clearly, but let me know if there's something you don't understand.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. public class JSPlayer : MonoBehaviour {
    5.     // Public properties you can tweak to change the behavior:
    6.     public float speed = 3;                // movement speed, in map cells per second
    7.  
    8.     // These properties are public just so you can watch them in the inspector
    9.     // to better understand what's going on.  Otherwise, they could be private.
    10.     public Vector2 position;            // current position on the map
    11.     public Vector2 currentDirection;    // which way we're currently moving
    12.     public Vector2 desiredDirection;    // which way the player wants to move
    13.  
    14.     // Our camera is twisted sideways, so we can't use the standard
    15.     // Vector2.up, Vector2.right, etc.  So we'll instead use these properties,
    16.     // which represent directions as the user sees them instead of how they're
    17.     // normally defined.
    18.     public Vector2 right = new Vector2(0,1);
    19.     public Vector2 left = new Vector2(0,-1);
    20.     public Vector2 up = new Vector2(-1,0);
    21.     public Vector2 down = new Vector2(1,0);
    22.  
    23.     int[,] mapArray;
    24.  
    25.     void Start() {
    26.         // Initialize our map from the level creator.
    27.         GameObject levelCreatorObj = GameObject.Find("LevelCreatorGameObject");
    28.         if (levelCreatorObj == null) Debug.LogError("Couldn't find LevelCreatorGameObject");
    29.         levelCreator creator = levelCreatorObj.GetComponent<levelCreator>();
    30.         if (creator == null) Debug.LogError("Couldn't find levelCreator component");
    31.         mapArray = creator.mapArray;
    32.  
    33.         // Set our initial position from the map (identified by value 2).
    34.         position = PositionInMapOf(2);
    35.  
    36.         // And set an initial direction... we'll just arbitrarily pick:
    37.         desiredDirection = right;
    38.         currentDirection = desiredDirection;
    39.     }
    40.    
    41.     void Update() {
    42.         // Check keyboard (or gamepad!) input.
    43.         HandleInput();
    44.  
    45.         if (desiredDirection != currentDirection) {
    46.             // Turn to the desired direction if possible.
    47.             if (!Blocked(position + desiredDirection)) {
    48.                 currentDirection = desiredDirection;
    49.             }
    50.         }
    51.  
    52.         // Move forward if we can, turning if needed.
    53.         // (The 0.1f here is how far ahead we look before turning.)
    54.         if (Blocked(position + currentDirection * 0.1f)) {
    55.             Autoturn();
    56.         }
    57.         position += currentDirection * speed * Time.deltaTime;
    58.  
    59.         // Lock ourselves onto the rails of the map by rounding whichever
    60.         // dimension we are NOT moving in.
    61.         position = RoundToPath(position, currentDirection);
    62.  
    63.         // Update our transform position (a Vector3) from our map position (a Vector2).
    64.         // Since our world is set up with Z always = 0, we can just:
    65.         transform.position = position;
    66.     }
    67.  
    68.     void OnGUI() {
    69.         // This is just some debugging code I used to check the Blocked method.
    70.         // You can delete this whole OnGUI function.
    71.         Rect r = new Rect(10, 10, 100, 20);
    72.         GUI.Label(r, Blocked(position) ? "BLOCKED" : "OK");
    73.     }
    74.  
    75.     /// <summary>
    76.     /// Find the first position of the given value in our map.  If
    77.     /// the value isn't found, then return Vector2.zero.
    78.     /// </summary>
    79.     /// <returns>Position of the given value in our map, as a Vector2.</returns>
    80.     /// <param name="value">Value to find.</param>
    81.     Vector2 PositionInMapOf(int value) {
    82.         int width  = mapArray.GetLength(0);
    83.         int height = mapArray.GetLength(1);
    84.         for (int i=0; i<width; i++) {
    85.             for (int j=0; j<height; j++) {
    86.                 if (mapArray[i,j] == value) return new Vector2(i,j);
    87.             }
    88.         }
    89.         return Vector2.zero;
    90.     }
    91.  
    92.     /// <summary>
    93.     /// Round the given position to a nice even path on the map with
    94.     /// respect to what way it's moving.  We do this by rounding whichever
    95.     /// dimension we are NOT moving in.
    96.     /// </summary>
    97.     /// <returns>The rounded position.</returns>
    98.     /// <param name="pos">Position of interest.</param>
    99.     /// <param name="movementDir">Movement direction.</param>
    100.     Vector2 RoundToPath(Vector2 pos, Vector2 movementDir) {
    101.         if (movementDir.x == 0) pos.x = Mathf.Round(pos.x);
    102.         if (movementDir.y == 0) pos.y = Mathf.Round(pos.y);
    103.         return pos;
    104.     }
    105.  
    106.     /// <summary>
    107.     /// Handles player input.  Note that by using Input.GetAxisRaw here,
    108.     /// instead of GetKey, we not only support folks (like JS) who use
    109.     /// Dvorak instead of QWERTY, but even people who have gamepads or
    110.     /// joysticks!
    111.     /// </summary>
    112.     void HandleInput() {
    113.         if (Input.GetAxisRaw("Horizontal") < 0) {
    114.             desiredDirection = left;
    115.         } else if (Input.GetAxisRaw("Horizontal") > 0) {
    116.             desiredDirection = right;
    117.         } else if (Input.GetAxisRaw("Vertical") > 0) {
    118.             desiredDirection = up;
    119.         } else if (Input.GetAxisRaw("Vertical") < 0) {
    120.             desiredDirection = down;
    121.         }
    122.     }
    123.  
    124.     /// <summary>
    125.     /// Return whether the player would be blocked (hitting a wall)
    126.     /// at the given position.
    127.     /// </summary>
    128.     /// <returns>True if hitting a wall; false if clear.</returns>
    129.     /// <param name="pos">Map position of interest.</param>
    130.     bool Blocked(Vector2 pos) {
    131.         // The player is nearly 1 unit wide.  So, we need to check
    132.         // a range of almost 1 whole square.  It suffices to check
    133.         // the corners.
    134.         int left = Mathf.FloorToInt(pos.x+0.05f);
    135.         int right = Mathf.FloorToInt(pos.x+0.95f);
    136.         int top = Mathf.FloorToInt(pos.y+0.05f);
    137.         int bottom = Mathf.FloorToInt(pos.y+0.95f);
    138.         return mapArray[left,top] == 1
    139.             || mapArray[left,bottom] == 1
    140.             || mapArray[right,top] == 1
    141.             || mapArray[right,bottom] == 1;
    142.     }
    143.  
    144.     /// <summary>
    145.     /// Automatically turn the player to the right as far as necessary
    146.     /// to avoid moving into a wall.  If we can't find any direction
    147.     /// that's clear, then just stop moving.
    148.     /// </summary>
    149.     void Autoturn() {
    150.         // try up to four times...
    151.         Vector2 d = currentDirection;
    152.         for (int i=0; i<4; i++) {
    153.             if (d == up) d = right;
    154.             else if (d == right) d = down;
    155.             else if (d == down) d = left;
    156.             else if (d == left) d = up;
    157.             if (!Blocked(RoundToPath(position + d, d))) {
    158.                 // Found a clear direction, let's go that way!
    159.                 currentDirection = desiredDirection = d;
    160.                 return;
    161.             }
    162.         }
    163.         // give up
    164.         currentDirection = Vector2.zero;
    165.     }
    166. }
    167.  
     
  12. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    This is extremely close to what I was looking for ! I'm currently going through the code to understand your process here. There is just one thing that might need some looking into. According to the code, everything is working the way it is supposed to, logically.
    When the object is travelling along the outside borders in the anti-clockwise direction, the object is doing a rebound instead of turning. For example, when Play is clicked upon, the object starts moving in the lower most lane horizontally. When it reaches the end of the lane, it gets blocked and tries to turn to its' right, which is downwards, which again detects as blocked, and again turns to its' right, which is screen left. In such a situation, how is it possible to override the turn right rule, and make the object turn left instead ? Kindly check the video here to see it in action (62kb) - https://dl.dropboxusercontent.com/u/3010784/GridMove02.mp4
    Something I forgot to mention in the last post was blocking the reverse movement. So that if the object is moving right, it can't move left, or if going upwards, going down is locked.
    Apologies for not being clear.
     
  13. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Yeah, I wondered if you really meant it when you said to always turn right. :) What you want to do is modify the Autoturn() method. If it's going up, for example, then it should first try right, then try left, and finally try turning around. The first one of those that is not blocked, is the direction it should return. (And then similar for the other three starting directions, of course.)

    You'll no longer need the for loop, but you will need a bit more code to handle these various cases.

    Ah. I would address that in HandleInput(). So the first case, instead of just saying if (Input.GetAxisRaw("Horizontal") < 0), you would say if (Input.GetAxisRaw("Horizontal") < 0 && currentDirection != right). So you only go left if the horizontal input is to the left, AND you're not currently going right.

    But I guess another place you could accomplish it would be in Update(), where we decide whether to turn... you could change that if condition to if (desiredDirection != currentDirection && desiredDirection != -currentDirection). (I.e., we turn only if the desired direction isn't the way we're already going, or the exact opposite.) That would do it too.

    Cheers,
    - Joe
     
  14. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    That's cool ! I'll try to implement it tomorrow, as it's late at night here.
    Thanks a lot for all your help here, Joe !
     
  15. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    Edited the Autorun method and almost got all of it to work. There is two cases that fail to work the way it is supposed to. When moving left, towards the top left corner, the object is supposed to turn to its' left, and start moving screen down, but somehow it sets the currentDirection vector to 0. The same happens to the object when moving towards the bottom left corner, the object stops instead of turning screen right. Kindly check if there is something wrong with the code.

    Code (CSharp):
    1. void Autoturn() {
    2.         Vector2 d = currentDirection;
    3.  
    4.         if (Mathf.Round(position.x) == 1 && currentDirection == up)
    5.             d = right;
    6.         if (Mathf.Round(position.x) == 1 && Mathf.Round(position.y) == 1 && currentDirection == left)
    7.             d = down;
    8.         else if (Mathf.Round(position.x) == 1 && Mathf.Round(position.y) == 1 && currentDirection == up)
    9.             d = right;
    10.  
    11.         if (Mathf.Round(position.y) == 15 && currentDirection == right)
    12.             d = down;
    13.         if (Mathf.Round(position.x) == 1 && Mathf.Round(position.y) == 15 && currentDirection == up)
    14.             d = left;
    15.         else if (Mathf.Round(position.x) == 1 && Mathf.Round(position.y) == 15 && currentDirection == right)
    16.             d = down;
    17.  
    18.         if (Mathf.Round(position.x) == 11 && currentDirection == down)
    19.             d = left;
    20.         if (Mathf.Round(position.x) == 11 && Mathf.Round(position.y) == 1 && currentDirection == down)
    21.             d = right;
    22.         else if (Mathf.Round(position.x) == 11 && Mathf.Round(position.y) == 1 && currentDirection == left)
    23.             d = up;
    24.  
    25.         if (Mathf.Round(position.y) == 1 && currentDirection == left)
    26.             d = up;
    27.         if (Mathf.Round(position.x) == 11 && Mathf.Round(position.y) == 15 && currentDirection == right)
    28.             d = up;
    29.         else if (Mathf.Round(position.x) == 11 && Mathf.Round(position.y) == 15 && currentDirection == down)
    30.             d = left;
    31.          
    32.         if (!Blocked(RoundToPath(position + d, d))) {
    33.             // Found a clear direction, let's go that way!
    34.             currentDirection = desiredDirection = d;
    35.             return;
    36.         }
    37.         currentDirection = Vector2.zero;         // give up
    38.     }
     
    Last edited: Oct 5, 2014
  16. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I don't quite understand why you're checking for specific positions here. I was expecting something like: return the direction to the left if that's not blocked; else return the direction to the right if that's not blocked; else reverse. (Probably repeated four times, once for each direction.) Wouldn't that be better than checking specific locations? What if your level has a corner somewhere else?
     
  17. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    Ah, that makes quite sense too. I tweaked the method but it gives an Array index is out of range error.
    Code (CSharp):
    1. void Autoturn() {
    2.         Vector2 d = currentDirection;
    3.  
    4.         if (!Blocked(left))
    5.             d = left;
    6.         else d = right;
    7.  
    8.         if (!Blocked(right))
    9.             d = right;
    10.         else d = left;
    11.  
    12.         if (!Blocked(down))
    13.             d = down;
    14.         else d = up;
    15.  
    16.         if (!Blocked(up))
    17.             d = up;
    18.         else d = down;
    19.  
    20.         if (!Blocked(RoundToPath(position + d, d))) {
    21.             // Found a clear direction, let's go that way!
    22.             currentDirection = desiredDirection = d;
    23.             return;
    24.         }
    25.         // give up
    26.         currentDirection = Vector2.zero;
    27.     }
     
  18. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Right, remember, the argument to Blocked is a map position, not a direction. So it should be something like

    Code (CSharp):
    1.  
    2.    Vector2 d;
    3.    // if going up, try going left or right
    4.    if (currentDirection == up) {
    5.       if (!Blocked(RoundToPath(position + left, left))) d = left;
    6.       else if (!Blocked(RoundToPath(position + right, right))) d = right;
    7.    }
    8.    // if going to the right, try going up or down
    9.    if (currentDirection == right) {
    10.       if (!Blocked(RoundToPath(position + up, up))) d = up;
    11.       else if (!Blocked(RoundToPath(position + down, down))) d = down;
    12.    }
    13.    // ...etc...
    And of course don't forget to set currentDirection = d at the end (no need to check Blocked again, since we already checked that in each case above).
     
  19. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    Since the first preference is given to the right turn, I had to interchange the order of the if and else if statements. I wrote the code for the other two directions but something still is going a bit wonky. For some reason it starts going in reverse after starting to continue in a new lane. Here is a video grab of the same - https://dl.dropboxusercontent.com/u/3010784/GridMove03.mp4

    Code (CSharp):
    1. void Autoturn() {
    2.         Vector2 d = currentDirection;
    3.  
    4.         // if going up, try going left or right
    5.         if (currentDirection == up) {
    6.             if (!Blocked(RoundToPath(position + right, right))) d = right;
    7.             else if (!Blocked(RoundToPath(position + left, left))) d = left;
    8.         }
    9.         // if going to the right, try going up or down
    10.         if (currentDirection == right) {
    11.             if (!Blocked(RoundToPath(position + down, down))) d = down;
    12.             else if (!Blocked(RoundToPath(position + up, up))) d = up;
    13.         }
    14.         // if going down, try going left or right
    15.         if (currentDirection == down) {
    16.             if (!Blocked(RoundToPath(position + left, left))) d = left;
    17.             else if (!Blocked(RoundToPath(position + right, right))) d = right;
    18.         }
    19.         // if going to the left, try going up or down
    20.         if (currentDirection == left) {
    21.             if (!Blocked(RoundToPath(position + up, up))) d = up;
    22.             else if (!Blocked(RoundToPath(position + down, down))) d = down;
    23.         }
    24.         // give up
    25.         currentDirection = d;
    26.     }
     
  20. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hmm... the only thing that's jumping out at me is, you shouldn't initialize d to currentDirection. Doing so would allow the agent to continue moving forward (right through the wall) if it's at a dead end. Instead you should leave it initialized to zero, which would make it stop in this case; or initialize it to -currentDirection, which would make it reverse course.

    I wonder if the reversal is somehow caused by the desiredDirection still being set. Could you try adding "desiredDirection=d" at the bottom of the Autoturn method too?
     
  21. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    That gives an error in the console.
    Code (CSharp):
    1. Assets/Scripts/JSPlayer.cs(133,36): error CS0165: Use of unassigned local variable `d'
    The code as it stands currently.
    Code (CSharp):
    1. void Autoturn() {
    2.         Vector2 d;
    3.  
    4.         // if going up, try going left or right
    5.         if (currentDirection == up) {
    6.             if (!Blocked(RoundToPath(position + right, right))) d = right;
    7.             else if (!Blocked(RoundToPath(position + left, left))) d = left;
    8.         }
    9.         // if going to the right, try going up or down
    10.         if (currentDirection == right) {
    11.             if (!Blocked(RoundToPath(position + down, down))) d = down;
    12.             else if (!Blocked(RoundToPath(position + up, up))) d = up;
    13.         }
    14.         // if going down, try going left or right
    15.         if (currentDirection == down) {
    16.             if (!Blocked(RoundToPath(position + left, left))) d = left;
    17.             else if (!Blocked(RoundToPath(position + right, right))) d = right;
    18.         }
    19.         // if going to the left, try going up or down
    20.         if (currentDirection == left) {
    21.             if (!Blocked(RoundToPath(position + up, up))) d = up;
    22.             else if (!Blocked(RoundToPath(position + down, down))) d = down;
    23.         }
    24.         // give up
    25.         desiredDirection = d;
    26.         currentDirection = d;
    27.     }
     
  22. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Sorry... change line 2 to "Vector2 d = Vector2.zero;".
     
  23. coolbird22

    coolbird22

    Joined:
    Mar 20, 2014
    Posts:
    38
    That worked like a charm ! Thanks a lot for all the assistance and major help. That was very informative and will aid me a lot in the future.
    Thanks yet again, Joe !
     
  24. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Great, I'm glad I was able to help. Programming is an incredibly fun and rewarding activity, and I can that you're getting it. Good luck with your game!