Bekijk de onderstaande demo en laten we aan de slag gaan!
Klik op de demo om hem scherp te stellen en gebruik vervolgens de pijltjestoetsen om het schip te verplaatsen.Zoals u kunt zien, zijn er twee manieren om dit te doen. De eerste is makkelijker om je hoofd rond te wikkelen. De tweede oplossing is niet veel ingewikkelder, maar vereist een aantal out-of-the-box-denkwijzen. We gaan ze allebei behandelen.
Laten we eerst de scène opzetten. Start Unity op, start een nieuw project en stel de camerapositie in x = 0
, y = 0
(z
kan zijn wat je wilt). U zult waarschijnlijk willen dat de camera zich in de orthografische modus bevindt; onze schermverpakking werkt in perspectiefmodus, maar het ziet er misschien niet uit zoals u dat wilt. Voel je vrij om te experimenteren.
Opmerking van de uitgever: Zie de opmerking van Clemens voor informatie over het maken van dit werk in de perspectiefmodus.
Voeg een object toe dat zich gaat omwikkelen en verplaatsen. U kunt de ShipMovementBehaviour
script van de demobron.
In mijn geval heb ik een eenvoudig ruimteschip (een kegel) met Asteroïden-achtige beweging. Zoals je op de afbeelding kunt zien, geef ik er de voorkeur aan om het gaas aan het hoofdobject te koppelen. Of je het nu zo doet of niet, of je nu een of meerdere mazen hebt, het maakt niet uit; we gaan een mooi schermverwerkingsscript maken dat in elk geval werkt.
Het basisidee achter schermverpakking is dit:
Dus het eerste wat we willen doen, is controleren of het object volledig buiten beeld is gegaan. Een eenvoudige manier om dit in Unity te doen, is door te controleren of de renderers van het object zichtbaar zijn. Als dit niet het geval is, betekent dit dat het object volledig buiten de camera en dus buiten het scherm valt.
Laten we de weergaven ophalen Begin()
en maak een functie om ze te controleren:
Renderer [] -weergaveprogramma's; void Start () renderers = GetComponentsInChildren (); bool CheckRenderers () foreach (var renderer in renderers) // Als er ten minste één render zichtbaar is, geeft u true als (renderer.isVisible) return true; // Anders is het object onzichtbare return false;
We kunnen nu zien of ons object van het scherm is gegaan, maar we moeten er nog wel achter komen waar het ging af en teleporteerde het naar de andere kant. Om dit te doen, kunnen we de assen afzonderlijk bekijken. Als de x-positie van ons schip buiten de schermgrenzen is, betekent dit dat deze naar links of naar rechts is gegaan.
De eenvoudigste manier om dat te controleren, is om eerst de wereldpositie van het schip in kijkpositie om te zetten en vervolgens de controle uit te voeren. Op deze manier werkt het of u een orthografische of een perspectiefcamera gebruikt.
var cam = Camera.main; var viewportPosition = cam.WorldToViewportPoint (transform.position);
Laat het me uitleggen om dingen duidelijker te maken coördinaten van de viewport. Zichtruimte is relatief ten opzichte van de camera. De coördinaten variëren van 0
naar 1
voor alles wat op het scherm staat, wat betekent:
x = 0
is de coördinaat van de linkerrand van het scherm.x = 1
is de coördinaat van de rechterrand van het scherm.evenzo,
y = 0
is de coördinaat van het onderste scherm.y = 1
is de coördinaat van het bovenste scherm.Dit betekent dat als een object buiten beeld is, het ofwel een negatieve coördinaat (minder dan 0) of een coördinaat groter dan 1 heeft.
Omdat de positie van onze camera is x = 0, y = 0
, het tafereel is aangelegd als een spiegel. Alles aan de rechterkant heeft positieve x-coördinaten; alles links, negatief. Alles in de bovenste helft heeft positieve y-coördinaten; alles in de onderste helft, negatief. Dus om ons object aan de andere kant van het scherm te plaatsen, keren we gewoon zijn positie langs de juiste as om. Bijvoorbeeld:
Merk op dat we het schip transformeren transformeren positie, niet zijn uitkijk postje positie.
In code ziet dat er zo uit:
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;
Als u het project nu uitvoert, werkt het meestal goed. Maar soms kan het object zich niet omwikkelen. Dit gebeurt omdat ons object voortdurend van positie wisselt terwijl het buiten het scherm is, in plaats van slechts één keer. We kunnen dit voorkomen door een aantal controlevariabelen toe te voegen:
bool isWrappingX = false; bool isWrappingY = false;
Alles zou nu perfect moeten werken, en de uiteindelijke schermverpakkingscode zou er als volgt uit moeten zien:
void ScreenWrap () var isVisible = CheckRenderers (); if (isVisible) isWrappingX = false; isWrappingY = false; terug te keren; if (isWrappingX && isWrappingY) ga terug; 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;
De eenvoudige verpakking werkt goed, maar het kan er beter uitzien. In plaats van dat het object van het scherm verdwijnt voordat het wordt omhult, kunt u een perfecte verpakking krijgen, zoals in de onderstaande afbeelding:
De eenvoudigste manier om dit te doen, is een beetje vals spelen en meerdere schepen ter plaatse hebben. Op deze manier creëren we de illusie van een enkel rondwikkelend schip. We hebben acht extra schepen nodig (ik ga ze bellen) spoken): één voor elke rand en één voor elke hoek van het scherm.
We willen dat deze spookschepen alleen zichtbaar zijn wanneer de speler op een rand belandt. Om dat te doen, moeten we ze op bepaalde afstanden van het hoofdschip plaatsen:
We moeten eerst de schermgrootte ophalen, zodat we onze spookschepen kunnen positioneren. Het punt is, we hebben de schermgrootte nodig in wereldcoördinaten ten opzichte van het spelersschip. Dat maakt niet echt uit of we een orthografische camera gebruiken, maar met perspectief is het erg belangrijk dat de spookschepen op dezelfde z-coördinaat staan als het hoofdschip.
Dus, om dit geheel te doen, gaan we de viewport coördinaten van de schermhoeken rechts boven en linksonder transformeren naar de wereldcoördinaten die op dezelfde z-as liggen als het hoofdschip. Vervolgens gebruiken we deze coördinaten om de schermbreedte en -hoogte in wereldeenheden te berekenen ten opzichte van de positie van ons schip.
Verklaren screenWidth
en screenHeight
als klassevariabelen en voeg dit toe aan Begin()
:
var cam = Camera.main; var screenBottomLeft = cam.ViewportToWorldPoint (new Vector3 (0, 0, transform.position.z)); var screenTopRight = cam.ViewportToWorldPoint (new Vector3 (1, 1, transform.position.z)); screenWidth = schermTopRight.x - screenBottomLeft.x; screenHeight = screenTopRight.y - screenBottomLeft.y;
Nu we ze correct kunnen positioneren, laten we de spookschepen spawnen. We zullen een array gebruiken om ze op te slaan:
Transform [] ghosts = new Transform [8];
En laten we een functie maken die de spawning zal doen. Ik zal het hoofdschip klonen om de geesten te maken, en dan zal ik het verwijderen ScreenWrapBehaviour
van hen. Het belangrijkste schip is de enige die zou moeten hebben ScreenWrapBehaviour
, omdat het de volledige controle over de geesten kan hebben en we niet willen dat de spoken hun eigen spoken spawnen. Je zou ook een apart prefab voor de spookschepen kunnen hebben en dat laten gebeuren; dit is de manier om te gaan als je wilt dat de geesten speciaal gedrag vertonen.
void CreateGhostShips () for (int i = 0; i < 8; i++) ghosts[i] = Instantiate(transform, Vector3.zero, Quaternion.identity) as Transform; DestroyImmediate(ghosts[i].GetComponent());
Vervolgens plaatsen we de geesten zoals in de bovenstaande afbeelding:
void PositionGhostShips () // Alle ghost-posities zullen relatief zijn ten opzichte van de (transformerende) schepen, // laten we daarmee dus een ster maken. var ghostPosition = transform.position; // We plaatsen de spoken met de klok mee achter de randen van het scherm. // Laten we beginnen met uiterst rechts. ghostPosition.x = transform.position.x + screenWidth; ghostPosition.y = transform.position.y; spoken [0] .position = ghostPosition; // Bottom-right ghostPosition.x = transform.position.x + screenWidth; ghostPosition.y = transform.position.y - screenHeight; spoken [1] .position = ghostPosition; // Bottom ghostPosition.x = transform.position.x; ghostPosition.y = transform.position.y - screenHeight; spoken [2] .position = ghostPosition; // GhostPortiment linksonder x = transform.position.x - screenWidth; ghostPosition.y = transform.position.y - screenHeight; spoken [3] .position = ghostPosition; // Left ghostPosition.x = transform.position.x - screenWidth; ghostPosition.y = transform.position.y; spoken [4] .position = ghostPosition; // Top-left ghostPosition.x = transform.position.x - screenWidth; ghostPosition.y = transform.position.y + screenHeight; spoken [5] .position = ghostPosition; // Top ghostPosition.x = transform.position.x; ghostPosition.y = transform.position.y + screenHeight; spoken [6] .position = ghostPosition; // Top-right ghostPosition.x = transform.position.x + screenWidth; ghostPosition.y = transform.position.y + screenHeight; spoken [7] .position = ghostPosition; // Alle ghostschepen moeten dezelfde rotatie hebben als het hoofdschip voor (int i = 0; i < 8; i++) ghosts[i].rotation = transform.rotation;
Run je project en probeer het uit. Als je de scènemodus bekijkt, zie je dat alle spookschepen met het hoofdschip meebewegen en draaien als het draait. We hebben dit niet expliciet gecodeerd, maar het werkt nog steeds. Heb je een idee waarom??
Geestenschepen zijn klonen van het hoofdschip zonder de ScreenWrappingBehaviour
. Ze moeten nog steeds het afzonderlijke bewegingsgedrag hebben en omdat ze allemaal dezelfde invoer ontvangen, bewegen ze allemaal hetzelfde. Als je de spoken uit een prefab wilt laten spawnen, vergeet dan niet om een bewegingscomponent of een ander script op te nemen dat hun beweging synchroniseert met het hoofdschip..
Alles lijkt nu goed te werken, toch? Nou bijna. Als je in één richting blijft gaan, zal het de eerste keer dat het inpakt prima werken, maar zodra je de rand weer bereikt, zal er geen schip aan de andere kant zijn. Logisch, want we doen deze keer geen teleportering. Laten we het oplossen.
Zodra het hoofdschip van de rand gaat, zal een spookschip op het scherm verschijnen. We moeten hun posities ruilen en de spookschepen rond het hoofdschip verplaatsen. We houden al een hele reeks spookbeelden, we moeten alleen bepalen welke ervan op het scherm wordt weergegeven. Dan doen we het wisselen en herpositioneren. In code:
void SwapShips () foreach (var ghost in ghosts) ifeach (ghost.position.x < screenWidth && ghost.position.x > -screenWidth && ghost.position.y < screenHeight && ghost.position.y > -screenHeight) transform.position = ghost.position; breken; PositionGhostShips ();
Probeer het nu, en alles zou perfect moeten werken.
U hebt nu een werkende schermterugloopcomponent. Of dit genoeg voor u is, hangt af van het spel dat u maakt en wat u probeert te bereiken.
Eenvoudig inpakken is vrij eenvoudig te gebruiken: sluit het gewoon aan op een object en u hoeft zich geen zorgen te maken over zijn gedrag. Aan de andere kant moet je een beetje voorzichtig zijn als je geavanceerde verpakking gebruikt. Stel je een situatie voor waarbij een kogel of een asteroïde een spookschip raakt: je moet botsingsgebeurtenissen naar het hoofdschip of een extern controllerobject doorgeven.
Je wilt misschien ook dat je game-objecten langs slechts één as wikkelen. We doen al afzonderlijke controles voor elke as, dus het is gewoon een kwestie van een paar Booleans toevoegen aan de code.
Nog een interessant ding om te overwegen: wat als je wilde dat de camera een beetje zou bewegen in plaats van in de ruimte te worden gerepareerd? Misschien wil je een arena die groter is dan het scherm. In dat geval zou u nog steeds hetzelfde wikkelscript kunnen gebruiken. Je hebt alleen een apart gedrag nodig dat de bewegingen van de camera regelt. Aangezien onze code is gebaseerd op de positie van de viewport, doet de positie van de camera in het spel er niet echt toe.
Waarschijnlijk heb je nu wel wat eigen ideeën. Dus ga je gang, probeer ze uit en maak wat spellen!