3
\$\begingroup\$

I don't want to find the shortest way and not moving in diagonals. The start position and end position are both on the edges walls so in the start i can't move back and in the end i can't move forward (depending on what direction the player is facing when getting to the end position ).

I want to move only left,right,forward but randomly. Randomly i mean that in the start i have only 3 directions possible moves: Forward,Left,Right so i want to store the 3 directions and then to pick one random direction for example Left to rotate the player facing to the left and then moving to the waypoint on the left the nearest waypoint on the left.

Now if he moved left now he have two directions to move right or keep forward and again to pick random direction from this two.

I want that the player will never go back to the last waypoint he visited.

So in any case the player will have 2 or 3 directions to move and each time to pick random direction to move to.

Grid

The red is the start position the blue is the end position. So i want to create a random path from the red to the blue.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.IO;

public class PathFinder : MonoBehaviour
{
    private Transform start;
    private Transform end;

    public void FindPath()
    {
        GenerateStartEnd();
        GenerateRandomPath();
    }

    private void GenerateRandomPath()
    {
        GridGenerator gridgenerator = GetComponent<GridGenerator>();

        List<float> distances = new List<float>();
        List<float> rotations = new List<float>();

        for (int i = 0; i < gridgenerator.allBlocks.Length; i++)
        {
            var distance = Vector3.Distance(start.localPosition, gridgenerator.allBlocks[i].transform.localPosition);
            if (distance == 1.5f)
            {
                var angle = Vector3.Angle(start.localPosition, gridgenerator.allBlocks[i].transform.localPosition);
                rotations.Add(angle);
            }
        }

        distances.Sort();
    }

    void SmoothLook(Vector3 newDirection)
    {
        start.rotation = Quaternion.Lerp(start.rotation, Quaternion.LookRotation(newDirection), Time.deltaTime);
    }

    private List<Vector3> GenerateStartEnd()
    {
        GameObject walls = GameObject.Find("Walls");
        List<Transform> wallsParents = new List<Transform>();
        List<Vector3> startEndPos = new List<Vector3>();

        foreach (Transform child in walls.transform)
        {
            wallsParents.Add(child);
        }

        for (int i = 0; i < 2; i++)
        {
            wallsParents.Remove(wallsParents[Random.Range(0, wallsParents.Count)]);
        }

        var childsWall0 = wallsParents[0].GetComponentsInChildren<Transform>().ToList();
        var childsWall1 = wallsParents[1].GetComponentsInChildren<Transform>().ToList();
        childsWall0.RemoveAt(0);
        childsWall1.RemoveAt(0);

        start = childsWall0[Random.Range(0, childsWall0.Count)];
        end = childsWall1[Random.Range(0, childsWall1.Count)];
        startEndPos.Add(start.position);
        startEndPos.Add(end.position);

        start.GetComponent<Renderer>().material.color = Color.red;
        end.GetComponent<Renderer>().material.color = Color.blue;

        return startEndPos;
    }
}

In GenerateStartEnd i pick two random walls. Then i'm getting the children of each wall the blocks(cubes). I remove the parents. Then i pick random block from each wall childs. I set the start and end.

Then in GenerateRandomPath this is where i'm trying to generate the random path between start and end. This is the whole part of the random path:

private void GenerateRandomPath()
    {
        GridGenerator gridgenerator = GetComponent<GridGenerator>();

        List<float> distances = new List<float>();
        List<float> rotations = new List<float>();

        for (int i = 0; i < gridgenerator.allBlocks.Length; i++)
        {
            var distance = Vector3.Distance(start.localPosition, gridgenerator.allBlocks[i].transform.localPosition);
            if (distance == 1.5f)
            {
                var angle = Vector3.Angle(start.localPosition, gridgenerator.allBlocks[i].transform.localPosition);
                rotations.Add(angle);
            }
        }

        distances.Sort();
    }

    void SmoothLook(Vector3 newDirection)
    {
        start.rotation = Quaternion.Lerp(start.rotation, Quaternion.LookRotation(newDirection), Time.deltaTime);
    }

The logic is simple find what directions you can move pick random direction move one waypoint in that direction don't stop continue to the next random waypoint. The next waypoint can be the nearest or even far away i don't mind but once he is reaching to the next waypoint find the directions pick random one and rotate and move to that direction to another rndom waypoint on this direction.

And son on until the player will get to the end. And i don't mind if the path will be long.

I'm just not sure how to translate it to the script.

allBlocks is GameObject[] array containing all the grid blocks(cubes).

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

If I understand correctly, you wish to implement random walk but prevent agent from visiting the same spot again. Agent also can't move backwards. If that's so, that's not possible. If agent can't visit same spot again, it is not guaranteed it can find the exit. At the end of answer I'll outline what you can do to improve that but first let's discuss random walk.

Since you are already using cubes, I assume these cubes also have colliders attached, right?

Each time you wish to decide on agent's next move, you can find available spots like this. I assume distance between cubes is 1.5.

// Array of forward, left and right directions
var availableDirections = new Vector3[]{Vector3.forward, Vector3.left, -Vector3.left}
// Map them to points around agent, 1.5 is distance between cubes
.Select(direction => transform.position + direction * 1.5f)
// Filter points that lie inside cubes, this assumes size of cube is 1
.Where(spot => Physics.OverlapBox(spot, Vector3.one * 0.5f).length > 0)
.ToArray();

Then just take random direction from all available directions.

var direction = availableDirections[Random.Range(0, availableDirections.Length-1)];

To check if you are currently at exit, you can use tags. First tag an exit in GenerateStartEnd method like this:

end.tag = "End";

and then when you move your agent, use OverlapBox to get all colliders at that point and check their tags.

if(Physics.OverlapBox(transform.position, Vector3.one * 0.5f)
  .FirstOrDefault(collider => collider.gameObject.tag == "End")
     != null)
    // We are at exit

To solve the problem of not getting to the exit because it is completely blocked with visited cubes, you have 2 options. You can either remove that rule or always choose exit if your agent is next to it. When you are selecting availableDirections, you also need to check if any of those colliders have tag == "End" and if it does have, you need to select that one instead of random.

\$\endgroup\$
10
  • \$\begingroup\$ Why when i assign a tag to end inside GenerateStartEnd i'm doing end.tag = "End"; and i have the End tag in the editor then it's adding more end positions in blue to the grid when i generate new grid. The startEndPos List size never change it's two but when i generate new grid it's coloring more cubes in blue. If i remove the line end.tag = "End"; then it's fine. What can be the problem that assigning end to tag make it color more cubes on the grid at blue ? Without this line it's generating new grid with new two startEndPos and only 2. Strange. \$\endgroup\$
    – Daniel Lip
    Commented Dec 2, 2017 at 5:45
  • \$\begingroup\$ Well, each time you run that method, you will change colours of 2 cubes, regardless of tag. Assigning tag to object doesn't affect anything so I'm pretty sure your problem is somewhere else. \$\endgroup\$
    – Dread Boy
    Commented Dec 2, 2017 at 5:57
  • \$\begingroup\$ Ok i found the problem and fixed. \$\endgroup\$
    – Daniel Lip
    Commented Dec 2, 2017 at 9:55
  • 1
    \$\begingroup\$ Direction the player is facing has nothing to do with direction he's going to walk. To rotate the player, you can do transform.forward = direction where direction is Vector3 pointing to next cube. \$\endgroup\$
    – Dread Boy
    Commented Dec 2, 2017 at 10:00
  • 1
    \$\begingroup\$ Comments are not for lengthy discussion, you should create new question. \$\endgroup\$
    – Dread Boy
    Commented Dec 3, 2017 at 5:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .