1.1 ScreenRotationFix

Published March 06, 2020
Advertisement

Hello followers welcome to part 2 of my developer story as I work to bring a 2D game engine to life.

Today I wanted to write to you about how props work in a Valkyrie project, but I've been side-tracked by a different issue – positioning props, obstacles and sprites correctly on-screen. I got this working with a static display but that just isn't the way devices work anymore. So after today I am almost finished with a feature branch allowing screen-rotation events to properly realign all the game elements on-screen – seamlessly switching between portrait and landscape mode.

So why was this challenging to do? A lot of my struggles in this project can be traced back to 1 thing and that is SkiaSharp. Skia is an excellent library and without it I would be nowhere but it has 1 detail that drives me nuts: Skia elements have coordinate origins in the upper-left corner. I have no way of knowing ahead of time what my runtime environment's screen dimensions are, so there is no way to hard-code something that's supposed to go near but not on the bottom of the screen. If the Y origin were on bottom I could just start building stuff at Y=50 or Y=100 but instead I've got to figure out what the height is and start building at height – 100. Confusingly, +Y is also down in SkiaSharp so I'd start at Y - 100 and keep subtracting more to go up.

When it comes to designing levels, I wanted a coordinate plane who's origin is in the lower left corner, where +Y is up. It's already a little difficult to think in terms of X, Y coordinates when creating XML tags to describe the elements of a level. This lead me to start using a Game Logic coordinate system. The GLPosition class is in the Valkyrie.GL library, and is based on a lower-left corner origin. The pixel units are the same units as are used in SkiaSharp, but +Y in GL is up and -Y is down. Segregating the game logic from the display logic this way made developing those libraries a breeze, but how to figure out where to draw something on-screen?

Sprites, Tiles, Props and everything else shown on screen actually partially exists in 3 places in Valkyrie. In the GL layer, in the Graphics layer, and in the Model layer which combines GL and Graphics members.

Valkyrie model heirarchy

Model

/ \

Graphics Game Logic

This is in keeping with the MVVM pattern of Xamarin.Forms. Putting the GL and Graphics together into higher level objects in the /Model layer has been tricky.

Step 1: I needed a class who's sole responsibility is to know the runtime display environment details. This is the Valkyrie.Graphics.Screen class. When constructed, it will use Xamarin.Essentials tools to discover the screen's height and width and infer weather the screen orientation is portriat, landscape or square. Every ViewModel has one of these and uses it to select an appropriate background image. The GamePageViewModel however uses a derived GameScreen class but it does the same thing when constructed.

Step 2: I needed a way to index game-logic coordinates (which are based on a lower-left corner origin) to SkiaSharp coordinates (based on an upper-left corner origin). To know this I needed:

  1. a higher level class in the /Model layer that combines the GL and Graphis objects. These are the Actor, Obstacle and Prop classes.
  2. A derived specialization of the Screen class, GameScreen which will be dealing with GL and Graphics objects so also resides in /Model. GameScreen has a member of the Scrollbox class which is going to be used to control camera movements. The scrollbox has 4 Skia coordiantes and 4 GL coordinates. The SK coordinates of this box do not move, but the GL coordinates do move to keep the camera focus inside the box. This can also translate GL positions into Skia coordinates, which is how I know where to place every sprite on screen. GameScreen currently uses a couple of ObservableCollections lists to store its sprite data. Don't get used to this though I'm going to change it again soon.
  3. The missing piece, how to reposition the sprites held in GameScreen's lists when the screen orientation changes? This was what I worked on most of today and have (mostly) solved. The hangup was that I was passing /Model classes as parameters to the GameScreen when adding these sprites, but it did not store the GL position data of these objects, it just used the Scrollbox to figure out where to put them and draws them where they're supposed to be. When a resize event happened, I couldn't re-key them the way I did before.
The scrollbox highlighted

Now, I have actually created a super position class in /Model, which GameScreen does have access to because it is storing lists of the /Model objects as references. When a resize event happens, the GameScreen creates a new scrollbox based on the current screenInfo details and then refers to those object's Position.GLPosition members and uses the Scrollbox to translate them to Skia coordinates again.

Ouch, right? A lot of work just to rotate the screen, but the payoffs are I am now free to think in normal cartesian coordinates when designing levels. Anyone developing content for Valkyrie will not have to worry about this going forward. I also had to solve this anyway to make the camera “scroll” later, so better now than later.

I promise, I'll get more into props next time.

0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement