ArgGrr gonna try Devtember

This is a placeholder for my attempt at #devember2k18.

I, ArgGrr, will participate to the next Devember. My Devember will be to develop a game in Unity. I promise I will program for my Devember for at least an hour, every day of the next December. I will also write a daily public devlog and will make the produced code publicly available on the internet. No matter what, I will keep my promise. I expect someone to hold me to account.

I think I’ll give Unity another shot, and try and get over the hump to make a playable game. Don’t care about selling/releasing, this will just be an experiment.

It will be in C#, the language of Unity. I hav only used it playing with Unity, but pretty similar to VB.Net, which I have used a bunch.
Probably use Blender for any modelling I do. I have already plenty of tutorials on Udemy already, so really have every I need. The hardest part will be sticking to the routine of doing an hour a day. But somehow, If i can hold off sitting on the couch watching meme compilations, I will find the time.

So yeah probably a game. Some kind of 3D adventure game, with AI controlled NPCs walking around interacting. Already sounding better than Fallout76. I have a concept in mind for story etc, but I tend to think getting a game working is what I need to learn. If I was a professional game developer, I would already know how to make games.

Working Title: ArgGame

Things to learn:

  • Get comfortable working in unity, and make use of object orientated programming techniques to work a bit smarter.
  • Get some proper 3d rigged characters in game, not just squares.
  • Have said characters linked with some kind of AI, to loiter, interact with each other and the environment.
  • Had an idea of using a relational database to store information lerned by NPCs, and possible exchanged.
  • Make a nice environment.
  • Either develop my own tetures and models, or use stuff clearly in PD. No pinching assets. Haven’t decided on Asset Store, probably shy away from it.

I’ll drop more thoughts as I have them.

(Devember 2018)

8 Likes

Any further thoughts? :slight_smile:

Good luck argrr

Not yet, been pretty busy with work stuff! The main idea is to try and get the tech sorted out. Might be a bit dry.
Also not allowed to start yet!

@Goalkeeper Thanks!

1 Like

Day 1:

Well time to get started.
Got unity installed, linked up with visual studio.

Made a basic testing ground with a plane, some walls and a few random blocks.

Added in a basic red sphere player character, with movement and mouse looking script.

Added a generic GameObject called script_holder to hold scene scripts. These are ones going to be controlling general things about the scene, like spawning in random NPCs all over the testing ground.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class scenescripts : MonoBehaviour {

    public GameObject tmpobject;    //Set the object to spawn in properties
    public int NPC_Count;           //Set number of ojbets to spawn in properties
    public int x_range;             // Translation X range
    public int y_range;             // Translation Y range

    // Use this for initialization
    void Start () {

        GameObject tmp;

        for (int i = 0; i < NPC_Count; i++)
        {
            tmp = tmpobject;
            tmp.transform.Translate(new Vector3(Random.Range(-x_range,x_range), 5, Random.Range(-y_range,y_range)));
            Instantiate(tmp);
        }
    }

    // Update is called once per frame
    void Update () {
		// Code to move NPCs in here I guess
	}
}

Then this happens when you run it.

That’s all for today, already over an hour stuffing about.

6 Likes

You use a pre-made movement script or you wrote your own? Writing your own scripts can really help you get used to the basic unity variables and constants and everything else…
Also the pre-made scripts tend to be way overcomplicated.

1 Like

Ah you got me, i dragged in pre-made ones for movement and camera look.
Just wanted to get it running :slight_smile:
It would help me get used to the API again to make my own, so stay tuned for the thrilling next hour of development tomorrow!

1 Like

Day 2:

Hmm I managed to cobble together a basic movement script. Still have issues getting the jump to be something other than a high speed bounce, but that’s tomorrow me’s problem!

public class movepc : MonoBehaviour
{
    public float speed = 5.0f;      //horizontal movement speed, in M/S?
    public float jump_speed = 5.0f;
    public float grav = 9.8f;       // Gravity in M/S/S
    private float vspeed = 0.0f;    // Var to hold current vertical speed

    private CharacterController mc;

    // Use this for initialization
    void Start()
    {
        //This object seems to handle character movement, without relying on rigidbodies etc. Some new way.
        mc =  GetComponent<CharacterController>();
    }

