Пример #1
0
    public override List<RoadAtom> SpawnRoads(BranchAtom currentAtom, CityGenerator gen)
    {
        List<RoadAtom> production = new List<RoadAtom>();

        if (currentAtom.Creator != null) {
            // Determine the vector from the creator atom's position and the current node's position, and split the
            // result into two groups:
            //   - the vector is more aligned with the radius vector from the origin to the current node
            //   - the vector is more aligned with the tangent of the circle which the radius vector defines
            Vector3 radiusVector = currentAtom.Node.position - gen.transform.position;
            Vector3 fromCreator = currentAtom.Node.position - currentAtom.Creator.Node.position;

            Vector3 tangent = Quaternion.Euler(0f, -90f, 0f) * radiusVector;

            Vector3 left = (Quaternion.Euler(0f, -90f, 0f) * fromCreator).normalized;
            Vector3 straight = fromCreator.normalized;
            Vector3 right = (Quaternion.Euler(0f, 90f, 0f) * fromCreator).normalized;

            // Get the angle between the radius vector and the vector from the creator to the current node
            // (Note: Vector3.Angle returns a value from 0f to 180f)
            float angle = Vector3.Angle(radiusVector, fromCreator);
            if (angle < 45f || angle > 135f) {
                // The incoming orientation is more aligned with the radius vector
                left = AlignVector(left, tangent);
                straight = AlignVector(straight, radiusVector);
                right = AlignVector(right, tangent);
            } else {
                // The incoming orientation is more aligned with the tangent
                left = AlignVector(left, radiusVector);
                straight = AlignVector(straight, tangent);
                right = AlignVector(right, radiusVector);
            }

            // For now, spawn roads in all three directions
            // TODO: Temporary
            production.Add(new RoadAtom(left, currentAtom.Node, Rule.Type.Radial));
            production.Add(new RoadAtom(straight, currentAtom.Node, Rule.Type.Radial));
            production.Add(new RoadAtom(right, currentAtom.Node, Rule.Type.Radial));
        } else {
            // There's no creator (which means this is the axiom node), so we'll create roads shooting in all directions
            // TODO: Temporary
            production.Add(new RoadAtom(Vector3.forward, currentAtom.Node, Rule.Type.Radial));
            production.Add(new RoadAtom(Vector3.back, currentAtom.Node, Rule.Type.Radial));
            production.Add(new RoadAtom(Vector3.left, currentAtom.Node, Rule.Type.Radial));
            production.Add(new RoadAtom(Vector3.right, currentAtom.Node, Rule.Type.Radial));
        }

        return production;
    }
Пример #2
0
    public override List<RoadAtom> SpawnRoads(BranchAtom currentAtom, CityGenerator gen)
    {
        List<RoadAtom> production = new List<RoadAtom>();

        if (currentAtom.Creator != null) {
            // Get the orientation of the "parent" road (the one we're continuing in this production)
            MapNode toNode = currentAtom.Node;
            MapNode fromNode = currentAtom.Node.edges[0].FromNode;
            Vector3 parentDirection = (toNode.position - fromNode.position).normalized;

            // Get the elevation at the current point
            float currentElevation = gen.ElevationAt(currentAtom.Node.position);

            // Create three roads: left, right and straight with regard to the "parent" road's direction
            float[] angles = new float[] { -90f, 0f, 90f };
            foreach (float angle in angles) {
                // Rotate the direction vector to get the direction we'll probe in
                Vector3 roadDirection = Quaternion.Euler(0f, angle, 0f) * parentDirection;

                // Probe elevations around the given direction and get the direction of the road which is least steep
                roadDirection = LeastSteepDirection(currentAtom.Node.position, roadDirection, currentElevation, gen);

                // Create a new RoadAtom with the given road direction
                RoadAtom roadAtom = new RoadAtom(roadDirection, currentAtom.Node, Rule.Type.Rectangular);

                // Add it to the production
                production.Add(roadAtom);
            }
        } else {
            // This is the axiom, just spawn roads in all directions
            production.Add(new RoadAtom(Vector3.forward, currentAtom.Node, Rule.Type.Rectangular));
            production.Add(new RoadAtom(Vector3.back, currentAtom.Node, Rule.Type.Rectangular));
            production.Add(new RoadAtom(Vector3.left, currentAtom.Node, Rule.Type.Rectangular));
            production.Add(new RoadAtom(Vector3.right, currentAtom.Node, Rule.Type.Rectangular));
        }

        return production;
    }
