AIR iOS scaling entire content, can't see anything - ios

I've created an app with the latest version of AIR (16.0.0.272) and I'm trying to scale the content to fit any resolution of the iPhone from 4 to 6+
In desktop debugging I see all perfectly scaled changing the stage dimension to test my solution. Trying on my iPhone 5 I can't see the scaled content.
If I comment the resize function everything is ok (I see all the content, not scaled obviously).
That's my solution
private function resize(e:Event=null):void{
w=stageW();
h=stageH();
var initW:Number=640;
var initH:Number=960;
//bg.img.width=w;
trace(w+"/"+h);
main.y=62;
//main.x=w*.5-320;
bg.width=w;
bg.height=h;
menu.bg.width=w;
menu.bg.height=h;
var divisor:Number = 640/w;
main.width = Math.floor(main.width / divisor);
main.height = Math.floor(main.height / divisor);
}
I have tried to temporize the resize call to test it on the iPhone but again after 2000ms I can't see anything.
After this I've tried with a listener addEventListener(Event.ADDED_TO_STAGE, init); calling the resize above at the end of my operations of building UI and so on.
Can't figure why resizing a movieclip that contains my app content make it disappear from iPhone and not from the desktop.
Hope in a solution
Thanks in advance

Capabilities.screenResolutionX and Capabilities.screenResolutionY are what you should use for screen width and height on apps.
Note: the X and Y don't change with rotation - they'll stay the same when screen rotates, so your code to get screen dimensions will look like this:
if (bStagePortrait) {
iScreenWidth = Capabilities.screenResolutionX;
iScreenHeight = Capabilities.screenResolutionY;
} else {
iScreenWidth = Capabilities.screenResolutionY;
iScreenHeight = Capabilities.screenResolutionX;
}
Also use this code to prevent app from resizing or moving:
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;

For a variety of reasons, your resize function could be getting called more than once.
main.width = Math.floor(main.width / divisor);
main.height = Math.floor(main.height / divisor);
will continuously make the main clip smaller. It may be better to use a constant or number for size calculation. I usually do something with scaling, though.
main.scaleX = main.scaleY = percentStageScaled;

Related

Xamarin Forms Bitmap Coordinates on SkiaSharp Canvas

I have a simple Xamarin Forms page that contains a SkiaSharp Canvas. I am allowing the user to load a floor plan bitmap to use as the background for the Canvas, and they can add smaller bitmaps to be dragged around the canvas where they need.
I am following this guidance on the Microsoft page for touch manipulation.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/touch
Everything works perfectly if I use it on a single device size. When I launch it on a different device size, the resolution and Canvas size are different, and it is causing the smaller bitmaps to be placed inconsistently.
Here is the result I am seeing. The left is an iPhone 13 Pro Max, and the right is a iPhone 13 Pro. As you can see, the result is off when I view it on different size devices.
Here is the Grid that contains the SKCanvas:
<Grid BackgroundColor="White" Grid.Row="1">
<skia:SKCanvasView x:Name="canvasView" PaintSurface="OnCanvasViewPaintSurface" />
<Grid.Effects>
<tt:TouchEffect Capture="True" TouchAction="OnTouchEffectAction" />
</Grid.Effects>
</Grid>
Here is the PaintSurface handler:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
//set background
SKRect dest = new SKRect(0, 0, info.Width, info.Height);
canvas.DrawBitmap(backgroundBitmap, dest, BitmapStretch.Uniform,
BitmapAlignment.Start, BitmapAlignment.Start);
//Set the coordinates for the pin bitmap
var transX = 1005;
var transY = 1189;
bitmap.Matrix = SKMatrix.CreateTranslation(transX, transY);
// Display the bitmap
bitmap.Paint(canvas);
// Display the matrix in the lower-right corner
SKSize matrixSize = matrixDisplay.Measure(bitmap.Matrix);
matrixDisplay.Paint(canvas, bitmap.Matrix,
new SKPoint(info.Width - matrixSize.Width,
info.Height - matrixSize.Height));
}
I've been through several posts on StackOverflow as well as in MS forums. I can't figure out a way to make this show consistently on different size devices.
Does anyone have a suggestion on how I can keep this smaller bitmap in the same location on the background/SKCanvas regardless of device size?
I appreciate any feedback.
The coordinate of the bitmap is not supposed to be hard-coded , you need to adjust the value (x/y) according to the background canvas's size.
Something like this
var transX = info.Width - 20;
var transY = info.Height - 20;

Spritekit scaling for universal app

I am having trouble properly setting up my app so that it displays correctly on all devices. I want my game to look best for iPhone and I understand that setting my scene size using GameScene(size: CGSize(width: 1334, height: 750)) and use .aspectFill means that on iPads there will be less space to display things which I'm fine with. The problem is, how do I position my nodes so that they are relative to the each devices frame height and width? I use self.frame.height, self.frame.width, self.frame.midX, etc. for positioning my nodes and when I run my game, it positions things properly considering I run on my iPhone 6, but on my iPad, everything seems blown up and nodes are off the screen. I'm going crazy trying to figure this out
I solved this in my game by using scaleFactors, numbers which tell my app how much to enlarge each length, width, height etc. Then I just make my game look well for one phone, and use that phone's width and height to calculate with which factor I need to enlarge it for other devices. In this example I use the iPhone 4 as a base, but you can use any device just change the numbers according to that device.
Portrait mode:
var widthFactor = UIScreen.main.bounds.width/320.0 //I divide it by the default iPhone 4 width
var heightFactor = UIScreen.main.bounds.height/480.0
Landscape mode:
var widthFactor = UIScreen.main.bounds.width/480.0 //I divide it by the default iPhone 4 landscape width
var heightFactor = UIScreen.main.bounds.height/320.0
Then when you make a node, a coin image for example, multiply its coordinates or width/height by the scaleFactors:
let coin = SKSpriteNode(imageNamed: "coin")
coin.position = CGPoint(x: 25 * widthFactor, y: self.size.height - 70 * heightFactor)
I think what you're looking for might be in this answer: https://stackoverflow.com/a/34878528/6728196
Specifically I think this part is what you're looking for (edited to fit your example):
if UIDevice.current.userInterfaceIdiom == .pad {
// Set things only for iPad
// Example: Adjust y positions using += or -=
buttonNode.position.y += 100
labelNode.position.y -= 100
}
Basically, this just adds or subtracts a certain amount from the iPhone position if the user is using an iPad. It's not too complicated, and you can increase or decrease both x and y values of position by a certain value of percentage of the screen (self.size.width * decimalPercentage).
Another benefit of using this way is that you're just modifying the iPhone positions, so it starts by using the default values that you set. Then if on iPad, it will make changes.
If this is hard to understand let me know so I can clear up the explanation

Why are SKSpriteNodes only partially rendering?

