使用下面的代码使用Sprite Kit拥有非常低的FPS (~7fps到~10 FPS)是否正常?
用例:
我画的只是从下到上的线(1024 * 64行)。我有一些增量值,它决定了每一帧的单行位置。这些行表示我的CGPath
,每个帧都分配给SKShapeNode
。没别的了。我想知道SpriteKit (也许是Swift)的性能。
你有什么改善表现的建议吗?
屏幕:
代码:
import UIKit
import SpriteKit
class SKViewController: UIViewController {
@IBOutlet weak var skView: SKView!
var scene: SKScene!
var lines: SKShapeNode!
let N: Int = 1024 * 64
var delta: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
scene = SKScene(size: skView.bounds.size)
scene.delegate = self
skView.showsFPS = true
skView.showsDrawCount = true
skView.presentScene(scene)
lines = SKShapeNode()
lines.lineWidth = 1
lines.strokeColor = .white
scene.addChild(lines)
}
}
extension SKViewController: SKSceneDelegate {
func update(_ currentTime: TimeInterval, for scene: SKScene) {
let w: CGFloat = scene.size.width
let offset: CGFloat = w / CGFloat(N)
let path = UIBezierPath()
for i in 0 ..< N { // N -> 1024 * 64 -> 65536
let x1: CGFloat = CGFloat(i) * offset
let x2: CGFloat = x1
let y1: CGFloat = 0
let y2: CGFloat = CGFloat(delta)
path.move(to: CGPoint(x: x1, y: y1))
path.addLine(to: CGPoint(x: x2, y: y2))
}
lines.path = path.cgPath
// Updating delta to simulate the changes
//
if delta > 100 {
delta = 0
}
delta += 1
}
}
谢谢并致以最良好的问候,艾芭^_^
发布于 2020-09-11 14:38:18
CPU
65536是一个相当大的数字。让CPU理解这么多循环总是会导致慢的结果。例如,即使我做了一个测试命令行项目,它只测量运行空循环所需的时间:
while true {
let date = Date().timeIntervalSince1970
for _ in 1...65536 {}
let date2 = Date().timeIntervalSince1970
print(1 / (date2 - date))
}
这将导致~17 fps。我甚至没有应用CGPath,而且它已经相当慢了。
调度队列
如果您想将游戏保持在60 use,但您的CGPath的呈现可能仍然很慢,您可以使用DispatchQueue
。
var rendering: Bool = false // remember to make this one an instance value
while true {
let date = Date().timeIntervalSince1970
if !rendering {
rendering = true
let foo = DispatchQueue(label: "Run The Loop")
foo.async {
for _ in 1...65536 {}
let date2 = Date().timeIntervalSince1970
print("Render", 1 / (date2 - date))
}
rendering = false
}
}
这保留了自然的60 you体验,您可以更新其他对象,但是SKShapeNode对象的呈现仍然相当缓慢。
GPU
如果您想加快呈现速度,我建议在GPU而不是CPU上运行它。GPU (图形处理单元)更适合这一点,并能处理巨大的循环,而不干扰游戏。这可能需要您将其编程为SKShader,其中有教程。
发布于 2020-11-01 01:03:54
检查分区的数目
没有任何iOS设备的屏幕宽度超过3000像素或1500个点(视网膜屏幕有逻辑点和物理像素,其中一个点相当于2或3个像素,取决于缩放因子;iOS适用于点,但您也必须记住像素),甚至接近的是那些在景观模式下具有最大屏幕的屏幕(iPad Pro 12.9和iPhone Pro Max)。
一个典型的设备在纵向方向将小于500点和1500像素宽。
您将此宽度划分为65536个部分,并最终得到像0.00、0.05、0.10、0.15、.、0.85这样的像素(非偶数点)坐标,这些坐标实际上将指向相同的像素20次(我的结果是在iPhone模拟器中舍入)。
您的代码在完全相同的物理位置上绘制20到60条直线,相加在一起!为什么要这么做?如果将N
设置为w
并对offset
使用1.0,则在60个FPS时将得到相同的可见结果。
重新考虑方法
尽管如此,这个实现仍然有一些缺点,即使你大大减少了每个帧要完成的工作量。不建议在update(_:)
中推进动画帧,因为在FPS上没有保证,而且您通常希望您的动画遵循设定的时间表,即在1秒内完成,而不是60帧。(如果FPS降至10,60帧动画将在6秒内完成,而1秒动画仍将在1秒内完成,但帧速率较低,即跳过帧。)
显然,动画所做的是在屏幕上绘制一个矩形,其宽度填充屏幕,其高度从0增加到100点。我要说的是,实现这一目标的一个更“标准”的方法是:
let sprite = SKSpriteNode(color: .white, size: CGSize(width: scene.size.width, height: 100.0))
sprite.yScale = 0.0
scene.addChild(sprite)
sprite.run(SKAction.repeatForever(SKAction.sequence([
SKAction.scaleY(to: 1.0, duration: 2),
SKAction.scaleY(to: 0.0, duration: 0.0)
])))
请注意,我使用SKSpriteNode
是因为据说SKShapeNode
受到了bug和性能问题的困扰,尽管人们在过去几年中报告了一些改进。
但是,如果由于某些特殊的需要,您坚持要重新绘制sprite每一个帧的整个纹理,那么这可能确实是定制着色器…的一部分。但这需要学习一种全新的方法,更不用说一种新的编程语言了。
您的着色器将在GPU 上为每个像素执行。我重复一遍:代码将对每一个像素执行--这与SpriteKit的世界大相径庭。
着色器将访问一组要处理的值,例如一个名为v_tex_coord
的变量中的标准化坐标集(在(0.00.0)到(1.01.0)之间)和u_time
中的系统时间(自着色器运行以来已经运行了几秒),它需要通过将该值存储在变量gl_FragColor
中来确定所述像素的颜色值需要- and设置。
可能是这样的:
void main() {
// default to a black color, or a three-dimensional vector v3(0.0, 0.0, 0.0):
vec3 color = vec3(0.0);
// take the fraction part of the time in seconds;
// this value will go from 0.0 to 0.9999… every second, then drop back to 0.0.
// use this to determine the relative height of the area we want to paint white:
float height = fract(u_time);
// check if the current pixel is below the height of the white area:
if (v_tex_coord.y < height) {
// if so, set it to white (a three-dimensional vector v3(1.0, 1.0, 1.0)):
color = vec3(1.0);
}
gl_FragColor = vec4(color,1.0); // the fourth dimension is the alpha
}
将其放入一个名为shader.fsh
的文件中,创建一个全屏雪碧mySprite
,并将着色器分配给它:
mySprite.shader = SKShader.init(fileNamed: "shader.fsh")
一旦你显示精灵,它的着色器将处理所有的渲染。但是,请注意,结果您的sprite将失去一些SpriteKit
功能。
https://stackoverflow.com/questions/63847138
复制相似问题