Jak radzisz sobie z różnicami proporcji obrazu w Unity 2D?

67

Otrzymałem wiele odpowiedzi na to pytanie, ale wszystkie są ogólne i ogólnie niezbyt przydatne. Żaden z samouczków nie mówi o proporcjach obrazu i postępowaniu z urządzeniami mobilnymi, a jest na to zillion sposobów, wszystkie wydają się mieć wady i błędy.

Naprawdę chciałbym wiedzieć, jakie udane gry wykorzystują różne współczynniki proporcji na iOS i Androidzie bez tworzenia zillionów aktywów o różnej wielkości.

Ściśle mówiąc, jestem mobilny, a nie stacjonarny, szczególnie z Unity i nie dbam o interfejs użytkownika, zależy mi tylko na kanwie rozgrywki.

Mam na myśli problemy, gdy są kluczowe rzeczy, które muszą znajdować się w niektórych miejscach i nie mogą spaść z ekranu. Stosowanie czarnych pasków u góry lub u dołu jest obecnie niedopuszczalne.

Michael
źródło
3
To pytanie jest bardzo ogólne, ponieważ właściwa droga zależy od prawie wszystkiego. Czego próbowałeś? Dlaczego to nie zadziałało?
Anko
3
Próbowałem różnych rzeczy, próbowałem dopasować rozmiar kamery orto, próbowałem dołączyć wszystkie duszki do listy i skalować je według różnej proporcji, ustawiając rozmiar orto na ekran. Wysokość / 2/100, wiele innych pomysły Niektóre działają, ale wszystkie mają problemy. Wiem, że różne gry radzą sobie z tym inaczej, ale nigdzie nie ma dyskusji na ten temat i nie jest to tak łatwe, jak „pozwalanie jedności sobie z tym poradzić”, jak wielu twierdzi.
Michael
1
Dlaczego więc nie działały? Jak wyglądałoby dobre rozwiązanie? (Nawiasem mówiąc, możesz edytować pytanie, aby je również wyjaśnić.)
Anko
7
Niektóre zniekształcały obrazy, inne nie były w porządku. Wiele różnych problemów, ale 65% gier opracowanych za pomocą Unity jest 2D i sprawiło, że działało. Chcę tylko wiedzieć, z czego korzystają ludzie i nie muszę wymyślać koła na nowo. Nikt nie mówi o tym i nie ma przewodników ani dokumentów na temat tego, jak sobie z tym poradzić. Jednak nie można za daleko sięgnąć w projekcie mobilnym bez posiadania odpowiedniego systemu.
Michael
1
„Mam na myśli problemy, gdy istnieją kluczowe rzeczy, które muszą znajdować się w niektórych miejscach i nie mogą spaść z ekranu. Obecnie stosowanie czarnych pasów na górze lub na dole jest niedopuszczalne”. Gwarancja elementów, które nie spadają z ekranu, zero zniekształceń, ale brak boksu na litery / kolumny (czarne paski i tym podobne). Te wymagania są nie do pogodzenia. Ostatnie wymaganie jest prawdopodobnie najmniej ważne lub można je ukryć, wypychając płótno poza to, co musi znajdować się na ekranie. Większość gier, które widziałem z tak surowymi wymaganiami, będzie miała ozdobne ramki / ramki.
Jibb Smart

Odpowiedzi:

36

To, co chcesz, to ograniczyć rzutnię kamery do portretu lub krajobrazu (w zależności od potrzeb), obliczając camera.orthographicSizewłaściwość, dzięki czemu możesz zbudować scenę 2D bez względu na proporcje i rozdzielczość:

// Attach this script on your main ortohgraphic camera:

/* The MIT License (MIT)

Copyright (c) 2014, Marcel Căşvan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */

using System;
using System.Collections;
using UnityEngine;

[ExecuteInEditMode]
[RequireComponent (typeof (Camera))]
public class ViewportHandler : MonoBehaviour
{
    #region FIELDS
    public Color wireColor = Color.white;
    public float UnitsSize = 1; // size of your scene in unity units
    public Constraint constraint = Constraint.Portrait;
    public static ViewportHandler Instance;
    public new Camera camera;

    private float _width;
    private float _height;
    //*** bottom screen
    private Vector3 _bl;
    private Vector3 _bc;
    private Vector3 _br;
    //*** middle screen
    private Vector3 _ml;
    private Vector3 _mc;
    private Vector3 _mr;
    //*** top screen
    private Vector3 _tl;
    private Vector3 _tc;
    private Vector3 _tr;
    #endregion

    #region PROPERTIES
    public float Width {
        get {
            return _width;
        }
    }
    public float Height {
        get {
            return _height;
        }
    }

    // helper points:
    public Vector3 BottomLeft {
        get {
            return _bl;
        }
    }
    public Vector3 BottomCenter {
        get {
            return _bc;
        }
    }
    public Vector3 BottomRight {
        get {
            return _br;
        }
    }
    public Vector3 MiddleLeft {
        get {
            return _ml;
        }
    }
    public Vector3 MiddleCenter {
        get {
            return _mc;
        }
    }
    public Vector3 MiddleRight {
        get {
            return _mr;
        }
    }
    public Vector3 TopLeft {
        get {
            return _tl;
        }
    }
    public Vector3 TopCenter {
        get {
            return _tc;
        }
    }
    public Vector3 TopRight {
        get {
            return _tr;
        }
    }
    #endregion

    #region METHODS
    private void Awake()
    {
        camera = GetComponent<Camera>();
        Instance = this;
        ComputeResolution();
    }

    private void ComputeResolution()
    {
        float leftX, rightX, topY, bottomY;

        if(constraint == Constraint.Landscape){
            camera.orthographicSize = 1f / camera.aspect * UnitsSize / 2f;    
        }else{
            camera.orthographicSize = UnitsSize / 2f;
        }

        _height = 2f * camera.orthographicSize;
        _width = _height * camera.aspect;

        float cameraX, cameraY;
        cameraX = camera.transform.position.x;
        cameraY = camera.transform.position.y;

        leftX = cameraX - _width / 2;
        rightX = cameraX + _width / 2;
        topY = cameraY + _height / 2;
        bottomY = cameraY - _height / 2;

        //*** bottom
        _bl = new Vector3(leftX, bottomY, 0);
        _bc = new Vector3(cameraX, bottomY, 0);
        _br = new Vector3(rightX, bottomY, 0);
        //*** middle
        _ml = new Vector3(leftX, cameraY, 0);
        _mc = new Vector3(cameraX, cameraY, 0);
        _mr = new Vector3(rightX, cameraY, 0);
        //*** top
        _tl = new Vector3(leftX, topY, 0);
        _tc = new Vector3(cameraX, topY , 0);
        _tr = new Vector3(rightX, topY, 0);           
    }

    private void Update()
    {
        #if UNITY_EDITOR
        ComputeResolution();
        #endif
    }

    void OnDrawGizmos() {
        Gizmos.color = wireColor;

        Matrix4x4 temp = Gizmos.matrix;
        Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
        if (camera.orthographic) {
            float spread = camera.farClipPlane - camera.nearClipPlane;
            float center = (camera.farClipPlane + camera.nearClipPlane)*0.5f;
            Gizmos.DrawWireCube(new Vector3(0,0,center), new Vector3(camera.orthographicSize*2*camera.aspect, camera.orthographicSize*2, spread));
        } else {
            Gizmos.DrawFrustum(Vector3.zero, camera.fieldOfView, camera.farClipPlane, camera.nearClipPlane, camera.aspect);
        }
        Gizmos.matrix = temp;
    }
    #endregion

    public enum Constraint { Landscape, Portrait }
}

Jeśli potrzebujesz więcej informacji na ten temat, zapytaj, a ja odpowiem. ;) Pozdrawiam i na zdrowie.

AKTUALIZACJA: Użyj skryptu zakotwiczenia obiektów Eliota Lasha razem z tym, aby w razie potrzeby umieścić obiekty w kluczowych pozycjach na ekranie (względem rogów / ramek ekranu). Jeśli tak, zmień nazwę „CameraFit” na „ViewportHandler”.

Podgląd symulujący różne ekrany o proporcjach: wprowadź opis zdjęcia tutaj

androidu
źródło
4
@Eliot Dodano licencję MIT powyżej. Powodzenia w twoich projektach!
androidu
7
Fantastycznie, dziękuję! Jedna dobra kolej zasługuje na drugą, więc oto komponent o otwartym kodzie źródłowym, który właśnie napisałem do prostego zakotwiczenia GameObjects do punktów pomocniczych określonych przez twój skrypt: gist.github.com/fadookie/256947788c364400abe1
Eliot
1
MarcelCăşvan Bardzo fajny skrypt! Dodałem wyliczenie do wyboru, czy chcesz zdefiniować rozmiar jednostki dla potrait dla krajobrazu (wysokość / szerokość). Musiałem tylko dodać kilka wierszy do skryptu, a ja używam GetComponent<Camera>().orthographicSize = UnitSize / 2f;do potrait / definiowania jednostek wysokości
am_
2
@ MarcelCăşvan świetne rzeczy! Dzięki. Widzę, że zmienne deviceWidth i deviceHeight z ComputeFunction () nie są używane. Być może rozważ ich usunięcie.
user2313267,
1
CameraAnchor zgłasza błąd: „CameraFit nie jest zdefiniowany w tym kontekście” - W przypadku, gdy ktoś inny znajdzie tę odpowiedź później, wydaje się, że „CameraFit” właśnie zmienił nazwę na „ViewportHandler”, ponieważ została pierwotnie opublikowana. Jeśli zmienisz nazwę klasy z ViewportHandler z powrotem na CameraFit.
Lenny,
5

Zwykle nie potrzebujesz różnych rozmiarów zasobów - importowane tekstury i duszki z automatycznie generowanymi mapami mip będą wyglądać ładnie, gdy będą renderowane w dowolnym rozmiarze mniejszym lub równym oryginalnemu rozmiarowi piksela obrazu.

Układ sceny jest wyzwaniem. Jedno dobre podejście jest następujące (i do twojej wiadomości używam kamery 3D, patrząc na zawartość 2D ustawioną na z = 0):

  1. Arbitralnie decyduj o minimalnym „logicznym” rozmiarze wyświetlacza w pikselach lub kafelkach. Nie musi to odpowiadać żadnej rzeczywistej rozdzielczości, ale powinno odzwierciedlać najwęższe / najkrótsze proporcje, które chcesz obsługiwać. Na przykład w przypadku gry krajobrazowej nie wybrałbym 480x320, ponieważ jest to szerszy współczynnik kształtu niż iPad. Mogę więc wybrać 1024x768 - a nawet 480x360, co daje mi oryginalny układ współrzędnych wielkości iPhone'a do pracy i taki sam współczynnik kształtu jak każdy iPad (w tym iPad Air 2 itp.). Pamiętaj też, że równie łatwo możesz pracować ze współrzędnymi kafelkowymi, a nie ze współrzędnymi pikselowymi - na przykład 15 x 11,25.
  2. Zaprogramuj logikę gry tak, aby wszystko, co ważne, było (lub mogło być) ustawione w obrębie minimalnego rozmiaru wyświetlacza, ale bądź przygotowany na wypełnienie dodatkowego miejsca po bokach dodatkową zawartością, nawet jeśli jest to tylko dekoracyjny wypełniacz.
  3. Określ, ile potrzebujesz skalować zawartość, aby szerokość lub wysokość odpowiadały wartości minimalnej, a druga oś była większa lub równa potrzebnemu minimum. Aby wykonać to „skalowanie w celu dopasowania”, podziel rozmiar piksela ekranu przez minimalny rozmiar ekranu i ustaw mniejszą z wynikowych wartości skali jako ogólną skalę widoku.
  4. Użyj skali widoku, aby obliczyć efektywny (rzeczywisty) rozmiar wyświetlacza dla celów logiki gry.
  5. Właściwie skaluj zawartość, przesuwając kamerę wzdłuż osi Z.

