Regardez la démo ci-dessous et commençons!
Cliquez sur la démo pour la mettre en évidence, puis utilisez les touches fléchées pour déplacer le vaisseau..Comme vous pouvez le constater, il existe deux manières de procéder. Le premier est plus facile à comprendre. La deuxième solution n’est pas beaucoup plus compliquée, mais nécessite une réflexion originale. Nous allons couvrir les deux.
Commençons par mettre en place la scène. Lancez Unity, démarrez un nouveau projet et réglez la position de la caméra sur x = 0
, y = 0
(z
peut être ce que vous aimez). Vous voudrez probablement que la caméra soit en mode orthographique; notre habillage d’écran fonctionnera en mode perspective, mais il se peut que cela ne ressemble pas à ce que vous souhaitez. N'hésitez pas à expérimenter.
Note de l'éditeur: Voir le commentaire de Clemens pour plus d'informations sur la réalisation de ce travail en mode perspective.
Ajoutez un objet qui va s'enrouler et le faire bouger. Vous pouvez utiliser le ShipMovementBehaviour
script de la source de démonstration.
Dans mon cas, j'ai un simple vaisseau spatial (un cône) avec un mouvement semblable à un astéroïde. Comme vous pouvez le voir sur l'image, je préfère avoir le maillage parenté à l'objet principal. Que vous le fassiez comme ça ou non, que vous ayez une ou plusieurs mailles, peu importe; nous allons faire un bon script d'emballage d'écran qui fonctionne dans tous les cas.
L'idée de base derrière le bouclage d'écran est la suivante:
Donc, la première chose à faire est de vérifier si l'objet est complètement sorti de l'écran. Un moyen simple de le faire dans Unity consiste à vérifier si les rendus de l'objet sont visibles. Si ce n'est pas le cas, cela signifie que l'objet est complètement hors caméra et donc hors écran.
Allons chercher les rendus sur Début()
et faire une fonction utilitaire pour les vérifier:
Renderer [] rendus; void Start () renderers = GetComponentsInChildren (); bool CheckRenderers () foreach (rendu var dans les rendus) // Si au moins un rendu est visible, renvoie true si (renderer.isVisible) return true; // Sinon, l'objet est invisible return false;
Nous pouvons maintenant dire si notre objet est sorti de l'écran, mais nous avons encore besoin de savoir où il s'est éteint, puis téléporté vers le côté opposé. Pour ce faire, nous pouvons examiner les axes séparément. Par exemple, si la position x de notre navire est en dehors des limites de l'écran, cela signifie qu'il s'est éteint à gauche ou à droite.
Le moyen le plus simple de vérifier cela consiste à convertir d'abord la position mondiale du navire en position d'affichage, puis à effectuer la vérification. De cette façon, cela fonctionnera que vous utilisiez une caméra orthographique ou en perspective.
var cam = Camera.main; var viewportPosition = cam.WorldToViewportPoint (transform.position);
Pour clarifier les choses, laissez-moi vous expliquer coordonnées de la fenêtre. L'espace de visualisation est relatif à la caméra. Les coordonnées vont de 0
à 1
pour tout ce qui est à l'écran, ce qui signifie:
x = 0
est la coordonnée du bord gauche de l'écran.x = 1
est la coordonnée du bord droit de l'écran.De même,
y = 0
est la coordonnée du bord inférieur de l'écran.y = 1
est la coordonnée du bord supérieur de l'écran.Cela signifie que, si un objet est hors écran, il aura soit une coordonnée négative (inférieure à 0), soit une coordonnée supérieure à 1..
Depuis la position de notre caméra est à x = 0, y = 0
, la scène est aménagée comme un miroir. Tout à droite a des coordonnées x positives; tout à gauche, négatif. Tout dans la moitié supérieure a des coordonnées y positives; tout dans la moitié inférieure, négatif. Donc, afin de positionner notre objet sur le côté opposé de l'écran, nous inversons simplement sa position le long de l'axe approprié. Par exemple:
Notez que nous transformons le navire transformer position, pas sa fenêtre d'affichage position.
Dans le code, cela ressemble à ceci:
var newPosition = transform.position; if (viewportPosition.x> 1 || viewportPosition.x < 0) newPosition.y = -newPosition.y; if (viewportPosition.y > 1 || viewportPosition.y < 0) newPosition.y = -newPosition.y; transform.position = newPosition;
Si vous exécutez le projet maintenant, cela fonctionnera la plupart du temps. Mais parfois, l'objet peut ne pas envelopper. Cela se produit parce que notre objet échange constamment des positions hors écran, au lieu d'une seule fois. Nous pouvons éviter cela en ajoutant quelques variables de contrôle:
bool isWrappingX = false; bool isWrappingY = false;
Tout devrait fonctionner parfaitement maintenant, et le code de wrapping final devrait ressembler à ceci:
void ScreenWrap () var isVisible = CheckRenderers (); if (isVisible) isWrappingX = false; isWrappingY = false; revenir; if (isWrappingX && isWrappingY) return; var cam = Camera.main; var viewportPosition = cam.WorldToViewportPoint (transform.position); var newPosition = transform.position; if (! isWrappingX && (viewportPosition.x> 1 || viewportPosition.x < 0)) newPosition.x = -newPosition.x; isWrappingX = true; if (!isWrappingY && (viewportPosition.y > 1 || viewportPosition.y < 0)) newPosition.y = -newPosition.y; isWrappingY = true; transform.position = newPosition;
Le simple emballage fonctionne bien, mais il pourrait sembler meilleur. Au lieu que l’objet disparaisse de l’écran avant d’enrouler, vous pouvez obtenir un emballage parfait, comme dans l’image ci-dessous:
Le moyen le plus simple de le faire est de tricher un peu et d’avoir plusieurs navires sur les lieux. De cette façon, nous allons créer l'illusion d'un seul navire qui s'enroule. Nous allons avoir besoin de huit navires supplémentaires (je vais les appeler des fantômes): un pour chaque bord et un pour chaque coin de l'écran.
Nous voulons que ces navires fantômes ne soient visibles que lorsque le joueur se trouve à la limite. Pour ce faire, nous devons les positionner à certaines distances du navire principal:
Nous devons d'abord rechercher la taille de l'écran pour pouvoir positionner nos navires fantômes. Le fait est que nous avons besoin de la taille de l’écran en coordonnées mondiales par rapport au navire du joueur. Cela n'a pas vraiment d'importance si nous utilisons une caméra orthographique, mais avec une vue en perspective, il est très important que les navires fantômes soient sur la même coordonnée z que le navire principal..
Donc, pour faire tout cela à la fourchette, nous allons transformer les coordonnées de la fenêtre d'affichage des coins d'écran en haut à droite et en bas à gauche en coordonnées mondiales qui se trouvent sur le même axe z que le navire principal. Nous utilisons ensuite ces coordonnées pour calculer la largeur et la hauteur de l'écran en unités mondiales par rapport à la position de notre navire..
Déclarer largeur d'écran
et screenHeight
en tant que variables de classe et ajouter ceci à Début()
:
var cam = Camera.main; var screenBottomLeft = cam.ViewportToWorldPoint (nouveau vecteur3 (0, 0, transform.position.z)); var screenTopRight = cam.ViewportToWorldPoint (nouveau vecteur3 (1, 1, transform.position.z)); screenWidth = screenTopRight.x - screenBottomLeft.x; screenHeight = screenTopRight.y - screenBottomLeft.y;
Maintenant que nous pouvons les positionner correctement, faisons apparaître les vaisseaux fantômes. Nous allons utiliser un tableau pour les stocker:
Transformer [] fantômes = nouveau Transformer [8];
Et créons une fonction qui fera le frai. Je vais cloner le vaisseau principal pour créer les fantômes, puis je retirerai le ScreenWrapBehaviour
d'eux. Le navire principal est le seul qui devrait avoir ScreenWrapBehaviour
, car il peut avoir un contrôle total sur les fantômes et nous ne voulons pas que les fantômes engendrent leurs propres fantômes. Vous pouvez également avoir un préfabriqué distinct pour les navires fantômes et l'instancier. c'est la voie à suivre si vous voulez que les fantômes aient un comportement spécial.
vide CreateGhostShips () pour (int i = 0; i < 8; i++) ghosts[i] = Instantiate(transform, Vector3.zero, Quaternion.identity) as Transform; DestroyImmediate(ghosts[i].GetComponent());
Nous positionnons ensuite les fantômes comme dans l'image ci-dessus:
void PositionGhostShips () // Toutes les positions fantômes seront relatives aux vaisseaux (this) transformés, // alors jouons avec cela. var ghostPosition = transform.position; // Nous positionnons les fantômes dans le sens des aiguilles d'une montre derrière les bords de l'écran. // Commençons par l'extrême droite. ghostPosition.x = transform.position.x + screenWidth; ghostPosition.y = transform.position.y; fantômes [0] .position = ghostPosition; // En bas à droite ghostPosition.x = transform.position.x + screenWidth; ghostPosition.y = transform.position.y - screenHeight; fantômes [1] .position = ghostPosition; // Bottom ghostPosition.x = transform.position.x; ghostPosition.y = transform.position.y - screenHeight; fantômes [2] .position = ghostPosition; // ghostPosition.x en bas à gauche = transform.position.x - screenWidth; ghostPosition.y = transform.position.y - screenHeight; fantômes [3] .position = ghostPosition; // left ghostPosition.x = transform.position.x - screenWidth; ghostPosition.y = transform.position.y; fantômes [4] .position = ghostPosition; // ghostPosition.x en haut à gauche = transform.position.x - screenWidth; ghostPosition.y = transform.position.y + screenHeight; fantômes [5] .position = ghostPosition; // Top ghostPosition.x = transform.position.x; ghostPosition.y = transform.position.y + screenHeight; fantômes [6] .position = ghostPosition; // ghostPosition.x en haut à droite = transform.position.x + screenWidth; ghostPosition.y = transform.position.y + screenHeight; fantômes [7] .position = ghostPosition; // Tous les navires fantômes doivent avoir la même rotation que le navire principal pour (int i = 0; i < 8; i++) ghosts[i].rotation = transform.rotation;
Exécutez votre projet et essayez-le. Si vous vérifiez la vue de la scène, vous verrez que tous les navires fantômes se déplacent avec le navire principal et tournent quand il tourne. Nous n'avons pas explicitement codé cela, mais cela fonctionne toujours. Avez-vous une idée pourquoi?
Les navires fantômes sont des clones du navire principal sans le ScreenWrappingBehaviour
. Ils doivent toujours avoir le comportement de mouvement séparé et, puisqu'ils reçoivent tous la même entrée, ils bougent tous de la même manière. Si vous voulez créer les fantômes à partir d'un préfabriqué, n'oubliez pas d'inclure un composant de mouvement ou un autre script qui synchronisera leur mouvement avec celui du vaisseau principal..
Tout semble bien fonctionner maintenant, non? Enfin presque. Si vous continuez dans une direction, la première fois que cela se déroulera, tout se passera bien, mais une fois que vous atteindrez de nouveau le bord, il n'y aura plus de navire de l'autre côté. Cela a du sens, puisque nous ne faisons aucune téléportation cette fois-ci. Corrigeons-le.
Une fois que le navire principal a quitté le bord, un navire fantôme apparaît à l'écran. Nous devons échanger leurs positions, puis repositionner les navires fantômes autour du navire principal. Nous conservons déjà une série de fantômes, il nous suffit de déterminer lequel d’entre eux est à l’écran. Ensuite, nous faisons l’échange et le repositionnement. Dans du code:
void SwapShips () foreach (fantôme var dans les fantômes) if (ghost.position.x < screenWidth && ghost.position.x > -screenWidth && ghost.position.y < screenHeight && ghost.position.y > -screenHeight) transform.position = ghost.position; Pause; PositionGhostShips ();
Essayez-le maintenant, et tout devrait fonctionner parfaitement.
Vous avez maintenant un composant de wrapping de travail. Si cela vous suffit, cela dépend du jeu que vous faites et de ce que vous essayez d'atteindre..
L'emballage simple est assez simple à utiliser: attachez-le simplement à un objet et vous n'avez pas à vous soucier de son comportement. D'autre part, vous devez être un peu prudent si vous utilisez un wrapping avancé. Imaginez une situation où une balle ou un astéroïde frappe un vaisseau fantôme: vous allez devoir propager des événements de collision sur le vaisseau principal ou sur un objet de contrôleur externe..
Vous pouvez également souhaiter que vos objets de jeu ne suivent qu'un seul axe. Nous effectuons déjà des vérifications séparées pour chaque axe. Il suffit donc d’ajouter quelques booléens au code..
Une autre chose intéressante à considérer: et si vous vouliez que la caméra bouge un peu au lieu d’être fixée dans l’espace? Peut-être que vous voulez avoir une arène plus grande que l'écran. Dans ce cas, vous pouvez toujours utiliser le même script de wrapping. Vous n'avez besoin que d'un comportement distinct qui contrôle le mouvement de la caméra. Puisque notre code est basé sur la position de la fenêtre, la position de la caméra dans le jeu n'a pas vraiment d'importance..
Vous avez probablement déjà quelques idées à vous. Alors allez-y, essayez-les et faites des jeux!