Autor Wątek: [Android] dopasowywanie gry pod telefony i tablety  (Przeczytany 5352 razy)

Offline .Dexter.

  • Użytkownik

# Październik 26, 2012, 20:24:20
Cześć.

Tworzę grę opartą na kafelkach. Chciałbym żeby działała zarówno na telefonach jak i na tabletach. Mapa w mojej grze ma 15x25 kafelków i chciałbym żeby na tablecie były one po prostu większe.
Bitmapę z tłem skaluję i tak, bo telefony mają różnej wielkości ekrany, ale tak się składa, że na zdecydowanej większości rozdzielczości ekranów dzielą się przez 25 i 15, np. mój ma ekran 240x400 (240%15=0 i 400%25=0) więc wszystko jest jak należy.
Problem jest z tabletami, gdzie rozdzielczości są typu 800x1280. Bitmapy skaluję za pomocą createScaledBitmap, której docelowe rozmiary trzeba podać jako inty.

Ma ktoś pomysł jak powinno się to robić?

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xirdus

  • Redaktor

# Październik 26, 2012, 20:49:31
1. Jakiś pasek menu na dole?

2. Jeśli kafli jest w rzeczywistości więcej niż te pokazane 25x15 i możesz sobie pozwolić na pokazanie samych urywków brzegowych kafli, to możesz skalować po polu (240*400=96000; 800*1280=1024000; 1024000/96000=10.67; sqrt(10.67)=3.27, czyli skalujesz kafle 3.27 raza) - wtedy obszar widzenia w grze będzie mniej więcej stały, zmieniać się będą proporcje.

Offline .Dexter.

  • Użytkownik

# Październik 26, 2012, 21:02:43
1. Nie ma menu ani nic na dole.

2. Problem w tym właśnie, że chciałem to zrobić tak, żeby nie było widać urywków, tylko żeby cały ekran był ładnie pokryty kaflami, ale to jest niewykonalne jeśli kafle mają być kwadratowe, więc pozostaje zostawić to z tymi urywkami albo rysować jakąś ramkę wokół ekranu.

Dzięki za odpowiedź :)

Offline dynax

  • Użytkownik

# Październik 26, 2012, 21:19:39
A nie możesz pomnożyć rozmiaru bitmapy przez (ROZMIAR_EKRANU / DOCELOWA_ROZDZIELCZOSC) i zrzutować na inta?
Więc powiedzmy, że piszesz pod rozdzielczość 800x480 i tworzysz sobie teksturę 100x50. Chcąc ją wczytać do programu skalujesz sobie jej rozmiar:
// float sizeX = 100.f;
// float sizeY = 50.f;

sizeX *= (getScreenSizeX() / 800.f);
sizeY *= (getScreenSizeY() / 480.f);

createScaledBitmap(..., (int)sizeX, (int)sizeY, ...);

EDIT: I przez ten sam factor warto też pomnożyć pozycję tekstury przed wyświetleniem.
« Ostatnia zmiana: Październik 26, 2012, 21:22:03 wysłana przez dynax »

Offline Xirdus

  • Redaktor

# Październik 26, 2012, 23:37:51
@dynax: twój kod rozwala proporcje.

Offline Pawelx156

  • Użytkownik

# Październik 27, 2012, 10:36:50
Nigdy nie pisałem na żaden telefon czy androida.
Ale ogólnie chyba we wszystkim czym się pisze ( tak mi się wydaje ) nie powinno być problemu z dopasowaniem rozdzielczości.

