【问题标题】:Constraining proportions of GUI elements in Spritekit game限制 Spritekit 游戏中 GUI 元素的比例
【发布时间】:2015-05-25 18:31:26
【问题描述】:

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

目标

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

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

"为 iPhone 6 制作@2x 大小的资源,然后 iOS 就可以了 为 iPhone 5" 自动缩小尺寸。

但对于 Spritekit 场景,这显然不是真的,或者我遗漏了一些东西。

问题

尽管 iPhone 6 和 iPhone 5 具有相同的纵横比和相同的 PPI,但使用相同的资源与场景大小相比看起来并不相同(与场景大小相比,查看第一张和第二张图像上的菜单精灵),因为 PPI与像素密度有关,iPhone 6 有更多空间(更大的对角线,更多英寸),这意味着它比 iPhone 5 有更多的像素。这就是我的问题所在,我不知道什么是有效的方法处理它。

到目前为止我做了什么

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

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

查看控制器.m

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

因此,场景在 iPhone 6 尺寸下始终具有固定大小,并且视图大小会根据设备而变化。我正在使用资产目录来启动图像,而不是 xib 文件。图像尺寸为推荐尺寸 - 4s 为 640x960 像素,5 为 640x1136 像素,6 为 750x1334 像素,6+ 模型为 1242x2208。资产的大小适合 iPhone 6,因此对于该型号,根本没有缩放。

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

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

问题

现在,正如我所说的,一切都可以在 iPhone 4s 上运行(由于宽高比不同,有两个黑条),5、6 使用 @2x 资产,但是如何使用@3x 资产让 iPhone 6+ 一切正常? 如果我为场景使用 375x667 尺寸,那么一切都定位正确并且比例很好,但质量会受到影响(因为升级@2x)

【问题讨论】:

  • 第一次添加节点时,您是否尝试过基于设备扩展节点?任何后续动画(纹理更改)都将带有预设比例因子。
  • 当然,这就是我尽可能避免的,但这当然是处理这个问题的一种选择。我只是想找到更简单/自动的解决方案,这将节省我许多计算/基于设备等手动设置比例属性。
  • 你是在缩放你的场景,还是你的场景和视图的大小相等(即没有缩放)。
  • @EpicByte 从我测试的结果来看,相同大小的场景和视图不会像第三张图片那样产生预期的效果。所以答案是否定的,如果我理解你的话,一开始我没有缩放任何东西。使用 view.bounds.size 初始化场景。但后来,我尝试在视图控制器中初始化场景时使用 CGSizeMake(375,667) 之类的东西,这给了我部分好的结果。我将在今晚晚些时候用该信息更新我的问题...

标签: ios sprite-kit size sprite universal


【解决方案1】:

统一的 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 像素,iPhone 6+ 的场景大小为 1125px x 2031px(以像素为单位的设备为实际 1080 x 1920)。

这如何处理资产?

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

@3x 呢?

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

一个例子……

Spritekit - not loading @3x images from SKTextureAtlas

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

我可以/应该如何处理 @3x 资产?

我的建议是不要担心它并运行@2x 资产。 SpriteKit 在缩放图像方面做得不错,而且有很多应用程序不支持@3x。作为 iPhone 6+ 的拥有者,这只是我现在学会的东西。我希望 Apple 尽快支持 .atlas 文件夹中的 @3x 图像。

警告

您要求除 iPhone 6 之外的所有设备缩小尺寸(并扩大 iPhone 6+)在大多数情况下,您不应该注意到您的艺术(或我的测试中的性能)有很大的不同,但作为你知道如果你缩小图像,它们看起来可能会略有不同。还有 4s 上的黑条问题,我暂时没有给你解决方案。

收盘点

如果您设置场景大小并将您的场景设置为SKSceneScaleModeAspectFill,您将在所有设备上的应用中获得完全相同的外观和感觉,但是您要求旧设备按比例缩小。就我看来,它节省了大量时间和计划,但缺点很小。

希望对您的应用有所帮助并祝您好运。

【讨论】:

  • 感谢 Skyler 的回答,是的,现在看来 iPhone 6+ 模拟器在地图集使用方面有问题。事实是,使用@2x 资产时差异并没有那么明显,但如果解决了这个问题会更好,因为当缩放高细节位图图像时,可能会出现伪影。我尝试了这些链接和许多其他链接的解决方案,但即使使用正确生成图集的 Texturepacker,或根据屏幕的比例因子选择正确的图集,iPhone 6+ 模拟器似乎也只喜欢 x2 图像。
  • 为了避免缩放过程中的伪影,我计划使用动态图像生成形式向量。首先,我使用 PaintCode 创建图像。然后导出相应的代码以生成根据需要大小的视图。最后,从此视图创建了一个 png 文件,然后在纹理上使用。
  • 其实上一版的PaintCode,2.3.2已经带了生成阶段!
【解决方案2】:

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

我的解决方案是像为 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

iPhone 6+

注意文本标签是如何正确缩小的,不仅是字体大小,还有位置。

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

- (void)viewDidLoad {
    [super viewDidLoad];

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

【讨论】:

  • 不错的解决方案。唯一的问题是您必须对每个位置使用 scaleFactorX 和 scaleFactorY 。您认为类别会使这更容易吗?
  • @sangony 你的意思是让所有图像都带有 3x 后缀吗?或者只是将它们制作成 3x 图像的大小并使用像 1x 这样的标准命名?我喜欢这个解决方案,因为看起来定位和缩放是正确的,甚至在 4s 上一切看起来都应该(没有黑条)。唯一的缺点是内存消耗,因为 3 倍资产和更多的编码,但我也会对此进行测试以查看结果。
  • @Whirlwind - 没有 3x 图像后缀。只需创建所有图像尺寸,就好像它们用于 3 倍一样。代码中的缩放负责其余部分,而没有任何明显的分辨率损失。这也允许您通过此方法使用纹理图集。
  • @SkylerLauren - 必须有一些额外的编码来实现这种兼容性。苹果最好提供一些后台功能来自动化该过程,但我并没有为此屏住呼吸。如果不是因为必须包含 iPhone 4 系列,那么每个设备只需要一个比例因子,而不是 2 (x,y)。 iPhone 4 的长度需要包含一个额外的 y 因子。你说的“类别”是什么意思?
  • @sangony 好的,我明白了。我已经对此进行了测试,这是一个可行的解决方案,因此我会对其进行投票,直到我做出最终决定使用什么。我必须看看当我使用动作时这会增加多少复杂性。我在想也许可以使用 Skyler 的解决方案,但是以您处理它的方式处理 4s。最好的解决方案是 Apple 允许开发人员按屏幕尺寸声明支持的设备,但这可能会产生一些其他问题(可能是商业问题)或谁知道呢。
猜你喜欢
  • 1970-01-01
  • 2018-04-14
  • 2022-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
相关资源
最近更新 更多