    // Update is called once per frame
    void Update()
    {
 
        //Get movement based on input axis
        Vector3 move_vector = new Vector3(Input.GetAxis("Horizontal") * speed * Time.deltaTime , 
                                  0.0f, 
                                  Input.GetAxis("Vertical") * speed * Time.deltaTime );

        if (mc.isGrounded)
        {
            //On ground, can jump
            if (Input.GetAxis("Jump") > 0.0f) //TODO: Fix key binding to a proper control input
            {
                vspeed = jump_speed;
            } else
            {
                vspeed = 0.0f;
            }
        } else
        {
            //Add deceleration for gravity
            vspeed -= grav * Time.deltaTime;
        }

        move_vector.y = vspeed;

        //Move char
        mc.Move(move_vector);
    }
}

Now… back to the Devtember Killer… X4…

4 Likes

Still Day 2?:

So looked into a bit more how to utilise the AI API in Unity. There are things called NavMeshes, and NavAgents. A NavMesh is pretty much a pre-calculated map of the level that the NavAgents can follow.

The area in blue is the “baked”, or pre-calculated NavMesh. I made the capsule shapes the NavAgents, since they are the NPCs I guess? They are set to head to the PC location.


Here the capsules are swarming me, or attempting too. Still doing something wrong, they can glitch out a bit if I jump around and sit on top of things.

using UnityEngine;
using UnityEngine.AI;

public class NPCmove : MonoBehaviour {

    public Transform goal;          //Set in the editor to the PC sphere.
    private NavMeshAgent navagent;
    void Start()
    {
        //Get a pointer to the NavAgent associated with this game object.
        navagent = GetComponent<NavMeshAgent>();
        //Initially attempt to set the goal destination to the PC sphere.
        navagent.destination = goal.position;

    }

    //Late update, so this happens after PC movements etc.
	void LateUpdate () {

        if (!navagent.isOnNavMesh )
        {
            //Because of my poor spawning code, some capsules spawn off the map.
            navagent.isStopped = true;
            Destroy(this);
        }

        //If path becomes stale or invalid (PC Moves?), calculate a new one.
        if (navagent.isPathStale )
        {
            navagent.destination = goal.position;
        }

        if (navagent.pathStatus == NavMeshPathStatus.PathComplete  || navagent.pathStatus == NavMeshPathStatus.PathInvalid )
        {
            navagent.destination = goal.position;
        }
	}
}
5 Likes

If I wanted to be evil i would say “A*” algorithm, but thats heavy task … Built in functions are fine here :yum:
Anyways, good job :wink:

Maybe down the track, it depends how well pre-baked meshes work in dynamic areas. The docs say the agents will do their best. Their best wasn’t very good when I didn’t exclude the white squares…
Maybe I can do it again but not set everything as static…

1 Like

Day 3a:

Hmm didn’t get much done today, crazy day with work.
I made up a 3d model staircase mesh and dragged that into the scene, and added a mesh collider component so it appears as a physical object. Baked it, so the navmesh flowed up it, so to speak.

I need to spend a bit more time making an interesting environment, and have the AI traverse it. Probably something like a randomly generated maze, and see if I can get that to work. Maybe that for the next few days.

5 Likes

Day 3b:

Anyways, went back a bit to look at the NavMesh agents, obstacles and stuff like that to figure out what I have been doing wrong.

With dynamic objects, like the white boxes on the right, you need to add a component called Nav Mesh Obstacle. This helps the navigation stuff keep track of what to avoid, and how it does it. These boxes move back and forth across the field. The blue capsule seems to follow a standard path to the goal, but slows down and when it hits the box, trieds to go around.

If I set the dynamic boxes to tear a hole in the navmesh on the fly, the capsule seems to do a good job at taking the shortest path around them without ridding the edge in the direction it wants to go.

If I do use something like this for a randomly generated maze, the AI will probably just A* its way through the maze in one pass, might not be that interesting a test.

5 Likes

Day 4:

Getting back into some code toady, trying to make a function to generate a random maze. I had a look on wikipedia for an algorithm.
Not done yet, here is what I have so far:

struct wall
{
    //index reference to wall, which should be even (damn already confusing) numbers.
    int X;
    int Y;

    public wall(int x1, int y1)
    {
        X = x1;
        Y = y1;
    }
}

void genMaze()
{
    //Array of cells set to right size
    bool[,] cells = new bool[Maze_X, Maze_y];

    //
    List<wall> walls = new List<wall>();


    for (int i = 0; i < Maze_X; i++)
    {
        for (int j = 0; j < Maze_y; j++)
        {
            if ((i % 2 == 0) && (j % 2 == 0))
            {
                //Set walls present by default
                cells[i, j] = true; 
            } else
            {
                //oddindex, should be a cell.
                //Set cells as not visited by default
                cells[i, j] = false;
            }
        }
    }

    /*
    Modified Prim's algorithm from Wikipedia
    1. Start with a grid full of walls.
    2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
    3. While there are walls in the list:
        a. Pick a random wall from the list. If only one of the two cells that the wall divides is visited, then:
            i. Make the wall a passage and mark the unvisited cell as part of the maze.
            ii. Add the neighboring walls of the cell to the wall list.
        b. Remove the wall from the list.
    */

    int x, y;

    //2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
    // Need to pick even index inside the maze
    // TODO: Should probably just pick 1,1 or something known!
    x = Random.Range(1,(Maze_X-1)/2)*2;
    y = Random.Range(1,(Maze_y-1)/2)*2;

    cells[x, y] = true; //Mark visited

    walls.Add(new wall(x - 1, y));
    walls.Add(new wall(x, y - 1));
    walls.Add(new wall(x+1, y));
    walls.Add(new wall(x, y+1));


    // 3. While there are walls in the list:
    do
    {


    } while (walls.Count > 0);

}
5 Likes

Day 5:

More code. Redid the maze stuff, and finished off the algorithm. Haven’t tested it yet though.
I was trying to store the maze rooms and walls in a single 2D array of booleans. The walls would be even cells (starting at 0), and the rooms would be odd. A true for a wall indicates a wall (as opposed to a passage), and a true for a room indicates it has been visited in the building process.
However it was confusing, and hard to navigate.
I changed over to a 2D array of Room classes, and wall classes. A room contains pointers or references to the 4 walls around it. Each wall contains references to rooms either side of it. Both have properties indicating if they are passages or have been visited.
Much less confusing, for me anyway.

Why am I doing all this? I don’t know. Practice I guess? I want to dynamically build a maze, or a level of some kind and have the entities manage to find their way through it.

Finally, I am no Computer Scientist or Software Engineer, can you tell? :slight_smile:

public int Maze_X = 10;
public int Maze_y = 10;

class Room
{
    public Wall Up;
    public Wall Down;
    public Wall Left;
    public Wall Right;

    public bool isPartOfMaze;
    public bool isVisited;
}
class Wall
{
    public Room Room1;
    public Room Room2;

    public bool passage;

    public Wall(Room Room1,Room Room2 )
    {
        this.passage = false;
        this.Room1 = Room1;
        this.Room2 = Room2;
    }

}

void genMaze()
{
    //Array of cells set to right size
    Room[,] Rooms = new Room[Maze_X, Maze_y];

    List<Wall> Walls = new List<Wall>();
    int x, y;

    //Build grid of rooms
    for (y = 0; y < Maze_y; y++) 
    {
        for (x = 0; x < Maze_X; x++)
        {
            Room tmproom = new Room();

            tmproom.isPartOfMaze = false;
            tmproom.isVisited = false;

            Rooms[x, y] = tmproom;
        }
    }
    //Run back over array and create walls between all the rooms, putting in the references to rooms etc.
    for (y = 0; y < Maze_y; y++)
    {
        for (x = 0; x < Maze_X; x++)
        {
            if (x == 0) Rooms[x, y].Left = new Wall(Rooms[x, y], null);
            if (x == Maze_X - 1) Rooms[x, y].Right = new Wall(Rooms[x, y], null);
            Wall tmpwall = new Wall(Rooms[x, y], Rooms[x + 1, y]);
            Rooms[x, y].Right = tmpwall;
            Rooms[x + 1, y].Left = tmpwall;

            if (y == 0) Rooms[x, y].Up = new Wall(Rooms[x, y], null);
            if (y == Maze_X - 1) Rooms[x, y].Down = new Wall(Rooms[x, y], null);
            tmpwall = new Wall(Rooms[x, y], Rooms[x, y+1]);
            Rooms[x, y].Down  = tmpwall;
            Rooms[x , y+1].Up = tmpwall;

        }
    }

    /*
    Modified Prim's algorithm from Wikipedia
    1. Start with a grid full of walls.
    2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
    3. While there are walls in the list:
        a. Pick a random wall from the list. If only one of the two cells that the wall divides is visited, then:
            i. Make the wall a passage and mark the unvisited cell as part of the maze.
            ii. Add the neighboring walls of the cell to the wall list.
        b. Remove the wall from the list.
    */


    //2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
    // Need to pick even index inside the maze
    // TODO: Should probably just pick 1,1 or something known!
    x = Random.Range(1, (Maze_X - 1) / 2) * 2;
    y = Random.Range(1, (Maze_y - 1) / 2) * 2;

    Rooms[x, y].isPartOfMaze = true;
    Rooms[x, y].isVisited  = true;

    Walls.Add(Rooms[x, y].Up);
    Walls.Add(Rooms[x, y].Down );
    Walls.Add(Rooms[x, y].Left );
    Walls.Add(Rooms[x, y].Right );


    // 3. While there are walls in the list:
    do
    {
        int r = Random.Range(0, Walls.Count - 1);
        //^ = XOR
        if (Walls[r].Room1.isVisited ^ Walls[r].Room2.isVisited )
        {
            Walls[r].passage = true;
            if (!Walls[r].Room1.isVisited)
            {
                Walls[r].Room1.isVisited = true;
                if (!Walls.Contains(Walls[r].Room1.Up)) Walls.Add(Walls[r].Room1.Up);
                if (!Walls.Contains(Walls[r].Room1.Down)) Walls.Add(Walls[r].Room1.Down);
                if (!Walls.Contains(Walls[r].Room1.Left)) Walls.Add(Walls[r].Room1.Left);
                if (!Walls.Contains(Walls[r].Room1.Right)) Walls.Add(Walls[r].Room1.Right);
            }
            else
            {
                Walls[r].Room2.isVisited = true;
                if (!Walls.Contains(Walls[r].Room2.Up)) Walls.Add(Walls[r].Room2.Up);
                if (!Walls.Contains(Walls[r].Room2.Down)) Walls.Add(Walls[r].Room2.Down);
                if (!Walls.Contains(Walls[r].Room2.Left)) Walls.Add(Walls[r].Room2.Left);
                if (!Walls.Contains(Walls[r].Room2.Right)) Walls.Add(Walls[r].Room2.Right);
            }
            Walls.RemoveAt(r);
        }
        Walls[r].passage = true;

    } while (Walls.Count > 0);

}

Anyway for tomorrow: Link this code back to the game and have it generate a maze by instantiating a bunch of cubes, adding obstacle components and make the NPCs walk through it.

7 Likes

Day 6:

Got a map to generate. I notice the Ai won’t try and go through it if I make it too big.
As for the code I mostly had to just do a few tweaks to get it to run without errors, bounds checking stuff to handle the edge of the maze.


Do nothing!


Working! Overruns a bit on corners, can tune this though.

Bulk code, not tidied up:

public class scenescripts : MonoBehaviour {

    public NPCmove tmpobject;    //Set the object to spawn in properties
    public int NPC_Count;           //Set number of ojbets to spawn in properties
    public int x_range;             // Translation X range
    public int y_range;             // Translation Y range

    public static int Maze_X = 10;
    public static int Maze_y = 10;
    Room[,] Rooms = new Room[Maze_X, Maze_y];

    public float  map_scale = 2;

    public GameObject template;
    public GameObject goal;

    float counter;

    // Use this for initialization
    void Start() {

        //for (int i = 0; i < NPC_Count; i++)
        //{
        //    tmpobject.transform.Translate(new Vector3(Random.Range(-x_range, x_range), 0, Random.Range(-y_range, y_range)));
        //    Instantiate(tmpobject);
        //}

        genMaze();

    }

    // Update is called once per frame
    void Update() {

    }



    class Room
    {
        public Wall Up;
        public Wall Down;
        public Wall Left;
        public Wall Right;

        public bool isPartOfMaze;
        public bool isVisited;
    }
    class Wall
    {
        public Room Room1;
        public Room Room2;

        public bool passage;

        public Wall(Room Room1,Room Room2 )
        {
            this.passage = false;
            this.Room1 = Room1;
            this.Room2 = Room2;
        }

    }

    void genMaze()
    {
        //Array of cells set to right size


        List<Wall> Walls = new List<Wall>();
        int x, y;

        //Build grid of rooms
        for (y = 0; y < Maze_y; y++) 
        {
            for (x = 0; x < Maze_X; x++)
            {
                Room tmproom = new Room();

                tmproom.isPartOfMaze = false;
                tmproom.isVisited = false;

                Rooms[x, y] = tmproom;
            }
        }
        //Run back over array and create walls between all the rooms, putting in the references to rooms etc.
        for (y = 0; y < Maze_y-1; y++)
        {
            for (x = 0; x < Maze_X-1; x++)
            {
                if (x == 0) Rooms[x, y].Left = new Wall(Rooms[x, y], null);
                if (x == Maze_X - 1) Rooms[x, y].Right = new Wall(Rooms[x, y], null);
                Wall tmpwall = new Wall(Rooms[x, y], Rooms[x + 1, y]);
                Rooms[x, y].Right = tmpwall;
                Rooms[x + 1, y].Left = tmpwall;

                if (y == 0) Rooms[x, y].Up = new Wall(Rooms[x, y], null);
                if (y == Maze_X - 1) Rooms[x, y].Down = new Wall(Rooms[x, y], null);
                tmpwall = new Wall(Rooms[x, y], Rooms[x, y+1]);
                Rooms[x, y].Down  = tmpwall;
                Rooms[x , y+1].Up = tmpwall;

            }
        }

        /*
        Modified Prim's algorithm from Wikipedia
        1. Start with a grid full of walls.
        2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
        3. While there are walls in the list:
            a. Pick a random wall from the list. If only one of the two cells that the wall divides is visited, then:
                i. Make the wall a passage and mark the unvisited cell as part of the maze.
                ii. Add the neighboring walls of the cell to the wall list.
            b. Remove the wall from the list.
        */


        //2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
        // Need to pick even index inside the maze
        // TODO: Should probably just pick 1,1 or something known!
        x = Random.Range(1, (Maze_X - 1) / 2) * 2;
        y = Random.Range(1, (Maze_y - 1) / 2) * 2;

        Rooms[x, y].isPartOfMaze = true;
        Rooms[x, y].isVisited  = true;

        Walls.Add(Rooms[x, y].Up);
        Walls.Add(Rooms[x, y].Down );
        Walls.Add(Rooms[x, y].Left );
        Walls.Add(Rooms[x, y].Right );


        // 3. While there are walls in the list:
        do
        {
            int r = Random.Range(0, Walls.Count - 1);
            //^ = XOR

            if (Walls[r]==null)
            {

            }
            else if (Walls[r].Room1 == null || Walls[r].Room2 == null) 
            {

            }
            else if (Walls[r].Room1.isVisited ^ Walls[r].Room2.isVisited)
            {
                Walls[r].passage = true;
                if (!Walls[r].Room1.isVisited)
                {
                    Walls[r].Room1.isVisited = true;
                    if (!Walls.Contains(Walls[r].Room1.Up)) Walls.Add(Walls[r].Room1.Up);
                    if (!Walls.Contains(Walls[r].Room1.Down)) Walls.Add(Walls[r].Room1.Down);
                    if (!Walls.Contains(Walls[r].Room1.Left)) Walls.Add(Walls[r].Room1.Left);
                    if (!Walls.Contains(Walls[r].Room1.Right)) Walls.Add(Walls[r].Room1.Right);
                }
                else
                {
                    Walls[r].Room2.isVisited = true;
                    if (!Walls.Contains(Walls[r].Room2.Up)) Walls.Add(Walls[r].Room2.Up);
                    if (!Walls.Contains(Walls[r].Room2.Down)) Walls.Add(Walls[r].Room2.Down);
                    if (!Walls.Contains(Walls[r].Room2.Left)) Walls.Add(Walls[r].Room2.Left);
                    if (!Walls.Contains(Walls[r].Room2.Right)) Walls.Add(Walls[r].Room2.Right);
                }
                
            }

            Walls.Remove(Walls[r]);

            //Walls[r].passage = true;
            //Debug.Log("Walls: " + Walls.Count);

        } while (Walls.Count > 0);

        //Done forming maze
        GameObject tmpobject;

        for (y = 0; y < Maze_y; y++)
        {
            for (x = 0; x < Maze_X; x++)
            {
                if (Rooms[x,y]==null) continue;

                if (Rooms[x, y].Left  == null) continue;
                if (!Rooms[x, y].Left.passage)
                {
                    tmpobject = Instantiate(template);
                    tmpobject.transform.Translate(new Vector3((x * map_scale ) - map_scale / 2, 2, (y * map_scale)));
                }

                if (Rooms[x, y].Up == null) continue;
                if (!Rooms[x, y].Up.passage)
                {
                    tmpobject = Instantiate(template);
                    tmpobject.transform.Translate(new Vector3((x * map_scale), 2, (y * map_scale) - map_scale / 2));
                }

                tmpobject = Instantiate(template);
                tmpobject.transform.Translate(new Vector3((x * map_scale) - map_scale / 2, 2, (y * map_scale) - map_scale / 2));

                if (x == (Maze_X - 2))
                {
                    tmpobject = Instantiate(template);
                    tmpobject.transform.Translate(new Vector3((x * map_scale) + map_scale / 2, 2, (y * map_scale)));
                    tmpobject = Instantiate(template);
                    tmpobject.transform.Translate(new Vector3((x * map_scale) + map_scale / 2, 2, (y * map_scale) - map_scale / 2));
                }
                if (y == (Maze_y - 2))
                {
                    tmpobject = Instantiate(template);
                    tmpobject.transform.Translate(new Vector3((x * map_scale), 2, (y * map_scale) + map_scale / 2));
                    tmpobject = Instantiate(template);
                    tmpobject.transform.Translate(new Vector3((x * map_scale) - map_scale / 2, 2, (y * map_scale) + map_scale / 2));
                }

                if (x == (Maze_X - 2) && y == (Maze_y - 2))
                {
                    tmpobject = Instantiate(template);
                    tmpobject.transform.Translate(new Vector3((x * map_scale) + map_scale / 2, 2, (y * map_scale) + map_scale / 2));
                }
            }
        }

        goal.transform.Translate(new Vector3(((Maze_X-1) * map_scale)-1, 0, ((Maze_y-1) * map_scale)-1));

    }
}
5 Likes

Day 7a:

Well I just wasted a couple of hours trying to sort the maze out a bit, render it with better objects (like these shapes: ─ │ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼), but my brain aint playing the game.
Can’t even render out the collections to a bunch of strings to see what the data looks like.
Probably that drinking I did yesterday since I’m gonna be on call over Xmas.
Or it might be all that Slime Rancher I played today… Hard to say.

2 Likes

What do you mean?

Well it is just a bunch of cubes. I had to muck around with the sizing so the capsule could fit in the pathway.
I opted to try use corner pieces, thinner walls, cross pieces, t pieces etc so the walls are thinner and the pathways bigger. But I am probably going about it all wrong. Should be rooms with walls that may have a door in them.

Day 7b:

A couple of hours later and I had another go. Went back over the code for generating the maze and found some bugs. It was stopping a bit early making the rooms. Looks like some defects in the maze generation, repeated cubicles down the sides etc…

Also built new template objects. A simple room with four posts in the corners, and a door/wall that can fit between the posts, and be rotated to any of the four positions.

It is much easier to build up the maze now. Just spawn a room for every Room[x,y] class, and build the walls as before.

The navigation stuff wigs out if the maze is over about 15x15 rooms though, and drop to <1FPS. That’s a later issue though.

public class scenescripts : MonoBehaviour {

    //public NPCmove tmpobject;    //Set the object to spawn in properties
    //public int NPC_Count;           //Set number of ojbets to spawn in properties
    //public int x_range;             // Translation X range
    //public int y_range;             // Translation Y range

    public int Maze_Segments_x = 10;
    public int Maze_Segments_y = 10;
    Room[,] Rooms;

