Comment dessiner un plan dans Unity3d?

13

J'essaie de mettre en évidence un rectangle de hauteur arbitraire. Je pensais que la façon la plus simple de le faire serait de créer un objet de jeu "boîte" distinct qui délimiterait le rectangle.

J'ai essayé à la fois avec un MeshRenderer + Transparent Texture et un LineRenderer pour décrire les quatre points du rectangle. Ni l'un ni l'autre ne sont très satisfaisants.

entrez la description de l'image ici

(Rendu de ligne au milieu, cube redimensionné à droite)

Quelle est la bonne façon de procéder? J'essaie d'obtenir quelque chose comme le rectangle de gauche - un simple périmètre de largeur fixe à travers quatre points de mon choix.

Raven Dreamer
la source

Réponses:

8

Utilisez GUI.Box().

Si vous n'avez besoin que d'un rectangle 2D, l'interface graphique est la voie à suivre. Créez un nouveau GUIStyle en utilisant un simple rectangle comme texture (l'intérieur du rectangle doit être transparent, bien sûr), définissez sa Bordervaleur pour qu'il ne soit pas étiré et appelez GUI.Box(new Rect(...),"",myGuiStyle);.

Vous pouvez utiliser la Camera.WorldToScreenPointméthode si vous souhaitez définir quelque chose en coordonnées universelles (c'est-à-dire en 3D), rappelez-vous simplement que dans les coordonnées universelles d'Unity, y va de bas en haut et dans l'interface graphique, y va de haut en bas.

Exemple de code:

void OnGUI()
{
    //top left point of rectangle
    Vector3 boxPosHiLeftWorld = new Vector3(0.5f, 12, 0);
    //bottom right point of rectangle
    Vector3 boxPosLowRightWorld = new Vector3(1.5f, 0, 0);

    Vector3 boxPosHiLeftCamera = Camera.main.WorldToScreenPoint(boxPosHiLeftWorld);
    Vector3 boxPosLowRightCamera = Camera.main.WorldToScreenPoint(boxPosLowRightWorld);

    float width = boxPosHiLeftCamera.x - boxPosLowRightCamera.x;
    float height = boxPosHiLeftCamera.y - boxPosLowRightCamera.y;


     GUI.Box(new Rect(boxPosHiLeftCamera.x, Screen.Height - boxPosHiLeftCamera.y, width, height),"", highlightBox);
}
Ça ne fait rien
la source
À moins que je ne me trompe, GUI.Box () sera toujours dessiné au-dessus des objets de la scène? Est-il possible d'avoir un élément GUI derrière ou partiellement derrière un gameobject?
Raven Dreamer
Oui, l'interface graphique est toujours dessinée après les autres objets. Il y a un hack que vous pouvez utiliser pour rendre une caméra séparée au-dessus de l'interface graphique, mais c'est un peu lourd.
Nevermind
Cette réponse m'a donné ce dont j'avais besoin, mais je la garde ouverte pour l'instant dans l'espoir qu'il existe une meilleure façon de le faire dans le cas général.
Raven Dreamer
J'ai accepté votre modification en ajoutant un exemple de code et corrigé le bogue GUI-y-upside-down.
Nevermind
1

Vous trouverez ci-dessous une approche sans shader.

Considérez votre boîte 2D comme rien de plus que quatre lignes, où chaque ligne est étirée dans une seule dimension (les deux autres dimensions sont la coupe transversale du bord). C'est un peu comme si vous construisiez une boîte dans la vie réelle, où vous assemblez des longueurs de bois variables qui ont toutes la même taille de section transversale.

Dans cet esprit, vous pouvez concevoir un composant, par exemple BoxBuilder, qui lorsqu'il est attaché à un GameObject, crée et gère quatre GameObjects enfants. Chaque objet de jeu enfant est l'un des bords de votre boîte et peut simplement être un cube 3D étiré dans une seule dimension. Avec un widthet heightdéfini BoxBuilderniveau, vous pouvez calculer le positionnement nécessaire et à l' échelle non uniforme des quatre bords de l' enfant. Ce sera beaucoup de pos.x=w/2, pos.y=h/2, ..., scale.x=h, scale.y=w, etc. sorte de code.

Bien que je pense que vous ne demandiez que le 2D, notez que cette même idée peut être appliquée aux boîtes 3D si nécessaire, où le BoxBuildermaintenant doit créer et gérer 12 bords enfants, mais encore une fois, mettre à l'échelle chaque bord dans une dimension locale.

DuckMaestro
la source
Réalisable, mais beaucoup trop compliqué.
Nevermind
Si je devais le faire de cette façon, j'utiliserais simplement des moteurs de rendu à 4 lignes ...
Raven Dreamer
Cependant, cela gère correctement la profondeur.
DuckMaestro
1

Un moyen simple consiste à utiliser un shader avec deux passes: la première passe utilise le vertex shader pour redimensionner un peu l'objet et utilise le pixel shader pour le colorer en une couleur unie correspondant à la couleur souhaitée pour le contour, et puis le deuxième Pass fait le rendu régulier.

UBSophung
la source
Pourriez-vous donner un exemple de code pour mieux expliquer ce que vous voulez dire?
Raven Dreamer
Cela ne fonctionnerait que pour les objets convexes (comme un rectangle) dont l'origine est à son centre géométrique.
rootlocus
1

J'ai eu ce même problème lors de la création d'un contour, sauf que j'avais besoin de créer le "trait" pour un cube 3D, et j'ai trouvé une nouvelle façon de le faire que je n'ai vu nulle part ailleurs en ligne.

Dans l'image ci-dessous, il y a deux formes avec des contours. Celui de droite est un cube construit avec LineRenderer, qui crée des faces planes qui se tournent toujours vers l'utilisateur. J'ai trouvé cette méthode super glitchy, avec des "coups" aléatoires qui imitaient les triangles qui composent le visage.

Celui de gauche est mon «innovation» avec 12 cubes maigres séparés construisant ce qui ressemble à un contour. Pour modifier la taille du «trait» dans le contour, je dois augmenter / diminuer la taille des deux côtés de chacun des 12 cubes maigres. Cela fonctionnerait également pour les contours 2D. Il suffit d'appliquer un matériau pour changer la couleur et le tour est joué!

deux contours différents

Dans cette image, vous pouvez voir un détail de la structure de ce cube. Tout cela pourrait être créé au moment de l'exécution, mais je l'ai fait à la main et l'ai utilisé comme préfabriqué.

détail

ow3n
la source
0

Je suis actuellement confronté au même problème, et ma solution est exactement ce que DuckMaestro et Raven Dreamer ont suggéré - Avoir un script qui crée 4 objets enfants au moment de l'exécution, chacun représentant un côté de la bordure et attacher des rendus de ligne à chacun.

Dans mon cas, je devais constamment redimensionner la bordure pour la garder autour de mon objet (un maillage de texte [qui utilise un rendu de maillage] pour un champ de texte personnalisé), donc à chaque mise à jour, je faisais ceci:


float width = Mathf.Max(renderer.bounds.size.x + paddingX * 2, minWidth);      
float x = renderer.bounds.center.x - width / 2;
float height = renderer.bounds.size.y + paddingY * 2;
float y = renderer.bounds.center.y - height / 2;

AlterBorder(0, new Vector3(x - thickness / 2, y, 0), new Vector3(x + width + thickness / 2, y, 0)); //Bottom edge going left to right
AlterBorder(1, new Vector3(x + width, y + thickness / 2, 0), new Vector3(x + width, y + height - thickness / 2, 0)); //Right edge going bottom to top
AlterBorder(2, new Vector3(x + width + thickness / 2, y + height, 0), new Vector3(x - thickness / 2, y + height, 0)); //Top edge going right to left
AlterBorder(3, new Vector3(x, y + height - thickness / 2, 0), new Vector3(x, y + thickness / 2, 0)); //Left edge going top to bottom

AlterBorder() accède simplement au rendu de ligne approprié (spécifié par le premier paramètre) et définit respectivement son début et sa fin sur le premier et le deuxième vecteur.

Notez que j'ai utilisé renderercomme référence pour la taille, mais vous pouvez évidemment utiliser n'importe quel rectangle, tant que x, y est le coin supérieur gauche.

D'après ce que je peux dire, cela fonctionne très bien, a fière allure dans le jeu car je peux facilement déplacer mon objet bordé dans les 3 axes (même le faire pivoter, et puisque les rendus de ligne font toujours face à la caméra, cela n'a pas l'air bizarre), et est pas difficile à mettre en œuvre.

romain
la source