1

I am currently working on a SpriteKit game written in Swift 3.

When I tried generating SKShapeNodes from points stored in a multidimensional array, using the init(splinePoints: UnsafeMutablePointer, count: Int) initialiser, I found that I just was not able to use implicit bridging feature of Swift, namely by adding a "&" in front of the var in question, to hand it to the function as an UnsafeMutablePointer.

Here is an image of the reproduction of the bug, including the error, in Xcode Playgrounds:

Reproduction of bug in Playground

The error reads

Cannot convert value of type '[CGPoint]' to expected argument type 'CGPoint'

, although the initialiser reference explicitly states that it wants a pointer to the CGPoint array. Even in the Apple provided example, section

Creating Shape Nodes from Points, it is done exactly that way:

var points = [CGPoint(x: 0, y: 0),               
              CGPoint(x: 100, y: 100),               
              CGPoint(x: 200, y: -50),               
              CGPoint(x: 300, y: 30),               
              CGPoint(x: 400, y: 20)]         
let linearShapeNode = SKShapeNode(points: &points,                                   
                                  count: points.count)          
let splineShapeNode = SKShapeNode(splinePoints: &points,                                   
                                  count: points.count)

So my question is why it is not working, whether it is a compiler bug, and if not, how I can fix it.

Please note, however, that creating a copy of the array inside the function, like this...

func createPressurePointShape(arm: Int, finger: Int) {
    var tempPoints = pointsArray[arm][finger]
    let shapeNode = SKShapeNode.init(splinePoints: &tempPoints, count: pointsArray[arm][finger].count)
}

...is not a solution for me, because in my actual application the variable would only exist for 1 frame, and either be gone or create a memory leak afterwards. Just the fact that this lets the error go away though hints towards a bug in Swift or Xcode rather than my app, but I am unsure about it.

Thank you for your help in advance. Here's the code example, if somebody would like to play around with it:

//: Playground - noun: a place where people can play

import UIKit
import SpriteKit

let armCount = 4
let fingersPerArm = 4
let pressurePointsPerFinger = 3

private var pointsArray = [[[CGPoint]]](repeating: [], count: Int(armCount))

for armIndex in 0...Int(armCount - 1) {

    for fingerIndex in 0...(fingersPerArm - 1) {

        pointsArray[armIndex].append([])
        for innerIndex in 0..<pressurePointsPerFinger {
            pointsArray[armIndex][fingerIndex].append(CGPoint(x: 0.5, y: 0.5))
        }
    }
}

let exampleArm = 1
let exampleFinger = 1

func createPressurePointShape(arm: Int, finger: Int) {
    let shapeNode = SKShapeNode.init(splinePoints: &pointsArray[arm][finger], count: pointsArray[arm][finger].count)
}

createPressurePointShape(arm: exampleArm, finger: exampleFinger)
4

3 回答 3

2

你说得对,这确实看起来像一个错误。我已经在这里提交了一份报告。一个更简单的例子是:

func foo(_ ptr: UnsafeMutablePointer<Int>) {}

var arr = Array(repeating: Array(0 ..< 5), count: 5)

let i = 0
foo(&arr[i]) // Cannot convert value of type '[Int]' to expected argument type 'Int'

但是,我会警惕传递&pointsArray[arm][finger][0]SKShapeNode' 初始化程序的建议——它恰好可以工作,因为编译器只是传递数组中第一个元素的地址,但我不相信这是有保证的。编译器将第一个元素复制到临时文件中,传递该临时文件的地址,然后再写回数组也是同样有效的。这将在初始化程序中调用未定义的行为

相反,我将只创建一个便利的初始化程序SKShapeNode,以便首先避开错误并使用更好的 API:

extension SKShapeNode {

    convenience init(splinePoints: inout [CGPoint]) {
        self.init(splinePoints: &splinePoints, count: splinePoints.count)
    }
}

func createPressurePointShape(arm: Int, finger: Int) {
    let shapeNode = SKShapeNode(splinePoints: &pointsArray[arm][finger])
}

inout尽管我不相信传递的内存曾经发生过变异,但仍然不能完全令人满意(这确实是 's API 的错误)SKShapeNode。但是,如果inout被取走并且将临时可变变量传递给init(splinePoints:count:),则将始终首先复制数组的缓冲区(因为它需要唯一引用),这可能是不可取的。

于 2017-10-08T22:30:42.230 回答
1

尝试像这样指向数组的第一个元素。

var finger = [CGPoint(x: 0.5, y: 0.5), CGPoint(x: 0.5, y: 0.5), CGPoint(x: 0.5, y: 0.5)]
var arm = [finger, finger, finger, finger]
var pointsArray = [arm, arm, arm, arm]

let exampleArm = 1
let exampleFinger = 1

func createPressurePointShape(arm: Int, finger: Int) {
  let shapeNode = SKShapeNode.init(splinePoints: &pointsArray[arm][finger][0], count: pointsArray[arm][finger].count)
}

createPressurePointShape(arm: exampleArm, finger: exampleFinger)
于 2017-10-08T17:27:31.240 回答
0

您可能缺少压力计数(每根手指),这是一个伪代码:

let examplePressuePointsPerFinger = 1

func createPressurePointShape(arm: Int, finger: Int, pressurePoint: Int) {
    let shapeNode = SKShapeNode.init(splinePoints: &pointsArray[arm][finger][pressurePoint], count: pointsArray[arm][finger].count)
}

createPressurePointShape(arm: exampleArm, finger: exampleFinger, pressurePoint: examplePressuePointsPerFinger)
于 2017-10-08T17:23:42.913 回答