Пример #3
0
    public override List<Atom> Produce(CityGenerator generator)
    {
        List<Atom> production = new List<Atom>();

        // Create a new map node
        Rule rule = generator.RuleAtCoordinates(Node.position);
        MapNode spawn = new MapNode(Node.position + forward * rule.CalculateRoadLength(this, generator));
        spawn.ruleType = ruleType;

        // Fetch the spawned node's neighbours
        List<MapNode> neighbours = generator.GetNeighbours(spawn, generator.neighboursSearchRadius);

        // Check if the node is close to one of its neighbours so that they can be merged into one node
        bool merged = false;
        Vector2 spawnPosition = spawn.PositionAsVector2;
        foreach (MapNode neighbour in neighbours) {
            // Convert coords to 2D, we don't want elevation messing around with our merging algorithm
            Vector2 neighbourPosition = neighbour.PositionAsVector2;

            // Check for proximity
            if (Vector2.Distance(spawnPosition, neighbourPosition) <= generator.nodeMergingMaximumDistance) {
                // The neighbour merges
                spawn = neighbour;

                // Stop iterating
                merged = true;
                break;
            }
        }

        // Create a map edge between the current map node and the spawn
        MapEdge spawnedEdge = new MapEdge(Node, spawn);

        if (!merged) {
            // Perform the intersection of the spawned node's edge against the neighbours' edges
            bool intersected = Intersect(spawn, spawnedEdge, neighbours);

            // Raycast to check for water
            RaycastHit hit;
            if (Physics.Raycast(spawn.position + Vector3.up * 1000f, Vector3.down, out hit)) {
                if (hit.collider.gameObject.tag == "Water") {
                    // Spawned over water, remove the edge from the starting node
                    Node.edges.Remove(spawnedEdge);

                    // Skip this node
                    return production;
                }

                // Set the Y coordinate of the spawned node to match the height where the raycast hit
                spawn.position.y = hit.point.y + 0.1f;
            }

            // Add the newly created map node to the environment
            generator.AddMapNode(spawn);

            // Continue producing only if there were no intersections
            if (!intersected) {
                // Summon a branch atom
                Atom branch = new BranchAtom(this);
                branch.Node = spawn;

                // Add the branch atom to the list of results and we're done
                production.Add(branch);
            }
        }

        return production;
    }
Пример #4
0
    private void ProduceIfNecessary()
    {
        // Check if we're here for the first time
        if (currentGeneration == null) {
            // Initialize the list
            currentGeneration = new List<Atom>();

            // Create the axiom
            // TODO: Add a configurable axiom
            Atom axiom = new BranchAtom(null);
            axiom.Node = new MapNode(transform.position);
            RootNode = axiom.Node;

            // Find out correct height for it
            RaycastHit hit;
            if (Physics.Raycast(transform.position + Vector3.up * 1000f, Vector3.down, out hit)) {
                axiom.Node.position.y = hit.point.y + 0.1f;
            }

            currentGeneration.Add(axiom);

            actualGenerations = 0;
        }

        // Check if we need to produce the L-system
        while (actualGenerations < targetGenerations) {
            Produce();
        }
    }
Пример #5
0
 /// <summary>
 /// Spawns the roads for the given branch atom. Each branch atom invokes this method during its production in order
 /// to calculate the roads which will be spawned from it.
 /// </summary>
 /// <returns>The roads.</returns>
 /// <param name="currentAtom">Current atom.</param>
 /// <param name="gen">City generator.</param>
 public abstract List<RoadAtom> SpawnRoads(BranchAtom currentAtom, CityGenerator gen);