I've coded a basic layout of "cards" for level selection in a game using Swift and SpriteKit. It's basically just 6 level selection cards side by side that have a picture of the level that the user can select. To create them I am running a for-loop and placing the first one in the center of the screen, a padding, then the second one, then padding etc. Each card is an SKSpriteNode from a png image. Each card is approx a 3rd of the device wide and about a 3rd of the devices height.
I create all six of them and then created an action that moves all 6 cards left or right to select which on the player would like. The one in the center is the one that is selected.
Everything works great on iphone simulators (tested on iPhone 6, iPhone 6 plus, iPhone 7 Plus and iPhone 5... all work great). On iPad simulators the first and last card have a portion of the image that doesn't render at all. The first card has about 1/4 of it lost on the left side, the last card has about 1/4 or it lost on the far right side. I tried running it on a physical iPad as well and it has the same issue. When I run it on an iPad Pro 12.7 it gets worse... it cuts off more of the image.
If I choose only to display 5 of the 6 cards they all render great.
If I choose to shrink them down to about 1/4 of the device width and 1/4 of the device height and lessen the padding they render fine.
I tried playing with the Scene and View sizes and scales and didn't have any improvement.
I've tried using different images and there is no changes at all.
I've double checked all zPositions and found no improvement.
I've tried systematically removing all other objects in the scene and still have the problem.
I've put them on their own "layer" which is an SKEffectNode named cardNode. (it's an SKEffectNode because I choose to later blur it when an alert screen comes up in front of it). I thought that putting them onto their own layer might help but it didn't.
I've put physics bodies on the cards just to make sure that they are still "there" and the physics bodies appear in the correct places. If I click on part of the node that isn't rendered it still does behave properly as though it was still rendered in that area.
I can't figure out where to go from here to fix this. Ideally I would like to add more cards yet in the future but getting stuck on this problem.
Here is the code that I have for creating the cards.
let cardNode = SKEffectNode()
let levelCardArray: [String] = [
"BlackBoxLevelCard.png"
,"FruitLevelCard.png"
,"SportsLevelCard.png"
,"BarnLevelCard.png"
,"SeaLevelCard.png"
,"SpaceLevelCard.png"
]
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
let w10 = screenWidth * 0.10
let w40 = screenWidth * 0.40
let w50 = screenWidth * 0.50
let w60 = screenWidth * 0.60
let h50 = screenHeight * 0.50
let cardMargin = w10
let cardSize = CGSize(width: w40, height: w60)
let startPosition = CGPoint(x: w50, y: h50)
override func didMove(to view: SKView) {
let scene = levelSelectionScene(size: view.bounds.size)
scene.backgroundColor = UIColor.black
let skView = view as SKView
skView.ignoresSiblingOrder = true
cardNode.zPosition = 100
self.addChild(cardNode)
for i in 1...levelCardArray.count {
let currentArrayValue = i - 1
let cardSprite = SKSpriteNode(imageNamed: levelCardArray[currentArrayValue])
cardSprite.size = cardSize
cardSprite.position = CGPoint(x: startPosition.x + (CGFloat(currentArrayValue) * (cardSize.width + cardMargin)), y: startPosition.y)
cardSprite.zPosition = cardNode.zPosition
cardSprite.name = "levelCardObject"
cardNode.addChild(cardSprite)
}
Any help or insight would be greatly appreciated. Thanks guys!
I tried all day today to try to find a way to make this work through resizing the scenes and views and haven't had any luck with it at all.
My solution is to check for the device type and if it's an iPad I am reducing the image sizes and buffer between images until it doesn't cut them off. I don't consider this a very good solution, really just a work around until I can find a better way to do it. Thank you guys for your thoughts though. I definitely appreciate it!

Scaling Image Causes Crash In AS3 Flex AIR Mobile App

Problem:
Zooming in on image by scaling and moving using matrix causes the app to run out of memory and crash.
Additional Libraries used:
Gestouch - https://github.com/fljot/Gestouch
Description:
In my Flex Mobile app I have an Image inside a Group with pan/zoom enabled using the Gestouch library. The zoom works to an extent but causes the app to die (not freeze, just exit) with no error message after a certain zoom level.
This would be manageable except I can’t figure out how to implement a threshold to stop the zoom at, as it crashes at a different zoom level almost every time. I also use dynamic images so the source of the image could be any size or resolution.
They are usually JPEGS ranging from about 800x600 - 9000x6000 and are downloaded from a server so cannot be packaged with the app.
As of the AS3 docs there is no longer a limit to the size of the BitmapData object so that shouldn't be the issue.
“Starting with AIR 3 and Flash player 11, the size limits for a BitmapData object have been removed. The maximum size of a bitmap is now dependent on the operating system.”
The group is used as a marker layer for overlaying pins on.
The crash mainly happens on iPad Mini and older Android devices.
Things I have tried already tried:
1.Using Adobe Scout to pin point when the memory leak occurs.
2.Debugging to find the exact height and width of the marker layer and image at the time of crash.
3.Setting a max zoom variable based on the size of the image.
4.Cropping the image on zoom to only show the visible area. ( crashes on copyPixels function and BitmapData.draw() function )
5.Using imagemagick to make lower quality images ( small images still crash )
6.Using imagemagick to make very low res image and make a grid of smaller images . Displaying in the mobile app using a List and Tile layout.
7.Using weak references when adding event listeners.
Any suggestions would be appreciated.
Thanks
private function layoutImageResized(e: Event):void
{
markerLayer.scaleX = markerLayer.scaleY = 1;
markerLayer.x = markerLayer.y = 0;
var scale: Number = Math.min(width / image.sourceWidth , height / image.sourceHeight);
image.scaleX = image.scaleY = scale;
_imageIsWide = (image.sourceWidth / image.sourceHeight) > (width / height);
// centre image
if(_imageIsWide)
{
markerLayer.y = (height - image.sourceHeight * image.scaleY ) / 2 ;
}
else
{
markerLayer.x = (width -image.sourceWidth * image.scaleX ) / 2 ;
}
// set max scale
_maxScale = scale*_maxZoom;
}
private function onGesture(event:org.gestouch.events.GestureEvent):void
{
trace("Gesture start");
// if the user starts moving around while the add Pin option is up
// the state will be changed and the menu will disappear
if(currentState == "addPin")
{
return;
}
const gesture:TransformGesture = event.target as TransformGesture;
////trace("gesture state is ", gesture.state);
if(gesture.state == GestureState.BEGAN)
{
currentState = "zooming";
imgOldX = image.x;
imgOldY = image.y;
oldImgWidth = markerLayer.width;
oldImgHeight = markerLayer.height;
if(!_hidePins)
{
showHidePins(false);
}
}
var matrix:Matrix = markerLayer.transform.matrix;
// Pan
matrix.translate(gesture.offsetX, gesture.offsetY);
markerLayer.transform.matrix = matrix;
if ( (gesture.scale != 1 || gesture.rotation != 0) && ( (markerLayer.scaleX < _maxScale && markerLayer.scaleY < _maxScale) || gesture.scale < 1 ) && gesture.scale < 1.4 )
{
storedScale = gesture.scale;
// Zoom
var transformPoint:Point = matrix.transformPoint(markerLayer.globalToLocal(gesture.location));
matrix.translate(-transformPoint.x, -transformPoint.y);
matrix.scale(gesture.scale, gesture.scale);
/** THIS IS WHERE THE CRASH HAPPENS **/
matrix.translate(transformPoint.x, transformPoint.y);
markerLayer.transform.matrix = matrix;
}
}
I would say that's not a good idea to work with such a large image like (9000x6000) on mobile devices.
I suppose you are trying to implement some sort of map navigation so you need to zoom some areas hugely.
My solution would be to split that 9000x6000 into 2048x2048 pieces, then compress it using png2atf utility with mipmaps enabled.
Then you can use Starling to easily load these atf images and add it to stage3d and easily manage it.
In case you are dealing with 9000x6000 image - you'll get about 15 2048x2048 pieces, having them all added on the stage at one time you might think it would be heavy, but mipmaps will make it so that there are only tiny thumbnails of image are in memory until they are not zoomed - so you'll never run out of memory in case you remove invisible pieces from stage from time to time while zooming in, and return it back on zoom out

PlayN iOS Landscape mode

My game is designed for landscape only (screen resolution 1024*768 - iPad resolution).
My game scene is rendered correctly on every platform supported by PlayN (Android, html etc). The problem with iOS only.
I have prepared all the resources for iPad screen resolution, setup info.plist file, registered platform using IOSPlatform.register(app, IOSPlatform.SupportedOrients.LANDSCAPES);
When I run my application everything regarding device orientaton is correct, but game scene isn't fully rendered. It is rendered for the resolution 768*768 (Not all of the scene objects are visible - only objects that belong to 768*768 rect are visible) and the remaining screen space is black.
I've investigated the issue in the following way:
Applied scale transform to rootLayer (to make sure the entire scene is rendered). PlayN.graphics().rootLayer().setScale(0.75f, 0.75f);
Result - game scene fits in 768*768 rect and I can see all game scene objects.
Applied translate transform to rootLayer (to make sure PlayN doesn't render scene outside of 768*768 rect). PlayN.graphics ().rootLayer().setTranslation(1024.0f - 768.0f, 0.0f);
Result - game scene is translated, but objects that are not belong to 768*768 screen rect are not visible.
My guess is that PlayN prepares its drawing context for the 768*1024 screen resolution (default iPad orientation resolution). When it renders the screen the objects located outside of 768*1024 rectangle are clipped (not rendered).
Any help or ideas what can cause such strange behavior would be very appreciated.
Thanks!
The problem is in the following:
iOS doesn't change main window frame and root view frame after device rotation (while Android does - I've checked).
PlayN code expects that view size after rotation should be changed from 768*1024 (default orientation) to 1024*768.
Actual result: PlayN transforms game scene using transform matrix but doesn't change OpenGL framebuffer size (framebuffer size is still 768*1024)
I've fixed this issue by adding the next piece of code to IOSGLContext:
#Override
public void setSize (int width, int height)
{
if (UIDeviceOrientation.LandscapeLeft == orient || UIDeviceOrientation.LandscapeRight == orient)
{
Console.WriteLine("Swap IOSGLContext width and height for " + UIDeviceOrientation.wrap(orient));
viewWidth = height;
viewHeight = width;
}
else
{
viewWidth = width;
viewHeight = height;
}
super.setSize(viewWidth, viewHeight);
}
Now everything work as expected!
If you have any other ideas how to fix this issue I will be glad to discuss them)
Thanks!
download the latest PlayN-1.5 snapshot (from -> https://github.com/threerings/playn not from google code). This issue is fixed in that version

Resources