1

I'm moving my first steps into SpriteKt and found this apparently weird behaviour of the SkSpriteNode.position property: it should be a CGPoint, but when assigned and read back, it behaves differently of any other CGPoint.

In the code that follows I have tested some randomly chosen points. Some of them can be assigned to the SkSpriteNode.position property and read back again and everything seems to work fine.

Some other, when assigned, are rounded to some other (close) value. It could be a floating-point value representation round off issue, but than comes the super weird thing: the exact same CGPoint, when assigned to another CGPoint (not to the position property, which, by the way, claims to be a CGPoint) the round off doesn't appear Does anybody knows what's going on?

check(position: CGPoint.zero)
check(position: CGPoint(x: 1, y: 1))
check(position: CGPoint(x: 100, y: 700))
check(position: CGPoint(x: -500, y: 200))
check(position: CGPoint(x: 0.5, y: 0.5))
check(position: CGPoint(x: 1.12, y: 1.14))
check(position: CGPoint(x: 1.12, y: 1.1415161718))

func check(position: CGPoint) {
    let sprite = SKSpriteNode(color: .cyan, size: CGSize(width: 32, height: 32))
    let point = position
    sprite.position = position
    print("position = \(position)")
    print("point = \(point)")
    print("sprite = \(sprite.position)")
    if sprite.position == position {
        print("✅ they match")
    } else {
        print("❌ they don't match")
    }
    assert(position == point, "assign \(position) to point result in \(point)")
}

On my system (Xcode Version 12.5 (12E262)) output is:

position = (0.0, 0.0)
point = (0.0, 0.0)
sprite = (0.0, 0.0)
✅ they match
position = (1.0, 1.0)
point = (1.0, 1.0)
sprite = (1.0, 1.0)
✅ they match
position = (100.0, 700.0)
point = (100.0, 700.0)
sprite = (100.0, 700.0)
✅ they match
position = (-500.0, 200.0)
point = (-500.0, 200.0)
sprite = (-500.0, 200.0)
✅ they match
position = (0.5, 0.5)
point = (0.5, 0.5)
sprite = (0.5, 0.5)
✅ they match
position = (1.12, 1.14)
point = (1.12, 1.14)
sprite = (1.1200000047683716, 1.1399999856948853)
❌ they don't match
position = (1.12, 1.1415161718)
point = (1.12, 1.1415161718)
sprite = (1.1200000047683716, 1.1415162086486816)
❌ they don't match
Luca
  • 303
  • 2
  • 13
  • 1
    The 1.12 and 1.14 is the classic floating point issue and the inaccuracies which go with it. My guess with your "point" variable would be that it is just storing a reference to the memory address of the CGPoint position, and not actually copying or doing anything with it. Whereas sprite.position would not be referencing the CGPoint position, but creating/copying its own version of the CGPoint position into a new section of memory. This is where the floating point issue comes in. That is my best guess anyway. – JohnL May 15 '21 at 18:38
  • thank you @JohnL for your reply! As far as I know, CGPoint should be a struct and, unless the compiler does some crazy stuff, when you assign a value type to another, in theory the contents are copied. Maybe I'm wrong, but the Swift compiler optimizes this with a copy-on-change strategy. This should hold true both for the `point` variable and for the `position` property. – Luca May 15 '21 at 18:49

1 Answers1

2

Internally the sprite is using 32-bit single-precision floats. The normal CGPoint is 64-bit double-precision. Assigning sprite.position is rounding 64-bit to 32-bit, and then for the printing and comparison it's going back from 32-bit to 64-bit. Your initial examples all happen to be numbers that are exactly representable in both 32- and 64-bits, so no loss of precision is happening there.

bg2b
  • 1,939
  • 2
  • 11
  • 18
  • Thank you @bg2b for your reply! May I ask how I could ever known that SpriteKit internally uses 32-bit floats? It doesn't seem to have much sense nowadays when all CPU are 64 bit ‍♂️ – Luca May 16 '21 at 13:34
  • 1
    Sprites are generally going to be dealt with on the GPU, which is typically optimized for 32-bit. In any case you can tell for this example since the trip through `position` is giving the same result as IEEE double to float to double. – bg2b May 17 '21 at 15:26
  • Thank you @bg2b! I have too much to learn – Luca May 19 '21 at 04:44