W przypadku gier 2D na komputer ( pisze w C# ) wybiera się tylko 2 rozdzielczości: albo 1280x720 albp 1920x1080. Chodzi o to że z nich jest bardzo ładny przekład na dowolnie inną rozdzielczość monitorów.
Więc telefon czy tablet musi coś w spólnego posiadać.

Są dwie szkoły które się sprawdzają.
Obydwie startowo pobierają  rozdzielczość maksymalną ( ustawioną aktualnie - rozdzielczość pulpitu )
Jest to najlepszy wybór z uwagi na to ze natywna rozdzielczość w monitorach ma najlepsze odwzorowanie kolorów.



1. Rozdzielczość skalowana.

Tutaj rysujesz wszystko na Tylnym Buforze. - ten tylny bufor nie jest domyślny. Musi być RenderTargetem.
Potem odpowiednio ustawiasz ViewPort urządzenia z odpowiednią skalą i jako końcowy etap rysujesz
RenderTarget z użyciem macierzy skali.
Przy spójnej rozdzielczości  ( aspect ratio - proporcje np: 1920x1080 ma 1.77777777 )   na ekranie zobaczysz 100% pokrycia.
Przy aspect innym niż Tylny bufor ekran będzie trochę węższy wyśrodkowany, z zachowaniem proporcji, ale pojawią się czarne pasy u dołu bądź z boku. ale jesteś niezależny od ustawień rozdzielczości. zawsze rysujesz tyle samo

2. Rozdzielczość nie skalowana.
Tutaj twój tylny bufor ma rozdzielczość taką jaką ma aktualnie ustawiony pulpit.
Skalujesz tylko ekrany np menu opcje itp. Z reguły level jeśli jest większy  rysujesz więcej.
Czyli im ktoś ma większą rozdzielczość Pulpitu tym więcej widzi.
Tego  nie polecam bo to nie jest takie łatwe. A do tego niesprawiedliwe.


U siebie ( piszę w xna) mam ten pierwszy sposób. Chodzi wyśmienicie.  Wkleję tobie kod mojej klasy tutaj. Może jakoś coś zaskoczy , albo sobie to przełożysz na to w czym piszesz.




  public static class ResolutionMenager
    {

        #region Pola
       
     
        /// <summary>
        /// Szerokosc aktualnego ekranu
        /// </summary>
        public static int ScreenWidth;
        /// <summary>
        /// Wysokosc aktualnego ekranu
        /// </summary>
        public static int ScreenHeight;
        /// <summary>
        /// aspect ratio czyli proporcje aktualnego ekranu
        /// </summary>
        public static float ScreenAspect;
        /// <summary>
        /// Aktualna skala VievPortu potrzebna do wyswietlenia  bufora w odpowiednim miejscu
        /// </summary>
        public static Matrix ScreenMatrix;
        /// <summary>
        /// Szerokosc tylnego bufora
        /// </summary>
        public static int VirtualWidth;
        /// <summary>
        /// Wysokosc tylnego bufora
        /// </summary>
        public static int VirtualHeight;
        /// <summary>
        /// Aspect ratio - proporcje tylnego bufora
        /// </summary>
        public static float VirtualAspect;
        /// <summary>
        /// Startowa szerokosc pulitu ( przed uruchomieniem gry)
        /// </summary>
        public static int DefaultWidth;
        /// <summary>
        /// Startowa wysokosc pulpitu ( przed uruchomieniem gry)
        /// </summary>
        public static int DefaultHeight;
        /// <summary>
        /// Asp[ect ratio - proporcje pulpitu
        /// </summary>
        public static float DefaultAspect;
        /// <summary>
        /// czy jest ustawiony tryb fullScreen
        /// </summary>
        public static bool isFullScreen = false;
        /// <summary>
        /// Jaki zostal wybrany tryb  ekranowy
        /// </summary>
        public static ResolutionType ActualScreenType = ResolutionType.UserSet;
        /// <summary>
        /// Zmienna muwiaca czy nalerzy odtworzyc skale matrix 
        /// </summary>
        private static bool CreateMatrix = false;
        /// <summary>
        /// Parametry i ustawienia gry ( zaraz przy starcie nie zmieniane wiecej )
        /// </summary>
        private static PresentationParameters GameParametrs = null;
        #endregion


        #region Metody
        /// <summary>
        /// Inicjalizuje  parametry klasy
        /// </summary>
        /// <param name="graph">GraphicDevice - urzadzenie graficzne</param>
        public static void Intialize()
        {

            DefaultWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
            DefaultHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
            DefaultAspect = (float)DefaultWidth / (float)DefaultHeight;
            CreateMatrix = true;
        }
        /// <summary>
        /// Ustawia wirtualny ekran w oparciu o te parametry ustawiaja sie inne menagery
        /// </summary>
        /// <param name="width">szerokosc bufora</param>
        /// <param name="height">wysokosc bufora</param>
        public static void SetVirulScreen(int width, int height)
        {
            VirtualWidth = width; VirtualHeight = height;
            VirtualAspect = (float)VirtualWidth / (float)VirtualHeight;
            CreateMatrix = true;
        }

        /// <summary>
        /// ustawia rozdzielczosc ekranu o podane parametry
        /// </summary>
        /// <param name="width">szerokosc ekranu</param>
        /// <param name="height">wysokosc ekranu</param>
        /// <param name="isfullscreen">czy ma byc na pelnym ekranie</param>
        /// <param name="type">tryb tworzenia ekranu</param>
        public static void SetResolution(int width, int height, bool isfullscreen, ResolutionType type)
        {
            ScreenWidth = width; ScreenHeight = height; isFullScreen = isfullscreen; ActualScreenType = type;
            ScreenAspect = (float)ScreenWidth / (float)ScreenHeight;
            ApplySettings();
            CreateMatrix = true;
         //   if (GameParametrs == null)
            {
                GameParametrs = GameServices.CurrentDevice.GraphicsDevice.PresentationParameters.Clone();
            }
        }

        /// <summary>
        /// ukrywa edytor i pokazuje Gre
        /// </summary>
        /// <param name="editor">Edytor ktory nalezy ukryc</param>
        public static void SwitchToGame(System.Windows.Forms.Form editor)
        {
            PresentationParameters par = GameParametrs.Clone();
            GameServices.GameWindow.Visible = true;
            Mouse.WindowHandle = GameServices.GameWindow.Handle;
            GameServices.CurrentDevice.GraphicsDevice.Reset(par);
            CalculateScreenSize(par.BackBufferWidth, par.BackBufferHeight);
            editor.Visible = false;
            Scene.SceneMode = EGameMode.GameMode;

        //    System.Windows.Forms.Cursor.Hide();
        }

        public static void SwitchToGame(System.Windows.Forms.Form editor, bool _FullScreen,bool _Auto)
        {
            Scene.LockScene = true;

            try
            {

                //  PresentationParameters par = GameParametrs.Clone();
             //   PresentationParameters par = GameServices.CurrentDevice.GraphicsDevice.PresentationParameters.Clone();

                GameServices.GameWindow.Visible = true;
                Mouse.WindowHandle = GameServices.GameWindow.Handle;
                editor.Visible = false;
                Scene.SceneMode = EGameMode.GameMode;
                if (_FullScreen)
                {
                    if (_Auto)
                    {
                        SetResolution(1280, 720, true, ResolutionType.Auto);
                        GameServices.Settings.FullScreen = true;
                    }
                    else
                    {
                        SetResolution(1280, 720, true, ResolutionType.Default);
                        GameServices.Settings.FullScreen = true;
                    }
                }
                else
                {
                    SetResolution(1280, 720, false, ResolutionType.UserSet);                   
                    GameServices.Settings.FullScreen = false;                 
                }

                CalculateScreenSize(ScreenWidth, ScreenHeight);
            }
            catch { }
            Scene.LockScene = false;
            //    System.Windows.Forms.Cursor.Hide();
        }

        /// <summary>
        /// Pokazuje Edytor i ukrywa gre
        /// </summary>
        /// <param name="editor">Edytor ktory ma zostac pokazy</param>
        /// <param name="drawsurface">Uchwyt do powierzchni rysunkowej na edytorze</param>
        /// <param name="width">Szerokosc powierzchni rysunkowej</param>
        /// <param name="height">Wysokosc powierzchni rysunkowej</param>
        public static void SwitchToEditor(System.Windows.Forms.Form editor,IntPtr drawsurface, int width, int height)
        {
            Scene.LockScene = true;

            try
            {
                PresentationParameters par = GameParametrs.Clone();
                // PresentationParameters par =  GameServices.CurrentDevice.GraphicsDevice.PresentationParameters.Clone();
                par.BackBufferWidth = width;
                par.BackBufferHeight = height;
                par.DeviceWindowHandle = drawsurface;
                Mouse.WindowHandle = drawsurface;
                par.IsFullScreen = false;
                GameServices.CurrentDevice.GraphicsDevice.Reset(par);
                GameServices.GameWindow.Visible = false;
                //  System.Windows.Forms.Cursor.Show();
                editor.Show();
                CalculateScreenSize(width, height);
                Scene.SceneMode = EGameMode.EditMode;
            }
            catch { }
            Scene.LockScene = false;
        }


        // Przekalkulowauje aktualnom rozdzielczosc ekranu
        private static void CalculateScreenSize(int width, int height)
        {
            ScreenWidth = width; ScreenHeight = height;
            ScreenAspect = (float)ScreenWidth / (float)ScreenHeight;
            CreateMatrix = true;
        }


        // aplikacja ustawien zgodnie z parametrami
        private static void ApplySettings()
        {

            if (ActualScreenType == ResolutionType.Auto)
            {
                // jelsi proporcje virtualan i pulpitu zgadzaja sie ustaw rozdzielczosc pulpitu
                if (VirtualAspect == DefaultAspect)
                {
                    ApplyDefaultScreen();
                }
                else
                {
                    //jelsi nie przeszukaj wszystkie rozdzielczosci o tych samych proporcjach co pulpit i ustaw pierwsza lepsza
                    // z nich
                    bool FoundedScreen = false;
                    foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
                    {
                        float aspect = (float)dm.Width / (float)dm.Height;
                        if (FoundedScreen == false)
                        {
                            if ((dm.Width >= VirtualWidth) && (dm.Height >= VirtualHeight) && aspect == DefaultAspect)
                            {                         
                                GameServices.CurrentDevice.PreferredBackBufferWidth = dm.Width;
                                GameServices.CurrentDevice.PreferredBackBufferHeight = dm.Height;
                                GameServices.CurrentDevice.IsFullScreen = isFullScreen;                               
                                GameServices.CurrentDevice.ApplyChanges();
                                FoundedScreen = true;
                            }
                        }

                    }
                    // jelsi nie zostala odnaleziona rozdzielczosc ustaw rozdzielczosc pulpitu
                    if (FoundedScreen == false) ApplyDefaultScreen();
                }
            }
            // jesli wybrano domyslna rozdzielczosc ustaw rozdzielczosc pulpitu
            if (ActualScreenType == ResolutionType.Default)
            {
                ApplyDefaultScreen();
            }

            // wymuszony ekran
            if (ActualScreenType == ResolutionType.UserSet)
            {
                bool FoundedScreen = false;
                // Pobranie aktualnej rozdzielczosci
                DisplayMode dp = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode;
                if (dp.Height >= ScreenHeight && dp.Width >= ScreenWidth)             
                {
                    // sprawdzenie czy podana rozdzielczosc jest w rozdzielczosciach obslugiwanych przez monitor
                    // jelsi nie ustaw domyslna pulpitu
                    foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
                    {
                        if (FoundedScreen == false)
                        {
                            if ((dm.Width >= ScreenWidth) && (dm.Height >= ScreenHeight))
                            {
                                GameServices.CurrentDevice.PreferredBackBufferWidth = ScreenWidth;
                                GameServices.CurrentDevice.PreferredBackBufferHeight = ScreenHeight;
                               GameServices.CurrentDevice.IsFullScreen = isFullScreen;
                                GameServices.CurrentDevice.ApplyChanges();
                                FoundedScreen = true;
                            }
                        }
                    }
                }
                // jelsi nei znaleziono rozdzielczosci ustawiona zostaje domyslan pulpitu
                if (FoundedScreen == false) ApplyDefaultScreen();

            }

            // ustawienie aktualnie dobranej rozdzielczosci
            ScreenWidth = GameServices.CurrentDevice.PreferredBackBufferWidth;
            ScreenHeight = GameServices.CurrentDevice.PreferredBackBufferHeight;
            ScreenAspect = (float)ScreenWidth / (float)ScreenHeight;
            CreateMatrix = true;
        }

 
        // aplikuje rozdzielczosc pulpitu ( domyslna)
        private static void ApplyDefaultScreen()
        {
            GameServices.CurrentDevice.PreferredBackBufferWidth = DefaultWidth;
            GameServices.CurrentDevice.PreferredBackBufferHeight = DefaultHeight;
            GameServices.CurrentDevice.IsFullScreen = isFullScreen;
            GameServices.CurrentDevice.ApplyChanges();
        }


        // ustawia Vievport na rozdzielczosc domyslna w celu wyczyszczenia device
        public static void SetScreenViewPort()
        {
            Viewport vp = new Viewport();
            vp.X = vp.Y = 0;
            vp.Width = ScreenWidth;
            vp.Height = ScreenHeight;
            GameServices.CurrentDevice.GraphicsDevice.Viewport = vp;

        }

 
        // pobiara skale Matrix
        public static Matrix GetMatrix()
        {
            if (CreateMatrix) ResizeMatrix();
            return ScreenMatrix;
        }


        // ustawia sklae matrix poprzez odpowiednie parametry
        public static void ResizeMatrix()
        {
            CreateMatrix = false;
            ScreenMatrix = Matrix.CreateScale(
                           (float)GameServices.CurrentDevice.GraphicsDevice.Viewport.Width / VirtualWidth,
                           (float)GameServices.CurrentDevice.GraphicsDevice.Viewport.Height / VirtualHeight,
                         1f);
        }

         
       
        // Ustawia Virtualny Vievport dla aplikacji   
        static public void SetVirtualViewport()
        {
            //pobranie virtualnego aspect ratio ( tego z tylnego bufora) - rendertarget
            float targetAspectRatio = VirtualAspect;
            // obliczenie szerokosci i wysokosci ( z zachowaniem proporcji)
            int width = ScreenWidth;
            int height = (int)(width / targetAspectRatio);
            bool changed = false;
            // jesli wysokosc jest wieksza niz orginalna wysokosc ekranu trzeba zamienic miejscami
            if (height > ScreenHeight)
            {
                height = ScreenHeight;
                // PillarBox
                width = (int)(height * targetAspectRatio);
                changed = true;
            }

            Viewport viewport = new Viewport();
            // obliczenie pozycji x i Y wzgledem ekranu
            viewport.X = (int)(ScreenWidth / 2) - (width / 2);
            viewport.Y = (int)(ScreenHeight / 2) - (height / 2);
            viewport.Width = width;
            viewport.Height = height;
            viewport.MinDepth = 0;
            viewport.MaxDepth = 1;

            if (changed)
            {
                CreateMatrix = true;
            }
            // ustawienie Vievportu na przeskalowany i przesuniety
            GameServices.CurrentDevice.GraphicsDevice.Viewport = viewport;
        }
       
        #endregion

    }


Teraz przy starcie gry wywołuję dwie metody z klasy powyżej:
  ResolutionMenager.Intialize(); // inicjalizacja danych
  ResolutionMenager.SetVirulScreen(1280, 720); // utworzenie wirtualnego BackBufora


Z tej lasy korzysta klasa Scene. Ona tworzy tylny bufor w oparciu o informacje zawarte w klasie ResolutionMenager.


A tak wygląda końcówka Draww  mojej scenie:

  ResolutionMenager.SetScreenViewPort(); // ustawinie Normalnego ViewPortu i go wyczyszczenie
                GameServices.CurrentDevice.GraphicsDevice.Clear(Color.Black);
                ResolutionMenager.SetVirtualViewport(); // ustawienie Virtualnego ViewPortu

                GameServices.Sprite.Begin(DeafultSort, CurrentBlend, null, null, null, null, ResolutionMenager.GetMatrix()); // narysowanie z matrixem
             
                GameServices.Sprite.Draw(TwojTylnyBufor, new Rectangle(0,0,(int)(ResolutionMenager.VirtualWidth),(int)(ResolutionMenager.VirtualHeight)), Color.White);
                GameServices.Sprite.End();


I koniec. Grę mam niezależną od rozdzielczości. zawsze mam zachowane proporcje. Jedynie pojawi się u kogoś dodatkowy pasek czarnego ekranu i tyle.

Offline dynax

  • Użytkownik

# Październik 27, 2012, 15:40:53
@dynax: twój kod rozwala proporcje.

Zazwyczaj różnice w proporcjach nie są na tyle duże, żeby było to zauważalne, choć masz rację - jeśli chciałbym to przenosić z telefonu na desktop to wyglądałoby to źle :)