我可以在 SceneKit 对象上使用 PNG 序列作为动画 material 吗?

Can I use a PNG sequence as an animated material on a SceneKit object?

我想以编程方式更改 iOS 中使用 SceneKit 渲染的对象的 material。但我想使用动画图像文件。 PNG 序列在 iOS 中适用于像 UIImageViews 这样的东西——我如何在 SceneKit 中将它们用作 material?

像这样的东西工作:

floor.firstMaterial.diffuse.contents = [UIImage animatedImageNamed:@"ANIMATION_" duration:6.0f];

谢谢!

开箱即用。

我会使用着色器修改器来做到这一点。如果将所有图像打包在单个图像中,则可以使用修改器(在 SCNShaderModifierEntryPointGeometry 入口点)根据 u_time 更新对象的纹理坐标,以便每个 t ms 另一个图像(或图集的子图像)被使用。

有一个更简单的解决方案(或者至少我认为它更简单),但我无法证明它是否更有效。我已经在 iPad mini(第 1 代)上对此进行了测试,它工作正常。

与其将作为 spritesheet 的纹理放入 material 的内容(以及 fiddle 使用着色器),不如将 SKScene 对象放置在那里。

这样您就可以在 SKScene 中使用 SKSpriteNode 对象并使用 SCNAction 对象为它们设置动画。无需接触着色器代码,即可轻松且更强大地控制所有动画。

    NSArray<SKTexture*> *cursorTextures =
    @[
      [SKTexture textureWithImageNamed:@"tilecursor1.png"],
      [SKTexture textureWithImageNamed:@"tilecursor2.png"],
      [SKTexture textureWithImageNamed:@"tilecursor3.png"],
      [SKTexture textureWithImageNamed:@"tilecursor4.png"],
      [SKTexture textureWithImageNamed:@"tilecursor5.png"]
      ];

    SKSpriteNode *cursorSprite = [SKSpriteNode spriteNodeWithTexture:cursorTextures[0]];
    cursorSprite.position = CGPointMake(cursorSprite.size.width/2.0, cursorSprite.size.height/2.0);
    [cursorSprite runAction:[SKAction repeatActionForever:[SKAction animateWithTextures:cursorTextures timePerFrame:0.1]]];

    SKScene *cursorScene = [SKScene sceneWithSize:cursorSprite.size];
    cursorScene.backgroundColor = [UIColor clearColor];
    [cursorScene addChild:cursorSprite];

    ...

    self.cursor.material.diffuse.contents = cursorScene;

Swift 4.1 / Xcode 9.3 / iOS 11.3

您可以通过使用 CADisplayLink 滚动您自己的动画来解决此问题:

将图像加载到 UIImage

数组中
var images : [UIImage] = [
    UIImage(named: "image1.png"),
    UIImage(named: "image2.png"),
    ...
]

然后,开始动画:

var animationImageFrameIndex : Double = 0
var displayLink : CADisplayLink?

func startAnimation() {
    animationImageFrameIndex = 0
    displayLink = CADisplayLink(target: self, selector: #selector(animationStep(_:)))
    displayLink!.preferredFramesPerSecond = 60
    displayLink!.add(to: .current, forMode: .defaultRunLoopMode)
}

preferredFramesPerSecond 是您希望调用 animationStep 方法所需的速率。但是,CADisplayLink 不保证它会按您要求的速率调用。

animationStep 函数由 CADisplayLink 定期调用,它实际上循环显示图像:

@objc func animationStep(_ displayLink: CADisplayLink) {
    let desiredFPS : Double = 24
    let realFPS = 1 / (displayLink.targetTimestamp - displayLink.timestamp)

    material.diffuse.contents = images[Int(animationImageFrameIndex)]
    animationImageFrameIndex += desiredFPS / realFPS
    if Int(animationImageFrameIndex) >= images.count {
        displayLink.remove(from: .current, forMode: .defaultRunLoopMode)
    }
}

在上面的方法中,desiredFPS是你实际想要在动画中使用的帧率。

Bitbucket 上有一个完整的示例项目,here

应该能够使用简单的CAKeyframeAnimation为这个(图像序列)制作动画,但经过大量测试后,很清楚那不起作用,这似乎是苹果方面的一个错误。有人打开了 Radar here,我也提交了错误报告。