    public float  Map_Scale_factor = 2;

    public GameObject room_template;
    public GameObject wall_template;

    public GameObject goal;

    float counter;

    public float Map_Offset_x = 5f;
    public float Map_Offset_y = 5f;
    public float Map_Offset_z = 0.5f;


    // Use this for initialization
    void Start() {

        //for (int i = 0; i < NPC_Count; i++)
        //{
        //    tmpobject.transform.Translate(new Vector3(Random.Range(-x_range, x_range), 0, Random.Range(-y_range, y_range)));
        //    Instantiate(tmpobject);
        //}

        genMaze();

    }

    // Update is called once per frame
    void Update() {

    }



    class Room
    {
        public Wall Up;
        public Wall Down;
        public Wall Left;
        public Wall Right;

        public bool isVisited;
    }
    class Wall
    {
        public Room Room1;
        public Room Room2;

        public bool passage;

        public Wall(Room Room1,Room Room2 )
        {
            this.passage = false;
            this.Room1 = Room1;
            this.Room2 = Room2;
        }

    }

    void genMaze()
    {
        //Array of cells set to right size
        Rooms = new Room[Maze_Segments_x, Maze_Segments_y];



        List<Wall> Walls = new List<Wall>();
        int x, y;

        //Build grid of rooms
        for (y = 0; y < Maze_Segments_y; y++)
        {
            for (x = 0; x < Maze_Segments_x; x++)
            {
                Room tmproom = new Room();

                tmproom.isVisited = false;

                Rooms[x, y] = tmproom;
            }
        }
        //Run back over array and create walls between all the rooms, putting in the references to rooms etc.
        for (y = 0; y < Maze_Segments_y; y++)
        {
            for (x = 0; x < Maze_Segments_x; x++)
            {
                // fixed some bugs here in map generation
                if (x == 0)
                {
                    Rooms[x, y].Left = new Wall(Rooms[x, y], null);
                }
                else if (x == Maze_Segments_x - 1)
                {
                    Rooms[x, y].Right = new Wall(Rooms[x, y], null);
                }
                else
                {
                    Wall tmpwall = new Wall(Rooms[x, y], Rooms[x + 1, y]);
                    Rooms[x, y].Right = tmpwall;
                    Rooms[x + 1, y].Left = tmpwall;
                }


                if (y == 0)
                {
                    Rooms[x, y].Up = new Wall(Rooms[x, y], null);
                }
                else if (y == Maze_Segments_x - 1)
                {
                    Rooms[x, y].Down = new Wall(Rooms[x, y], null);
                }
                else
                {
                    Rooms[x, y].Down = new Wall(Rooms[x, y], null);
                    Wall tmpwall = new Wall(Rooms[x, y], Rooms[x, y + 1]);
                    Rooms[x, y].Down = tmpwall;
                    Rooms[x, y + 1].Up = tmpwall;
                }
            }
        }

        /*
        Modified Prim's algorithm from Wikipedia
        1. Start with a grid full of walls.
        2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
        3. While there are walls in the list:
            a. Pick a random wall from the list. If only one of the two cells that the wall divides is visited, then:
                i. Make the wall a passage and mark the unvisited cell as part of the maze.
                ii. Add the neighboring walls of the cell to the wall list.
            b. Remove the wall from the list.
        */


        //2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
        // Need to pick even index inside the maze
        // TODO: Should probably just pick 1,1 or something known!
        x = Random.Range(1, (Maze_Segments_x - 1) / 2) * 2;
        y = Random.Range(1, (Maze_Segments_y - 1) / 2) * 2;

        Rooms[x, y].isVisited = true;

        Walls.Add(Rooms[x, y].Up);
        Walls.Add(Rooms[x, y].Down);
        Walls.Add(Rooms[x, y].Left);
        Walls.Add(Rooms[x, y].Right);


        // 3. While there are walls in the list:
        do
        {
            int r = Random.Range(0, Walls.Count - 1);

            if (Walls[r] == null) { }
            else if (Walls[r].Room1 == null || Walls[r].Room2 == null) { }
            else if (Walls[r].Room1.isVisited ^ Walls[r].Room2.isVisited) //either visited but not both
            {
                Walls[r].passage = true;
                if (!Walls[r].Room1.isVisited)
                {
                    Walls[r].Room1.isVisited = true;
                    if (!Walls.Contains(Walls[r].Room1.Up)) Walls.Add(Walls[r].Room1.Up);
                    if (!Walls.Contains(Walls[r].Room1.Down)) Walls.Add(Walls[r].Room1.Down);
                    if (!Walls.Contains(Walls[r].Room1.Left)) Walls.Add(Walls[r].Room1.Left);
                    if (!Walls.Contains(Walls[r].Room1.Right)) Walls.Add(Walls[r].Room1.Right);
                }
                else
                {
                    Walls[r].Room2.isVisited = true;
                    if (!Walls.Contains(Walls[r].Room2.Up)) Walls.Add(Walls[r].Room2.Up);
                    if (!Walls.Contains(Walls[r].Room2.Down)) Walls.Add(Walls[r].Room2.Down);
                    if (!Walls.Contains(Walls[r].Room2.Left)) Walls.Add(Walls[r].Room2.Left);
                    if (!Walls.Contains(Walls[r].Room2.Right)) Walls.Add(Walls[r].Room2.Right);
                }

            }

            Walls.Remove(Walls[r]);

        } while (Walls.Count > 0);

        //Done forming maze
        GameObject tmpobject;
        
        //Build maze out of 3d objects
        for (y = 0; y < Maze_Segments_y; y++)
        {
            for (x = 0; x < Maze_Segments_x; x++)
            {
                //No room! Shouldnt happen anymore.
                if (Rooms[x,y]==null) continue;

                tmpobject = Instantiate(room_template);
                tmpobject.transform.Translate(new Vector3(Map_Offset_x + (x * Map_Scale_factor) + Map_Scale_factor / 2, Map_Offset_z, Map_Offset_y + (y * Map_Scale_factor) + Map_Scale_factor / 2));

                if (Rooms[x, y].Up != null && !Rooms[x, y].Up.passage)
                {
                    tmpobject = Instantiate(wall_template);
                    tmpobject.transform.Translate(new Vector3(Map_Offset_x + (x * Map_Scale_factor) + Map_Scale_factor / 2, Map_Offset_z, Map_Offset_y + (y * Map_Scale_factor) + Map_Scale_factor / 2));
                    tmpobject.transform.Rotate(new Vector3(0, 270, 0));
                }

                if (Rooms[x, y].Right != null && !Rooms[x, y].Right.passage && x == Maze_Segments_x - 1)
                {
                    tmpobject = Instantiate(wall_template);
                    tmpobject.transform.Translate(new Vector3(Map_Offset_x + (x * Map_Scale_factor) + Map_Scale_factor / 2, Map_Offset_z, Map_Offset_y + (y * Map_Scale_factor) + Map_Scale_factor / 2));
                    tmpobject.transform.Rotate(new Vector3(0, 180, 0));
                }

                if (Rooms[x, y].Down != null && !Rooms[x, y].Down.passage && y == Maze_Segments_y - 1)
                {
                    tmpobject = Instantiate(wall_template);
                    tmpobject.transform.Translate(new Vector3(Map_Offset_x + (x * Map_Scale_factor) + Map_Scale_factor / 2, Map_Offset_z, Map_Offset_y + (y * Map_Scale_factor) + Map_Scale_factor / 2));
                    tmpobject.transform.Rotate(new Vector3(0, 90, 0));
                }

                if (Rooms[x, y].Left != null && !Rooms[x, y].Left.passage)
                {
                    tmpobject = Instantiate(wall_template);
                    tmpobject.transform.Translate(new Vector3(Map_Offset_x + (x * Map_Scale_factor) + Map_Scale_factor / 2, Map_Offset_z, Map_Offset_y + (y * Map_Scale_factor) + Map_Scale_factor / 2));
                    tmpobject.transform.Rotate(new Vector3(0, 0, 0));
                }

            }
        }

        goal.transform.Translate(new Vector3(Map_Offset_x + ((Maze_Segments_x-1) * Map_Scale_factor)-1, 0, Map_Offset_y + ((Maze_Segments_y-1) * Map_Scale_factor)-1));

    }
}
4 Likes