限制 Spritekit 游戏中 GUI 元素的比例

Constraining proportions of GUI elements in Spritekit game

我提前道歉,因为巨大 post,但每个尝试过制作某种通用应用程序的人都知道这是非常有问题的东西,所以请对我放轻松...

目标

我想要实现的(如上图所示)是在 iPhone 5 和 6 上使用 @2x 资产,并保持应用程序的相同外观。如果可能的话,在不根据检测到的设备手动计算节点的比例和位置属性的情况下完成所有这些……简而言之,如何实现该应用程序自动约束元素(和场景)之间的比例?我也想使用@3x 资产在 iPhone 6+ 上拥有相同外观的应用程序,但由于简单起见,我只专注于 iPhone 5 和 6.

我在网上发现,有人说这个(下采样)是由 iOS 自动完成的,例如他们建议:

"Make @2x assets at the size for iPhone 6, and then iOS will do downscaling automatically for iPhone 5".

但是当涉及到 Spritekit 场景时,这显然不是真的,或者我遗漏了一些东西。

问题

即使 iPhone 6 和 iPhone 5 具有相同的宽高比和相同的 PPI,使用相同的资产与场景大小相比看起来并不相同(请看 1 号和 1 号的菜单精灵)第二张图片与场景大小相比),因为 PPI 与像素密度有关,iPhone 6 有更多 space(对角线更大,更多英寸),这意味着它比 iPhone 5 有更多像素.这就是我的问题的来源,我不知道什么是有效的处理方法。

到目前为止我做了什么

第二张图片对于 GUI 来说不是问题,但对于游戏玩法来说,就我而言是问题,因为我希望在不同的设备上具有相同的外观和感觉。 只看第一张和第三张图片。

感谢 Skyler Lauren's 建议,我已经设法在所有 iPhone 5 台设备上,无论是在 7.1 或 8.1 系统上,还是在 iPhone 6 上,都拥有相同外观的应用程序。所以现在,问题是如何使此代码适应使用@3x 纹理的 iPhone 6+ 以及 iPhone 4s。这是 iPhone 5 和 6 的解决方案:

查看controller.m

GameScene *scene = [GameScene sceneWithSize:CGSizeMake(375,677)];//fixed instead of view.bounds.size
 scene.scaleMode = SKSceneScaleModeAspectFill;

因此场景的大小始终固定为 iPhone 6 个维度,视图大小会根据设备而变化。我正在使用资产目录作为启动图像而不是 xib 文件。图片大小为推荐尺寸 - 4s 为 640x960px,5 为 640x1136px,6 为 750x1334px,6+ 型号为 1242x2208。资产大小为 iPhone 6,因此对于该模型根本没有缩放。

与4s机型有关,我在使用上述方法时,两边各有两条黑条...

到目前为止,我只在模拟器和 iPhone 6 设备上对此进行了测试(我在设备或模拟器上看到的第一张图片看起来像)。

问题

正如我所说,现在一切都在 iPhone 4s(由于纵横比不同而有两个黑条),5、6 使用 @2x 资产,但是如何让一切都与 [=60= 一起工作] 6+ 使用@3x 资产? 如果我为场景使用 375x667 尺寸,那么一切都正确定位并且具有良好的比例,但质量会受到影响(因为放大 @2x)

统一的 GUI 和游戏玩法

据我所知,处理统一 GUI 和游戏的最佳方法是设置场景大小(无论设备如何)并让 SpriteKit 从那里缩放。

GameScene *scene = [GameScene sceneWithSize:CGSizeMake(375,677)];//fixed instead of view.bounds.size
scene.scaleMode = SKSceneScaleModeAspectFill;

这是 iPhone 6 的点数。由于 SpriteKit 以点为单位工作,但设备以像素显示,因此场景大小对于 @2x 设备为 750px x 1354px 像素,对于 [=62 设备为 1125px x 2031px =] 6+(以像素为单位的设备实际为 1080 x 1920)。

这对资产有何影响?

好吧,它对 .atlas 文件夹中的 1x 和 2x 资源非常有效。同样,因为所有内容都已转换为点,所以您可以在纹理图集中使用 button.png 和按钮@2x.png,并且所有 iPhone 的位置和外观都相同。

@3x呢?

这对 Apple 来说是一个更好的问题。显然 SpriteKit 不支持纹理图集中的 @3x 图像。 SO 上已经有一些问题试图解决这个问题。

一个例子...

Spritekit - not loading @3x images from SKTextureAtlas

它似乎也没有在 Xcode 6.2 中修复。如果您正在阅读这篇文章并想要@3x,那么可能值得向 Apple 提交雷达。需要注意的一件事是,我在文档中没有看到任何地方声称纹理图集应该支持 @3x(或者甚至 @2x)当它们被支持时,您不必对代码进行任何更改.只需将 @3x 资源放入您的 .atlas 文件夹即可。

