Jump to content
Unity Insider Forum
Stephan F.

Inselwelt per Script erzeugen

Recommended Posts

Hallo,

ich bin seit einigen Tagen dabei eine Inselwelt per Script zu erzeugen, was auch Grundsätzlich schon funktioniert. Das Problem aktuell ist, dass sich die Inseln derzeit überlappen. Der aufmerksame Leser wird wissen, wieso das so ist, wenn man das Script liest. Ich hatte schon verschiedene Lösungsansätze, die aber nicht so funktioniert haben wie ich es vorgestellt habe. Meine Idee wäre ich es von Random.Range weg zu kommen und die Werte Irgendwie anders zu Berechnen bzw die erzeugten Werte direkt zu checken ob an der Position schon eine andere Insel ist oder nicht. Die Besonderheit ist allerdings, das sich die Inseln auch etwas Raum für sich beanspruchen, so das Platz zwischen den Inseln ist.

Und wer sich außerdem fragt wieso die Inseln jetzt kein Terrain sind. Die Antwort lautet, ein Terrain kann man nicht drehen...<_<

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


public class LoadIsland : MonoBehaviour
{

    public GameObject level;
    private Object[] arrayOfIsland;

    void Start()
    {
        arrayOfIsland = Resources.LoadAll("Inseln");
        Vector3 position = level.transform.localScale;
        List<position> listOfPositions = getPosition(position);
        setIslands(listOfPositions);
    }

    private void setIslands(List<position> position)
    {
        for (int i = 0; i < position.Count; i++)
        {
            GameObject island = (GameObject)Instantiate(arrayOfIsland[0]);
            island.transform.localPosition = new Vector3(position[i].XPosition, 25.0F, position[i].ZPosition);
            island.name = "insel";
            island.isStatic = true;
        }
    }

    private List<position> getPosition(Vector3 position)
    {
        List<position> listOfPositions = new List<position>();
        position = position * 8;
        for (int i = 0; i < 25; i++)
        {
            position singlePosition = new position();
            singlePosition.XPosition = Random.Range(position.x * -1, position.x);
            singlePosition.ZPosition = Random.Range(position.y * -1, position.y);
            singlePosition.YRotation = getRotation();
            listOfPositions.Add(singlePosition);
        }
        return listOfPositions;
    }

    private float getRotation() {
        List<float> listOfRotations = new List<float>();
        listOfRotations.Add(0.0F);
        listOfRotations.Add(90.0F);
        listOfRotations.Add(180.0F);
        listOfRotations.Add(270.0F);
        float rotation = listOfRotations[Random.Range(0, listOfRotations.Count)];
        return rotation;
    }
}

MFG

Share this post


Link to post
Share on other sites

Als Idee würde ich die Inseln am Start gleichmäßig auf die verfügbare Fläche verteilen (in einem Raster), so daß alle Inseln ausreichend Platz haben. Man kann hier aber auch andere Ansätze verfolgen, wie beispielsweise, daß alle Inseln sich am Anfang zusammengeballt im Zentrum der Karte befinden. Zudem würde ich jeder Insel einen Trigger Collider (+RB ) verpassen, der die gesamte Ausdehnung der Insel abdeckt.
Nun würde ich anfangen alle Inseln über einen zufällig erzeugten Pfad zu verschieben und zwar so lang, bis eine gewisse Pfadlänge erreicht wurde oder bis die gerade aktuell verschobene Insel mit einer anderen Insel (oder dem Rand) kollidiert. Ich kann dir nicht sagen, ob dies dann die beste Lösung ist, aber ich finde diese Methode hat den Vorteil, daß damit eine natürliche Kontinentaldrift simuliert wird. Die Ausgangspositionen der Inseln und die erzeugte Zufallsbewegung der Inseln beeinflussen dabei das Endresultat.
 

Share this post


Link to post
Share on other sites

Ich habe die Idee auch gleich mal umgesetzt. Dabei werden 2 Klassen verwendet. Die 1. Klasse "StartPlacement" erzeugt eine zufällige Anordnung der Objekte innerhalb eines Bounding-Volumes (Spielfeldgrenzen). Und die 2. Klasse "DriftMovement" verschiebt danach ein Objekt innerhalb der Spielfeldgrenzen.

Hier siehst du die Verwendung der beiden Klassen in einer Beispielszene:
https://ufile.io/wji3t

Klasse "StartPlacement"
- hängt am Spielfeldobjekt, d.h. das Objekt das die Grenzen der Spielwelt definiert, sollte ein einfacher Boxcollider sein
- dem Skript werden alle Mesh-Objekte (Inseln) zugewiesen
- erzeugt die zufällige Startpositionierung innerhalb eines Rasters

using UnityEngine;
using System.Collections;

public class StartPlacement : MonoBehaviour {

    public Transform[] transforms;

    private Transform[] ShuffledTransforms;
    private Renderer borderRenderer;

