Jump to content
Unity Insider Forum

Größe eines prefabs


chrische5

Recommended Posts

Hallo

 

Ich versuche mich an einem programm und möchte dazu tiles dynmaisch erstellen.

um zu wissen, wo ich das nächste tile zeichnen soll, muss ich allerdings wissen, wie groß das vorherige war. wie kann ich die entsprechende größe auslesen?

noch besser wäre es, wenn ich eine größe angeben könnte und das prefab entsprechend skaliere. aber auch dazu brauche ich zuerst die größe, um den faktor zu berechnen.

oder gibt es eine möglichkeit direkt beim instanzieren eine größe als rechteck oder so anzugeben?

 

danke

christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mach dir da keinen Stress, das irgendwie ausrechnen zu lassen, wird nur schmerzhaft. Mach dir stattdessen eine simple Komponente, die einen Vector2 bzw. Vector3 hat, der auf dem Prefab eingestellt wird und fertig. Mit OnDrawGizmos(Selected) kannst du, wenn du Lust hast, noch die eingestellte Größe visualisieren. Oder wenn du richtig abgehen willst, auch mit Handles in der Scene View einstellen. Dann liest dein Generator-Code einfach die Daten aus dieser Komponente aus.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Naja... ein Prefab ist ja einfach nur ein GameObject in den Assets und Instantiate erstellt davon eine Kopie. Wenn du ein grünes Licht als Prefab hast und instanziierst, dann wird die Farbe "grün" als Eigenschaft kopiert und das instanziierte Licht ist dann auch grün. Bei einem roten Licht wird dann rot kopiert.

Dein Prefab hat ja inhärent bestimmte Maße. Du packst die Komponente auf dein Prefab und stellst den Vektor im Inspektor per Hand ein. Dann erstellt dein Generator-Script mit Instantiate eine Kopie davon und die Kopie hat dann denselben eingestellten Vektor. Da du nicht vorhast, die Werte der Instanz zu ändern, kannst du natürlich genauso gut den Vektor aus dem Prefab selbst auslesen, ist ja derselbe Wert :)

Aber um die Frage nochmal direkt zu beantworten:

vor 3 Stunden schrieb chrische5:

aber wie kann ich dann die größe ändern.

Prefab im Editor öffnen und im Inspektor einstellen. Der Vorschlag mit der Visualisierung z.B. in OnDrawGizmosSelected war auch ernst gemeint, der kann da echt helfen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Instantiate gibt ja eine Referenz auf das neu erstellte Objekt zurück:

var instance = Instantiate(prefab);

und damit kannst du arbeiten:

var block = instance.GetComponent<LevelGenerationBlock>();
Debug.Log(block.size);

Oder halt, wie gesagt, direkt im Prefab:

var block = prefab.GetComponent<LevelGenerationBlock>();
Debug.Log(block.size);

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

hallo

 

1. ich möchte eine tilemap erstellen

2. diese besteht aus verschiedenen tiles

3. diese werden dynamisch instaziert

4. das ganze funktioniert so:

public void GenerateLayer()
        {
            var layerHeight = _layerTexture.height;
            var layerWidth = _layerTexture.width;
            for (var x = 0; x < layerWidth; ++x)
            {
                for (var y = 0; y < layerHeight; ++y)
                {
                    var idColor = _layerTexture.GetPixel(x, y);
                    var newTile = Array.Find(_layerTiles, element => element.IDColor == idColor);
                    if (newTile != null)
                    {
                        var posX = WorldStartPos.x + _tileWidth * x;
                        var posY = WorldStartPos.y + _tileHeight * y;
                        var tile = Instantiate(newTile.Prefab);
                        
                        tile.transform.position = new Vector3(posX, posY, 0);
                    }
                }
            }
        }

5. das problem ist die positionierung: tileWidth und tileHeight stelle ich händisch im editor ein.

6. das manuelle einstellen will ich aber vermeiden und will, dass das prefab immer in der größe gezeichnet wird, die ich einstelle. also entweder vergrößert, verkleinert oder eben in der originalgröße

 

ist das verständlich? wenn nicht, bin ich wahrscheinlich viel mehr auf dem holzweg als ich dachte. 😄

 

christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hmm... das ist schon ungefähr so, wie ich mir das vorgestellt hatte. Ich labere einfach mal ein bisschen, vielleicht hilft das.

So zur Vereinfachung nehmen wir mal die höheren Dimensionen raus und stellen uns einen Endless Runner vor. Da gibt's nur Stücke mit unterschiedlicher Breite, die hintereinander gepackt werden. Die Breite eines Elements wird in einer Komponente auf den Prefabs gesetzt:

public class LevelElement : MonoBehaviour
{
  public float width;
}

Der Einfachheit halber ist das ein einfaches öffentliches Feld, geht natürlich eleganter. Wir nehmen an, dass der Ursprungspunkt jedes Element auf dessen linker Seite ist.

Dann kann ein Generator folgendermaßen ein Level generieren:

public LevelElement[] elementPrefabs;

public void Generate()
{
  var x = 0f;
  
  for ( ... )
  {
    var prefab = elementPrefabs[Random.Range(0, elementPrefabs.Length)];
    var instance = Instantiate(prefab, Vector3.right * x, Quaternion.identity);
    
    x += instance.width;
  }
}

Die letzte Zeile in der Schleife ist dabei die relevante. Es wird immer die Breite jedes Teils aus der Instanz ausgelesen (wie gesagt, "prefab.width" wäre äquivalent) und auf x obendrauf addiert.

Stattdessen kann man natürlich auch die Instanz anpassen:

instance.transform.localScale = new Vector3(1f / x, 1f, 1f);

wobei sich dabei die Frage stellt, warum man nicht einfach das Prefab von vornherein auf eine normalisierte Größe hin baut.

Link zu diesem Kommentar
Auf anderen Seiten teilen

hallo

 

es wird wohl an mir liegen...😀

 

ich habe meinen code jetzt etwas nach deinen vorschlägen angepasst. mein ziel ist es eigentlich, dass ich angeben kann, wie große die einzelnen tiles sind und diese dann entsprechend dieser größe gezeichnet werden. also eben nicht immer in der originalgröße, sondern manchmal größer oder kleiner. die skalierung haut jetzt hin, aber die verschiebung noch nicht. die tiles werden, wie im anhang zu sehen übereinander gezeichnet und das will ich natürlich nicht. code sieht folgendermaßen aus (wie gesagt, habe ich gar nicht viel verändert):

 

var layerHeight = _layerTexture.height;
            var layerWidth = _layerTexture.width;
            for (var x = 0; x < layerWidth; ++x)
            {
                for (var y = 0; y < layerHeight; ++y)
                {
                    var idColor = _layerTexture.GetPixel(x, y);
                    var newTile = Array.Find(_layerTiles, element => element.IDColor == idColor);
                    if (newTile != null)
                    {
                        var posX = WorldStartPos.x + _tileWidth * x;
                        var posY = WorldStartPos.y + _tileHeight * y;
                        var tile = Instantiate(newTile.Prefab, gameObject.transform, true);
                        tile.transform.localScale = new Vector3(_tileWidth, _tileHeight);
                        tile.transform.position = new Vector3(posX, posY, 0);
                    }
                }
            }

christoph

Unbenannt.png

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich meine halt im Editor. Warum sind deine Prefabs nicht alle in der richtigen Größe? Das ist, als ob du ein Lego-Set kaufst, um damit etwas bestimmtes zu bauen, dafür die Steine aber alle als erstes in der Mitte durchsägst. Warum willst du das unbedingt beim Spawnen per Code lösen, anstatt die Prefabs einfach in die richtige Größe zu bringen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi chrische5!

Also genau wie Sascha würde ich dir auch erstmal empfehlen das du dafür sorgst das alle deine Prefabs die selbe (zumindest optische größe haben). Wenn du die objekte alle selbst erstellt hast sollte das das kleinste problem sein... einfach alle mal von hand in eine Unity szene droppen und entweder

  • Zusehen das alle auf dem Transform die gleichen Scale werte haben (in der gesamten hierarchie der Objekte sofern es sich um Objekte handelt mit mehreren Unterobjekten)
  • Zusehen das die Objekte wenigstens optisch etwa gleich groß sind. Das wäre die Lösung wenn du nicht selbst die Objekte erstellt hast und / oder alle in unterschiedlichen größen nach Unity importiert wurden.

Wenn du die Objekte nun alle auf eine einheitliche größe gebracht hast z.B. scale werte von 1/1/1. Kannst du beim instanzieren getrost darauf verzichten die objekte zu skalieren und musst dich nur noch um die positionierung kümmern (Da alle objekte die skalierung von 1/1/1 haben wären alle zentren der objekte 1 einheit (2D) oder ~1.41 Einheiten (3D) von einander entfernt).

