0
votes

I am developing a turn-based game with swift, where everything has to be converted to NSData. Right now i have been stuck on the "NS-stuff" for many days. Because CGPoint is not an object but a struct, it can't be converted to NSData directly, and so do the CGFloat inside CGPoint. What i am trying to do is: (Just an idea, not sure if it works yet.)

  1. convert CGPoint to 2 different Floats
  2. create NSNummer with Float
  3. create NSData with NSNumber
  4. convert NSData back to NSNumber in another device
  5. convert NSNumber to Float
  6. merge them back to CGPoint

This way is too complicated. Is there any better way to implement it or any best practices? Thanks in advance!!!

PS: NSKeyedArchiver should NOT be considered because of it's performance. (Not sure!)

2
Although you may think of it as 'way too complicated' and others kindly proposed alternatives, the solution you describe is fine.user234736

2 Answers

4
votes

The simplest way to encode CGPoint is using NSStringFromCGPoint and CGPointFromString. This gets rid of all 32/64-bit issues that you would otherwise face.

Transport of this is very cheap (less than one MTU, so it doesn't really matter). The only thing that might be expensive is the string parsing, which is almost certainly overwhelmed by the cost of putting it on the wire.

But if you have a very tight network protocol and need the best performance, I would transform the CGPoint into a (Float32, Float32), or better yet, an (Int32, Int32) if you don't actually need non-integral values. That's easy to encode as @robmayoff suggests, on the assumption that you don't care about endianness. With Int, you can easily, though, use Int.bigEndian and Int(bigEndian:) to deal with this.

1
votes

Note: if you don't need high performance, you're probably better off with Rob Napier's suggestion (NSStringFromCGPoint/CGPointFromString), as it is simpler and easier to debug. However, many games have high performance requirements and need more than a single CGPoint per game loop. Those are my assumptions here.

Assuming CGFloat is the same type (32-bit or 64-bit) on both the sender and receiver:

var pointToSend = CGPointMake(100, 200)
let data = NSData(bytes: &pointToSend, length: MemoryLayout<CGPoint>.size)
// send data to other device

// receive data on other device
if (data.length == MemoryLayout<CGPoint>.size) {
    var receivedPoint = UnsafePointer<CGPoint>(data.bytes).memory
} else {
    // error
}

If the receiver's CGFloat might be different from the sender's:

typealias DoublePoint = (x: Double, y: Double)

// sender
var pointToSend = CGPointMake(100, 200)
var pairToSend = (Double(pointToSend.x), Double(pointToSend.y))
let data = NSData(bytes: &pointToSend, length: MemoryLayout.size(ofValue: pairToSend))

// receiver
if (data.length == MemoryLayout<DoublePoint>.size) {
    var receivedPair = UnsafePointer<DoublePoint>(data.bytes).memory
    var receivedPoint = CGPointMake(CGFloat(receivedPair.x), CGFloat(receivedPair.y))
} else {
    // error
}