W formie kodu:

  // Adjust the camera to show world position 'centeredAt' - (0,0,0) or other - with
  // the display being at least 480 units wide and 360 units high.

  Vector3 minimumDisplaySize = new Vector3( 480, 360, 0 );

  float pixelsWide = camera.pixelWidth;
  float pixelsHigh = camera.pixelHeight;

  // Calculate the per-axis scaling factor necessary to fill the view with
  // the desired minimum size (in arbitrary units).
  float scaleX = pixelsWide / minimumDisplaySize.x;
  float scaleY = pixelsHigh / minimumDisplaySize.y;

  // Select the smaller of the two scale factors to use.
  // The corresponding axis will have the exact size specified and the other 
  // will be *at least* the required size and probably larger.
  float scale = (scaleX < scaleY) ? scaleX : scaleY;

  Vector3 displaySize = new Vector3( pixelsWide/scale, pixelsHigh/scale, 0 );

  // Use some magic code to get the required distance 'z' from the camera to the content to display
  // at the correct size.
  float z = displaySize.y /
            (2 * Mathf.Tan((float)camera.fieldOfView / 2 * Mathf.Deg2Rad));

  // Set the camera back 'z' from the content.  This assumes that the camera
  // is already oriented towards the content.
  camera.transform.position = centeredAt + new Vector3(0,0,-z);

  // The display is showing the region between coordinates 
  // "centeredAt - displaySize/2" and "centeredAt + displaySize/2".

  // After running this code with minimumDisplaySize 480x360, displaySize will
  // have the following values on different devices (and content will be full-screen
  // on all of them):
  //    iPad Air 2 - 480x360
  //    iPhone 1 - 540x360
  //    iPhone 5 - 639x360
  //    Nexus 6 - 640x360

  // As another example, after running this code with minimumDisplaySize 15x11
  // (tile dimensions for a tile-based game), displaySize will end up with the following 
  // actual tile dimensions on different devices (every device will have a display
  // 11 tiles high and 15+ tiles wide):
  //    iPad Air 2 - 14.667x11
  //    iPhone 1 - 16.5x11
  //    iPhone 5 - 19.525x11
  //    Nexus 6 - 19.556x11
AbePralle
źródło
2

Jeśli zaczniesz korzystać z pasków, jest to dość proste do wdrożenia (piszę to, chociaż PO stwierdził, że jest to nie do przyjęcia, ponieważ ma tę zaletę, że nie jest tak źle na telefonie komórkowym i jest to proste rozwiązanie, które nie wymaga żadnego kodu)

Camera.orthographicSizeto zmienna w kamerze orto (używanej przez większość gier 2D), która pasuje do zmierzonej liczby jednostek gry pionowo na ekranie ( podzielona przez 2 ) ( źródło ). Dlatego wybierz proporcje, które pasują do większości urządzeń (wybrałem 16: 9, ponieważ większość ekranów, które badałem to 16: 9, 16:10, 3: 2) i dodaj maskę, która nakłada się na ten współczynnik.

Przykład :

W mojej grze (nie wymienionej tutaj, ponieważ nie jest to reklama, w razie potrzeby mogę zapytać w komentarzach) używamy trybu portretowego. Aby zrobić ładny prosty 16: 9, zrobiłem moją kamerę Ortho w rozmiarze 16. Oznacza to, że kamera dostosuje 32 jednostki gry wysokości (w moim przypadku od 16 do -16) do pionu ekranu urządzenia.

Następnie umieściłem czarne maski w grze pomiędzy -9 a +9. Voila, ekran gry wygląda dokładnie tak samo na wszystkich urządzeniach, a nieco bardziej chudy na urządzeniach, które są nieco szersze. Nie miałem absolutnie żadnej negatywnej opinii na temat masek. Aby zrobić krajobraz, po prostu odwróć te wartości, a następnie stworzysz kamerę o rozmiarze 9. Zmień wartości, aby dopasować to, co zdecydowałeś, do skali jednostki gry.

Jedyne miejsce, w którym zaobserwowaliśmy znaczący czarny pasek, to iPad na 3: 2. Nawet wtedy nie miałem żadnych skarg.

McAden
źródło
1

Robię to w grze, nad którą obecnie pracuję. Mam obraz tła o wymiarach 1140 x 720. Najważniejsze bity (te, które nigdy nie powinny zostać przycięte) znajdują się w środkowym obszarze 960 x 640. Uruchamiam ten kod w funkcji uruchamiania mojego aparatu:

    float aspect = (float)Screen.width / (float)Screen.height;

    if (aspect < 1.5f)
        Camera.main.orthographicSize = 3.6f;
    else
        Camera.main.orthographicSize = 3.2f;

    float vertRatio = Screen.height / 320.0f;
    fontSize = (int)(12 * vertRatio);

Określam również rozmiary inne niż rozmiar czcionki dla przycisków i tym podobne. Działa dobrze na każdym testowanym współczynniku proporcji. Minęło trochę czasu, odkąd go skonfigurowałem, więc być może brakuje mi kroku. Daj mi znać, jeśli to nie zadziała zgodnie z oczekiwaniami, a zobaczę, czy coś pominąłem.

Jan
źródło
1

@ Odpowiedź Marcela i kod są świetne i pomogły mi zrozumieć, co się dzieje. To ostateczna odpowiedź. Pomyślałem, że ktoś może również uznać za użyteczne to, co zrobiłem w moim konkretnym przypadku: ponieważ chciałem czegoś naprawdę bardzo prostego, jednego duszka zawsze na ekranie, wymyśliłem kilka wierszy:

public class CameraFit : MonoBehaviour {

    public SpriteRenderer spriteToFitTo;

    void Start () { // change to Update to test by resizing the Unity editor window
        var bounds = spriteToFitTo.bounds.extents;
        var height = bounds.x / camera.aspect;
        if (height < bounds.y)
            height = bounds.y;
        camera.orthographicSize = height;
    }
}

Dodałem to do kamery i przeciągnąłem duszka (to moje tło) do jedynej właściwości skryptu. Jeśli nie chcesz żadnych czarnych pasków (poziomych lub pionowych), możesz umieścić za nimi większe tło ...

maltalef
źródło
0

Istnieje kilka sposobów rozwiązania tego problemu i nie ma idealnego rozwiązania (przynajmniej jeszcze go nie znalazłem), a rodzaj rozwiązania, które wybierzesz, będzie w dużej mierze zależeć od rodzaju gry, w której grasz rozwijanie się.

Niezależnie od tego, co robisz, powinieneś zacząć od wybrania najniższej możliwej rozdzielczości, którą chcesz wesprzeć, i zbuduj duszki do tej rozdzielczości. Więc jeśli jesteś zainteresowany programowaniem na iOS, według http://www.iosres.com/ najniższa rozdzielczość urządzenia z iOS to 480x320.

Stamtąd możesz rozpocząć skalowanie duszków, aby spełnić wyższe rozdzielczości. Zastrzeżeniem tego jest to, że w końcu zaczniesz zauważać, że duszki zaczynają się rozmywać w dalszej skali, w którym to przypadku możesz przełączyć się na inny zestaw duszek zbudowany dla wyższych rozdzielczości.