Soweit die Theorie....

nun weis ich aus eigener erfahrung das aller Anfang schwer sein kann!! Und vielleicht sollen nicht alle Objekte die selbe blockgröße haben (ist bei Lego ja auch nicht so)... dann wird es natürlich ein wenig komplizierter, aber der schlüssel zur lösung ist das du dir, wie Sascha schon empfohlen hat, die blöcke standardisierst ... d.h. du überlegst dir wie groß dein Grid bzw deine kleinste einheit sein soll (z.B. 1 Einheit) und dann baust du darauf auf .. z.B. hat der nächst größere Block die größe 2/1/1 und der danach 4/1/1 oder wie auch immer. Dann bist du in der lage relativ stressfrei die blöcke im code zu platzieren und musst dir nicht unnötig den kopf über objektgrößen zerbrechen!

Sollte dir das wider Erwarten noch nicht geholfen haben oder du trotz unserer ratschläge dennoch die größe programmatisch berechnen willst solltest du dir einmal Renderer.bounds ansehen... vorallem bounds.extends würde dir die tatsächliche ausdehnung eines Objektes geben... dafür müsstest du dir nur vom GameObject die RendererComponent holen:

Bounds bounds = myAwesomeGameObject.GetComponent<Renderer>().Bounds;

Aber sei gewarnt .. sollten deine objekte aus mehreren unterobjekten bestehen, die nicht ALLE ihre skalierungswerte auf 1/1/1 haben, wirst du Pandoras Büchse öffnen wenn du versuchst dann die größe der Objekte via script zu setzen und ich schätze diesmal wird die Hoffnung nicht in der Büchse bleiben ;)

Hoffe wir konnten dir helfen...

VG Cxyda

Link zu diesem Kommentar
Auf anderen Seiten teilen

hallo

 

vorneweg auch wenn das immer gilt: danke für eure bemühungen und eure geduld.

mir ist grundsätzlich klar, dass man das im editor einstellen kann. stellen wir uns aber mal einfarbige tiles vor (grün - land, blau - wasser). es wäre doch toll, wenn ich beim erstellen eines layers angeben kann, wie groß dann diese tiles gezeichnet werden. sie liegen also nur einmal vor und werden je nach einstellung im editor skaliert. das scheint mir gar kein so verrückter anwendungsfall zu sein. um das zu bewerkstelligen, muss ich allerdings die orginalgröße des prefabs haben, um dann die skalierung usw entprechend zu machen. ich versuche nachher mal GetComponent<Renderer>().Bounds und werde dann berichten.

 

christoph 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 4 Stunden schrieb chrische5:

hallo

 

vorneweg auch wenn das immer gilt: danke für eure bemühungen und eure geduld.

mir ist grundsätzlich klar, dass man das im editor einstellen kann. stellen wir uns aber mal einfarbige tiles vor (grün - land, blau - wasser). es wäre doch toll, wenn ich beim erstellen eines layers angeben kann, wie groß dann diese tiles gezeichnet werden. sie liegen also nur einmal vor und werden je nach einstellung im editor skaliert. das scheint mir gar kein so verrückter anwendungsfall zu sein. um das zu bewerkstelligen, muss ich allerdings die orginalgröße des prefabs haben, um dann die skalierung usw entprechend zu machen. ich versuche nachher mal GetComponent<Renderer>().Bounds und werde dann berichten.

 

christoph 

  • Also ich denke du denkst da zu kompliziert. Das ist garkein problem... mach dir wie schon gesagt 1 Prefab das die standard größe 1 hat (x/y/z  =  1/1/1)
  • dann machste dir z.B. ein TileComponent das z.B. die farbe je nach Typ des Tiles zuweist (Wasser =  blau usw)
  • dann weist du beim generieren der Tiles dem TileComponent den typ zu z.B. Wasser / Land etc -> Farbe updatet sich
  • dann hast du dir irgendwo aufgeschrrieben land = 2x2 einheiten groß, wasser 5x5 etc... und dann skalierst du das standadisierte ausgangsprefab das 1/1/1 groß ist mit den entsprechenden werten und setzt die position
    • Oder du arbeitest mit Prefabs und nested prefabs ... machst von jedem TileTyp 1 prefab (wie mehrfach schon oben beschrieben) und instanzierst das entsprechende prefab, das schon skaliert ist, und positionierst es danach entsprechend ... eigentlich easy ¯\_(ツ)_/¯

also entweder stehen Sascha und ich auf dem schlauch, oder du 😉

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo

und genau da muss ich einen fehler machen. die prefabs haben einen scale von 1,1,1. und die positionen werden richtig gesetzt, aber die prefabs werden dann anscheinend zu "groß" beim skalieren und überlappen sich, wie in dem bild, welches ich weiter oben angehangen habe. ich verstehe es einfach nicht. das mit den bounds war ein denkfehler von mir. das geht zwar (wenn auch etwas komplizierter) das ergebnis ist aber das gleiche: die tile überlappen sich. der code hat sich praktisch nicht geändert:

 

public void GenerateLayer()
        {
            var layerHeight = _layerTexture.height;
            var layerWidth = _layerTexture.width;
            for (var x = 0; x < layerWidth; ++x)
            {
                for (var y = 0; y < layerHeight; ++y)
                {
                    var idColor = _layerTexture.GetPixel(x, y);
                    var newTile = Array.Find(_layerTiles, element => element.IDColor == idColor);
                    if (newTile != null)
                    {
                        var posX = WorldStartPos.x + _tileWidth * x;
                        var posY = WorldStartPos.y + _tileHeight * y;
                        var tile = Instantiate(newTile.Prefab, gameObject.transform, true);
                        tile.transform.localScale = new Vector3(_tileWidth, _tileHeight);
                        Debug.Log("#Layer#: posX: " + posX + " posY: " + posY);
                        tile.transform.position = new Vector3(posX, posY, 0);
                    }
                }
            }
        }

normalerweise müssten die tiles doch nun direkt neben- und übereinander gesetzt werden. warum überlappen sie sich?

 

christoph

Link zu diesem Kommentar
Auf anderen Seiten teilen

🤔 ich hab grade wieder mehrere Fragezeichen im kopf ^^' Also dann mal los...

erstmal ist es schwer da ne ferndiagnose zu stellen da es an wirklich vielen dingen liegen kann.

  • Aber fangen wir nochmal vorne an .. ehrlich gesagt werde ich nicht 100% schlau aus deinem Screenshot mit den tiles.. für mich sieht es so aus als wären die tiles nicht quadratisch sondern recht eckig oder ? Das einzige voll sichtbare stück scheint ein endstück zu sein und das hat doch auf 3 seiten keine anschluss möglichkeit, sollte da also transparent sein ?
  • Dein code sieht so erstmal gut aus, allerdings könnte sich beim parenten an gameObject.transform ein fehler einschleichen, checke doch mal ob gameObject nicht doch vielleicht skaliert ist? Das (bzw. alle) parent objekte müssen natürlich alle scale = 1/1/1 haben (und sollten am besten auch auf 0/0/0 positioniert sein) damit die Rechnung aufgeht.
  • Ich würde dir raten die tiles mit
tile.transform.localPositon = new Vector3(posX, posY, 0);

zu positionieren wenn du sie schon einem paren anhängst (sollte aber nicht der grund für das overlap sein)

 

Allerdings kommt mir jetzt gerade beim schreiben dieser Zeile die lösung (glaube ich) für dein Problem ! Checke mal bitte den "Pixel Per Unit" wert deiner Tile grafik ! Die muss natürlich der Pixelgröße entsprechen damit das sprite auch genau 1 unit groß ist ! Also wenn dein Sprite 512x512 pixel groß ist musst du da natürlich auch 512 eintragen (512 pixel entsprechen dann auch genau 1 unit!) Sonst ist die skalierung von 1/1/1 natürlich relativ korrekt aber die tatsächliche größe ist nicht 1 einheit lang und 1 einheit breit!

 

Beste grüße, Cxyda

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 26 Minuten schrieb chrische5:

scale von 1,1,1

Denk daran, dass die "Scale"-Eigenschaft der Transform-Komponente ein Faktor ist, keine absolute Größe. Wenn du einen 2x2x2-Würfel importierst und den auf Scale 1,1,1 hast, dann ist der immer noch 2x2x2 groß und nicht 1x1x1. Das ist bei Sprites nicht anders. Den Konvertierungsfaktor von Sprites findest du in den Import Settings deines Bild-Assets. Da kannst du angeben, wie viele Pixel eine Einheit sein sollen. Bei einem Sprite mit z.B. 128x128 Pixeln Größe gibst du da 128 ein und dein Sprite wird 1x1 groß sein.

Und ach ja... genau das hat @Cxyda83 schon gerade geschrieben. Zu langsam :P

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...