XNA – skalowanie i centrowanie

XNAJeśli ktoś (tak jak ja) pierwsze kroki z XNA stawiał w Windows Phone, to możliwe, na w swoich projektach założył dość sztywne reguły, np: rozdzielczość urządzenia to 800×480 pikseli. Tak właśnie zrobiłem ja. Na jakiś czas (około 1 rok) było to ok, bo wszystkie telefony z Windows Phone pracowały właśnie w tej rozdzielczości. Ale czasy się zmieniły. Z jednej strony pojawił się Windows Phone 8 i nowe rozdzielczości, a z drugiej strony Windows 8, gdzie różnych rozdzielczości, formatów jest jeszcze więcej.

Ten wpis powstał w nawiązaniu do mojej mini-serii o portowaniu gier z Windows Phone 7 do Windows Store przy pomocy MonoGame

Skalowanie

Moja aplikacja XNA z Windows Phone nie była na to gotowa wiec trzeba było coś  z tym zrobić. Bez skalowania prezentowała się jak na poniższych screenach:

Space Bridge - Windows Store - Brak skalowania Space Bridge - Windows Store - Brak skalowania

Na początek chciałem by po prostu content mojej gry potrafił się odpowiednio przeskalować, zachowując oczywiście prawidłowe proporcje.

W globalnej (singletone) klasie Setting wprowadziłem nową właściwość ScreenScale. Jest to współczynnik określający scalę, wartość przez którą będziemy mnożyć wielkość tekstur i wyliczanie pozycji gdzie dana tekstura ma zostać wyświetlona.

public static class Settings
{
	static Settings()
	{
		ScreenScale = 1f;
		ScreenCenterVector = Vector2.Zero;
	}

	public static float ScreenScale { get; set; }
	public static Vector2 ScreenCenterVector { get; set; }

	// more settings
}

W głównej klasie gry dodałem metodę SetupScale(), która wylicza współczynnik skali.

private void SetupScale()
{
	float screenScaleX = graphics.GraphicsDevice.Viewport.Width/800f;
	float screenScaleY = graphics.GraphicsDevice.Viewport.Height/480f;
	float screenScale = Math.Min(screenScaleX, screenScaleY);
	Settings.ScreenScale = screenScale;
}

Wartości 800 i 480 wysokość i szerokość mojego kontentu (moja gra została zaprojektowana pod taką rozdzielczość). Następnie trzeba umieścić wywołanie metody SetupScale(). Ja wybrałem zdarzenie ClientSizeChanged oraz metodę LoadContent. Dzięki temu skala zostanie wyliczona na początku oraz zawsze po zmianie rozdzielczości, również gdy urządzenie zostanie obrócone.

Ostatni krok to zaaplikowanie naszego współczynnika skali, by tekstury były rysowane na całym ekranie.

Odszukałem wszystkie wywołania spriteBatch.Draw() i uwzględniłem Setting.ScreenScale:

spriteBatch.Draw(
	playerTexture,
	playerPosition * Settings.ScreenScale, // position
	null,
	Color.White,
	0f,
	Vector2.Zero,
	Settings.ScreenScale, // scale
	SpriteEffects.None,
	0f);

Efekt skalowania widoczny na poniższych screenach:

Space Bridge - Windows Store - Skalowanie Space Bridge - Windows Store - Skalowanie

Centrowanie

Po drugie chciałem, by aplikacja potrafiła automatycznie wycentrować zawartość gry. Bez tego, np. po obróceniu tabletu do pionu, gra wypełniała całą szerokość, a w pionie była dociągnięto to górnej krawędzi. Podobnie wyglądało to w orientacji landscape, gdzie dociąganie było do lewej.

W klasie Settings dodałem jeszcze jedną właściwość ScreenCenterVector.

public static class Settings
{
	static Settings()
	{
		ScreenScale = 1f;
		ScreenCenterVector = Vector2.Zero;
	}

	public static float ScreenScale { get; set; }

	public static Vector2 ScreenCenterVector { get; set; }
}

ScreenCenterVector ma określać wektor przesunięcia zawartości gry tak, by całość była wyśrodkowana w pionie i poziomie. Wyliczanie tej wartości dodałem do metody SetupScale().

private void SetupScale()
{
	float screenScaleX = graphics.GraphicsDevice.Viewport.Width/800f;
	float screenScaleY = graphics.GraphicsDevice.Viewport.Height/480f;
	float screenScale = Math.Min(screenScaleX, screenScaleY);
	Settings.ScreenScale = screenScale;

	float gameWidth = 800f*screenScale;
	float gameHeight = 480f*screenScale;

	Settings.ScreenCenterVector = new Vector2(
		(graphics.GraphicsDevice.Viewport.Width - gameWidth)/2,
		(graphics.GraphicsDevice.Viewport.Height - gameHeight)/2);
}

Na koniec zostało zaaplikowanie ScreenCenterVector w metodach Draw():

spriteBatch.Draw(
	playerTexture,
	playerPosition * Settings.ScreenScale + Settings.ScreenCenterVector, // position
	null,
	Color.White,
	0f,
	Vector2.Zero,
	Settings.ScreenScale, // scale
	SpriteEffects.None,
	0f);

Efekt końcowy (skalowanie + centrowanie) widoczny na poniższych screenach:

Space Bridge - Windows Store - Skalowanie i centrowanie Space Bridge - Windows Store - Skalowanie i centrowanie

Advertisements

2 thoughts on “XNA – skalowanie i centrowanie

  1. Pingback: dotnetomaniak.pl

  2. Pingback: Portowanie gier z XNA WP7 na Windows 8 MonoGame | Wojciech Poniatowski [PL]

Możliwość komentowania jest wyłączona.