Możesz też całkowicie zignorować skalowanie i po prostu zdecydować o wyświetleniu większej ilości ekranu gry dla wyższych rozdzielczości (uważam, że tak właśnie robi Terraria). Jednak w przypadku wielu gier nie jest to właściwe; na przykład gry konkurencyjne.

Stosowanie czarnych pasków u góry lub u dołu jest obecnie niedopuszczalne.

Czy to jest Robi to wiele gier, które chcą wymusić proporcje 4: 3. Ponieważ używasz Unity, możesz w tym celu skorzystać ze skryptu AspectRatioEnforcer .

Bednarz
źródło
Kieruję reklamy na urządzenia mobilne, więc nie mogę zmienić proporcji ani rozdzielczości. Korzystanie z zasobów o niskiej rozdzielczości nie jest akceptowalnym rozwiązaniem. Niektóre gry (zwłaszcza 3D) to nie problem, po prostu pokaż mniej więcej. Ale w grach takich jak Kingdom Rush, tower defense i innych takich jak Alex chcesz widzieć to samo bez względu na urządzenie. Jeśli rzeczy nie pojawią się w odpowiednich lokalizacjach, gra nie będzie działać poprawnie.
Michael
0

Istnieje wiele sposobów rozwiązania tego problemu, zależy to od gry i tego, co będzie działać najlepiej. Na przykład w naszej grze Tyrant Unleashed po prostu stworzyliśmy szerokie mapy z nieistotnymi szczegółami po bokach, aby można było odcinać boki na węższych urządzeniach. Jednak inne gry mogą być lepsze z podejściem, w którym faktycznie przesuwasz przyciski lub coś, co lepiej pasuje do ekranu.

(Częścią naszego podejścia jest także utrzymywanie stałej wysokości na wszystkich urządzeniach, zmieniając jedynie szerokość. To z pewnością ułatwia nam życie, ale znowu może to, ale nie musi, być dobre dla twojej konkretnej gry. To nie ma znaczenia nasz styl artystyczny, jeśli obrazy są nieco skalowane na różnych ekranach, podczas gdy może to mieć znaczenie w przypadku sztuki pikselowej. Zasadniczo jest to podejście „pozwól Unity sobie z tym poradzić”)

jhocking
źródło
Czy zrobiłeś jakieś projekty, w których nie było takiej opcji? Prawie wszystkie gry w stylu tower defense, które widziałem i wiele innych stylów, możesz zobaczyć cały widok gry na urządzeniu bez przewijania, i jest to spójne na różnych urządzeniach. Na IOS możesz tworzyć zasoby i zamieniać je, ale staje się to niemożliwe (a tak naprawdę tylko duża PITA) na Androida.
Michael
Nie, nie mam. btw Właśnie dodałem trochę więcej szczegółów na temat rozmiaru ekranu
jhocking
0

Korzystam z następującego skryptu, który dodaje targetAspectparametr do kamery i dostosowuje go orthographicSizew stosunku do proporcji ekranu (więcej szczegółów w tym poście na blogu ):

using UnityEngine;
using System.Collections;

public class AspectRatioScript : MonoBehaviour {

    public float targetAspect;

    void Start () 
    {
        float windowAspect = (float)Screen.width / (float)Screen.height;
        float scaleHeight = windowAspect / targetAspect;
        Camera camera = GetComponent<Camera>();

        if (scaleHeight < 1.0f)
        {  
            camera.orthographicSize = camera.orthographicSize / scaleHeight;
        }
    }
}
sdabet
źródło
0

Moja metoda jest w większości podobna do rozwiązań podanych przez innych :) Postaram się szczegółowo wyjaśnić, jak podchodzę do uniezależnienia gry od rozmiaru ekranu.

Orientacja ekranu