    void Start () {
        ShuffledTransforms = new Transform[transforms.Length];
        int count = 0;
        foreach (Transform transform in ShuffledTransforms)
        {
            ShuffledTransforms[count] = transforms[count];
            count++;
        }

        // Mischen
        reshuffle(ShuffledTransforms);

        borderRenderer = this.GetComponent<Renderer>();
        Vector3 step = borderRenderer.bounds.size;
        float stepInterval = Mathf.Sqrt(transforms.Length);
        float stepx = step.x / stepInterval;
        float stepz = step.z / stepInterval;
        float currentz = 0.5f;
        float currentx = 0.5f;
        foreach (Transform transform in ShuffledTransforms)
        {
            transform.position = borderRenderer.bounds.min + new Vector3(currentx * stepx, borderRenderer.bounds.extents.y, currentz * stepz);
            currentz += 1f;
            print(currentz);
            if (currentz > stepInterval)
            {
                currentx += 1f;
                currentz = 0.5f;
            }
        }
    }


    void reshuffle(Transform[] transforms)
    {
        // Knuth shuffle algorithm :: courtesy of Wikipedia :)
        for (int t = 0; t < transforms.Length; t++)
        {
            Transform tmp = transforms[t];
            int r = Random.Range(t, transforms.Length);
            transforms[t] = transforms[r];
            transforms[r] = tmp;
        }
    }
}

Klasse DriftMovement
- hängt an jeder Insel
- das Spielfeldobjekt muss zugewiesen werden!
- definiert die maximale Schrittweite (Pfadlänge)
- verschiebt die Inseln auf ihre Endposition

using UnityEngine;
using System.Collections;

public class DriftMovement : MonoBehaviour
{
    public GameObject border;
    public int maxIterations = 150;

    private Vector2 movement;
    private Renderer borderRenderer;
    private Renderer myRenderer;
    private bool stopMovement = false;
    private int currentIterations;

    void Start () {
        borderRenderer = border.GetComponent<Renderer>();
        myRenderer = this.GetComponent<Renderer>();

        movement = Random.insideUnitCircle;
    }
	
	void FixedUpdate () {
        if (currentIterations > maxIterations) return;
        else currentIterations += 1;

        if (!ContainBounds(borderRenderer.bounds, myRenderer.bounds)) {
            stopMovement = true;
            return; // Spielfeldrand erreicht
        }

        // Random move    
        if (!stopMovement)
        {
            transform.position = transform.position + (new Vector3(movement.x, transform.position.y, movement.y)) * Time.deltaTime;
        }

        // Rotationsvector
        float degrees = Random.Range(-5f, 5f);
        movement = Quaternion.Euler(0, 0, degrees) * movement;
    }

    bool ContainBounds(this Bounds bounds, Bounds target)
    {
        return bounds.Contains(target.min) && bounds.Contains(target.max);
    }

    void OnTriggerEnter(Collider other)
    {
        if (other != border.GetComponent<Collider>())
        {
            stopMovement = true;
        }
    }
}


Wenn die Verschiebung schneller vonstatten gehen soll, dann hinter Time.deltaTime noch ein Speedfaktor anhängen (>1). Wenn dieser zu hoch wird könnte es dann irgendwann Probleme mit der Kollisionserkennung geben. Bei der Generierung der Spielewelt solltest du die Szene beispielsweise hinter einem GUI-Image verdecken.

Share this post


Link to post
Share on other sites

Wenn du noch Fragen hast zu den Skripten, aber schau dir erst einmal alles in Ruhe an und experimentiere damit. Ich konnte nun auch nicht mit den Dimensionen deiner Inseln testen, aber es sollte so generell gehalten sein, daß es auch damit funktioniert. Wichtig ist nur, daß die Collider der Inseln sich am Anfang vollständig innerhalb des Colliders der Spielewelt (gameObjekt "border") befinden.

Share this post


Link to post
Share on other sites

Heyho,

also einiges ist auch vollkommen unbekannt und hängt von User ab. Zum Beispiel soll der User entscheiden wie Groß die Inselwelt sein soll, die Größe legt auch die anzahl der Inseln fest. So das zum Beispiel eine kleine Inselwelt 20 Inseln hat und eine große Inselwelt dann 40 Inseln.

mfg

Share this post


Link to post
Share on other sites

Ah ok, je großer du das GameObjekt für den Border (Boxcollider der Grenzen Spielwelt) ziehst, desto mehr Platz haben die Inseln am Anfang und desto höher könnte man die Anzahl der Iterationen einstellen und desto seltener kollidieren die Inseln. Zudem sollte man mehr Platz schaffen, wenn die Inseln sehr unterschiedliche Größen haben, damit auch eine relativ große Insel genug Platz hat. Man könnte man zusätzlich auch noch einbauen, daß sich die Bewegung zweier Inseln umkehrt sobald sie kollidieren (dann hat man weniger zusammengefallene Inseln, aber ich finde zusammengefallene Inseln können eben auch topologisch interessant sein, hängt eben davon ab was gewünscht ist). 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...