我可以在 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,我也提交了错误报告。
我想以编程方式更改 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,我也提交了错误报告。