W zależności od orientacji ekranu (pozioma lub pionowa) należy rozważyć, czy kamera będzie skalować ze stałą wysokością lub stałą szerokością. Najczęściej wybieram stałą szerokość dla gier zorientowanych na krajobraz i stałą wysokość dla gier zorientowanych na portret.

Skalowanie kamery

Jak omówiono, może to być stała wysokość lub stała szerokość.

Naprawiono wysokość : pionowy obszar gry zawsze będzie pasował do wysokości ekranu. Wraz ze zmianą proporcji ekranu po lewej i prawej stronie ekranu zostanie dodane dodatkowe miejsce. Aby to zaimplementować, nie trzeba niczego kodować, jest to domyślne zachowanie kamery unity.

Naprawiona szerokość : Poziomy obszar gry zawsze będzie pasował do szerokości ekranu. Dodatkowe miejsce zostanie dodane na górze i na dole wraz ze zmianą proporcji ekranu. Aby to zaimplementować, musisz napisać mały fragment kodu. Później upewnij się, że usunąłeś funkcję aktualizacji formularza kodu i wstawiłeś go w stan czuwania.

using UnityEngine;

[ExecuteInEditMode]
public class ScaleWidthCamera : MonoBehaviour {

    public int targetWidth = 640;
    public float pixelsToUnits = 100;

    void Update() {

        int height = Mathf.RoundToInt(targetWidth / (float)Screen.width * Screen.height);

        camera.orthographicSize = height / pixelsToUnits / 2;
    }
}

W edytorze możesz zmienić targetWidth, aby zdefiniować obszar kosmiczny, który chcesz wyświetlić. Ten kod jest wyjaśniony w poniższym filmie wraz z wieloma innymi ćwiczeniami dla gier 2D :)

Unite 2014 - Najlepsze praktyki 2D w jedności

Proporcje obrazu

Poniższy współczynnik proporcji, od najszerszego do najwęższego, obejmuje prawie wszystkie rozmiary ekranu dla Androida i iOS

  • 5: 4
  • 4: 3
  • 3: 2
  • 16:10
  • 16: 9

Zazwyczaj wszystkie te współczynniki kształtu ustawiam w podanej kolejności pod oknem gry, ponieważ jest to przydatne podczas testowania różnych rozmiarów ekranu :)

Obszar wydatkowy

Jest to obszar, który jest dodawany do ekranu po bokach lub u góry / u dołu, w zależności od wybranego skalowania kamery.

Dla stałej wysokości wszystkie elementy gry powinny najlepiej pasować do proporcji 16: 9, która jest najwęższa. Tło powinno się rozciągać, aż obejmie stosunek 5: 4. Dzięki temu Twoja gra nigdy nie ma czarnych pasów po bokach.

Dla stałej szerokości jest prawie taka sama, ale tutaj elementy powinny pasować w stosunku 5: 4, a BG powinien rozciągać się do 16: 9.

Miedza

Czasami nie możemy zastosować podejścia do obszaru zbywalnego, ponieważ musimy wykorzystać cały dostępny ekran do gry.

Weźmy na przykład grę portretową o stałej wysokości, łapiąc spadające z nieba monety. W tym musimy umożliwić graczowi poruszanie się w poziomie na dostępnej szerokości ekranu.

Dlatego potrzebujemy granic kamery pod względem współrzędnych świata, aby wiedzieć, gdzie dokładnie lewa, prawa, górna lub dolna część klipów kamery w pozycji na świecie.
Możemy również użyć tych granic do zakotwiczenia elementów gry lub interfejsu użytkownika do pożądanej strony kamery.

Za pomocą Camera.ViewportToWorldPoint możemy uzyskać granice. Przestrzeń widoku jest znormalizowana w stosunku do kamery. Lewy dolny róg kamery to (0,0); prawy górny róg to (1,1). Pozycja Z znajduje się w jednostkach światowych z kamery. Dla 2D / ortografii z nie ma znaczenia.

Vector3 leftBottom = camera.ViewportToWorldPoint(new Vector3(0, 0, camera.nearClipPlane));
Vector3 rightTop = camera.ViewportToWorldPoint(new Vector3(1, 1, camera.nearClipPlane));

float left = leftBottom.x;
float bottom = leftBottom.y;
float right = rightTop.x;
float top = rightTop.y;

Interfejs użytkownika

W przypadku interfejsu użytkownika możemy zastosować te same koncepcje, których użyliśmy do elementów gry. Po wprowadzeniu Unity5 UI i dostępności wtyczek takich jak NGUI nie będzie większego problemu :)

Hash Boja
źródło
0

Stworzyłem Unity Asset 'Resolution Magic 2D', aby rozwiązać ten bardzo problem (reklama: możesz go pobrać ze sklepu Unity Asset Store lub zobaczyć więcej szczegółów na grogansoft.com).

Sposób rozwiązania tego problemu był następujący ...

Zdefiniuj obszar ekranu, który musi być zawsze widoczny, niezależnie od proporcji / rozdzielczości, i użyj prostej transformacji prostokąta, aby „rozrysować” ten region. To jest twój idealny kształt ekranu. Za pomocą prostego algorytmu powiększam następnie kamerę, aż obszar zablokowany przez prostokąt będzie tak duży, jak to możliwe, a jednocześnie będzie w 100% widoczny dla kamery.

Zatem główny obszar gry zawsze zajmuje jak najwięcej ekranu. I tak długo, jak istnieje wystarczająca „dodatkowa” zawartość (np. Tło) poza zdefiniowanym wcześniej obszarem prostokąta, gracze, których ekran nie ma takiego samego współczynnika proporcji jak Twój „idealny” prostokąt, zobaczą dodatkową zawartość, w której czarne paski inaczej iść.

Mój zasób zawiera również logikę do umieszczania interfejsu użytkownika, ale jest to w większości przestarzałe ze względu na nowy system interfejsu użytkownika Unity.

Mój zasób zapewnia to po wyjęciu z pudełka przy minimalnej konfiguracji i działa naprawdę dobrze na wszystkich platformach.

Jeśli używasz tej techniki (lub mojego zasobu), po prostu upewnij się, że projektujesz grę tak, aby wokół miała „opcjonalną” przestrzeń, aby pomieścić ekrany szersze lub wyższe niż Twoje (w celu uniknięcia czarnych pasków).

Nie tworzę osobiście gier 3D, więc nie wiem, czy to zadziała w 3D (czy nawet jest konieczne).

Naprawdę trudno to wytłumaczyć bez zdjęć, więc proszę odwiedzić moją stronę internetową ( Resolution Magic 2D )

Pumpkin
źródło
0

Najlepszym rozwiązaniem jest dla mnie użycie twierdzenia o przecinających się liniach, aby nie było po bokach odcięcia ani zniekształcenia widoku gry. Oznacza to, że musisz cofnąć się do przodu lub do przodu w zależności od różnych współczynników kształtu.

jedność
źródło
-3

Stworzyłem rozszerzenie AssetStore, które pozwala na łatwiejsze przełączanie aspektów o nazwie AspectSwitcher . Zapewnia system umożliwiający łatwe określenie różnych właściwości dla różnych aspektów. Zasadniczo są dwie metody, których większość ludzi używa do zmiany aspektów. Jednym z nich jest zapewnienie różnych obiektów gry dla każdego aspektu. Drugim jest stworzenie niestandardowego kodu, który modyfikuje właściwości pojedynczego obiektu gry na podstawie bieżącego aspektu. Zwykle wymaga to dużo niestandardowego kodowania. Moje rozszerzenie próbuje złagodzić wiele tego bólu.

Jason
źródło
2
Ta odpowiedź byłaby lepsza i wydaje się mniej spamowa, gdybyś opracował rzeczywiste techniki, których można by użyć, aby odpowiedzieć na pytanie PO.
Josh