can/should 我如何处理 @3x 资产?

我的建议是不要担心它 运行@2x 资产。 SpriteKit 在缩放图像方面做得不错,并且有很多应用程序不支持 @3x。作为一名 iPhone 6+ 的车主,我现在已经学会了忍受。希望Apple尽快支持.atlas文件夹中的@3x图片

警告

你要求所有设备缩小,但 iPhone 6 除外(并放大 iPhone 6+)在大多数情况下,你不应该注意到你的艺术有很大差异(或我测试的性能),但如您所知,如果您缩小图像,它们看起来可能会略有不同。还有就是4s的黑条问题暂时没有办法给你解决。

收盘点

如果您设置场景大小并将场景设置为 SKSceneScaleModeAspectFill,您将在所有设备上获得完全相同的应用外观和感觉,但您要求旧设备缩小。据我所知,它节省了大量时间和计划,但有一些小缺点。

希望对您有所帮助,祝您的应用程序好运。

您的主要问题似乎是处理与图像资源和屏幕对象坐标相关的各种屏幕尺寸。

我的解决方案是像编写 iPhone 6 plus 一样编写代码。将所有图像设为 @3x 大小,并将屏幕布局坐标设置为 iPhone 6 屏幕尺寸。

只需一点代码,我就能为 iPhone 6 plus、6、5 和 4 屏幕尺寸获得统一的布局。我已经包含了每一个的屏幕截图。角色图像为 300x300。 2 个按钮图像为 100x100。

static const float kIphone6PlusScaleFactorX = 1.0;
static const float kIphone6PlusScaleFactorY = 1.0;
static const float kIphone6ScaleFactorX = 0.9;
static const float kIphone6ScaleFactorY = 0.9;
static const float kIphone5ScaleFactorX = 0.772;
static const float kIphone5ScaleFactorY = 0.772;
static const float kIphone4ScaleFactorX = 0.772;
static const float kIphone4ScaleFactorY = 0.652;

#import "GameScene.h"

@implementation GameScene {
    float scaleFactorX;
    float scaleFactorY;

    SKSpriteNode *node0;
    SKSpriteNode *node1;
    SKSpriteNode *node2;
    SKLabelNode *label0;
}

-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor blackColor];

    if(view.frame.size.height == 736) {
        NSLog(@"iPhone 6 plus");
        scaleFactorX = kIphone6PlusScaleFactorX;
        scaleFactorY = kIphone6PlusScaleFactorY;
    }
    if(view.frame.size.height == 667) {
        NSLog(@"iPhone 6");
        scaleFactorX = kIphone6ScaleFactorX;
        scaleFactorY = kIphone6ScaleFactorY;
    }
    if(view.frame.size.height == 568) {
        NSLog(@"iPhone 5");
        scaleFactorX = kIphone5ScaleFactorX;
        scaleFactorY = kIphone5ScaleFactorY;
    }
    if(view.frame.size.height == 480) {
        NSLog(@"iPhone 4");
        scaleFactorX = kIphone4ScaleFactorX;
        scaleFactorY = kIphone4ScaleFactorY;
    }

    node0 = [SKSpriteNode spriteNodeWithImageNamed:@"Pic"];
    node0.position = CGPointMake(self.size.width/2, self.size.height/2);
    [node0 setScale:scaleFactorX];
    [self addChild:node0];

    node1 = [SKSpriteNode spriteNodeWithImageNamed:@"button0"];
    node1.position = CGPointMake(100*scaleFactorX, 100*scaleFactorY);
    [node1 setScale:scaleFactorX];
    [self addChild:node1];

    node2 = [SKSpriteNode spriteNodeWithImageNamed:@"button1"];
    node2.position = CGPointMake(314*scaleFactorX, 100*scaleFactorY);
    [node2 setScale:scaleFactorX];
    [self addChild:node2];

    label0 = [SKLabelNode labelNodeWithFontNamed:@"HelveticaNeue-Bold"];
    label0.text = @"Big Game Menu";
    label0.fontSize = 48*scaleFactorX;
    label0.fontColor = [SKColor whiteColor];
    label0.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
    label0.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
    label0.position = CGPointMake(207*scaleFactorX,690*scaleFactorY);
    [self addChild:label0];
}

iPhone 4

iPhone 5

iPhone 6

iPhone6+

请注意,文本标签不仅按字体大小而且按位置正确缩小。

供您参考,我确实在我的 GameViewController 中使用了标准代码,因为我发现使用更简单的版本更容易。这是我用来展示我的 SKView 的代码:

- (void)viewDidLoad {
    [super viewDidLoad];

    SKView * skView = (SKView *)self.view;
    SKScene *scene = [GameScene sceneWithSize:skView.bounds.size];
    scene.scaleMode = SKSceneScaleModeAspectFill;
    [skView presentScene:scene];
}