有趣的低功耗藍牙

lokikiller 8年前發布 | 9K 次閱讀 iOS開發

來自: http://www.cocoachina.com/ios/20160218/15307.html

本文由CocoaChina譯者 KingNotJustAName 翻譯

原文: Bluetooth Low Energy the Fun Way

目前很多炫酷的應用中都使用了低功耗藍牙技術,它能夠用于簡單的數據交換、支付終端以及采用iBeacon技術的用途上。但如果我們想要創建一些有趣的事情呢?比如一些非實時的簡單游戲。想象下你無需經過長時間的設置等待服務器玩家做好準備等。

每個人都知道開發優秀的多人游戲是困難的,并且多人游戲本身就很難……不過這里我展示下我在多人游戲中使用低功耗藍牙技術的一些小經驗。

它可以用于任何類型的游戲!比如策略游戲、棋盤游戲、角色扮演游戲以及競賽游戲等。我創建了一個小的demo示例來展示使用細節,但現在要關注基礎知識。

優點:

  • 很簡單!

  • 適用于任何設備。

  • 不需要配對、登錄等。只需靠近其他手機。

缺點:

  • 帶寬(一個數據包大概30字節)

  • 距離限制(適用范圍大約在20米內)

我們將會把接口類用在基于服務器和客戶端邏輯的功能擴展上(我們電話使用中心和外圍設備模式)

正如你所看到的,很簡單,一個發送,一個接收方法作為委托,并且同時接收和發送參數,我們可以在游戲中使用指令,通過這個指令我們可以識別數據包類型和數據。

現在我們需要實現服務端和客戶端的邏輯,我不想具體描述怎么在蘋果手機上設置藍牙,相反我只強調像客戶端和服務端接收和發送數據之類的方法。

KWSBluetoothLEClient

enum KWSPacketType : Int8 {

case HearBeat
case Connect
case Disconnect
case MoveUp
case MoveDown
case Jump
case Attack
case DefenseUp
case DefenseDown
case Restart
case GameEnd

}

protocol KWSBlueToothLEDelegate: class {

func interfaceDidUpdate(interface interface: KWSBluetoothLEInterface, command: KWSPacketType, data: NSData?)

}

class KWSBluetoothLEInterface: NSObject {

weak var delegate : KWSBlueToothLEDelegate?
weak var ownerViewController : UIViewController?

var interfaceConnected : Bool = false

init(ownerController : UIViewController, delegate: KWSBlueToothLEDelegate) {

    self.ownerViewController = ownerController
    self.delegate = delegate
    super.init()
}

func sendCommand(command command: KWSPacketType, data: NSData?) {

    self.doesNotRecognizeSelector(Selector(__FUNCTION__))
}

}</pre>

KWSBluetoothLEServer

class KWSBluetoothLEClient: KWSBluetoothLEInterface, CBPeripheralManagerDelegate  {

override func sendCommand(command command: KWSPacketType, data: NSData?) {

    if !interfaceConnected {

        return
    }

    var header : Int8 = command.rawValue
    let dataToSend : NSMutableData = NSMutableData(bytes: &header, length: sizeof(Int8))

    if let data = data {

        dataToSend.appendData(data)
    }

    if dataToSend.length > kKWSMaxPacketSize {

        print("Error data packet to long!")

        return
    }

    self.peripheralManager.updateValue( dataToSend,
                     forCharacteristic: self.readCharacteristic,
                  onSubscribedCentrals: nil)

}

func peripheralManager(peripheral: CBPeripheralManager, didReceiveWriteRequests requests: [CBATTRequest]) {

    if requests.count == 0 {

        return;
    }

    for req in requests as [CBATTRequest] {

        let data : NSData = req.value!
        let header : NSData = data.subdataWithRange(NSMakeRange(0, sizeof(Int8)))

        let remainingVal = data.length - sizeof(Int8)

        var body : NSData? = nil

        if remainingVal > 0 {

            body = data.subdataWithRange(NSMakeRange(sizeof(Int8), remainingVal))
        }

        let actionValue : UnsafePointer = UnsafePointer(header.bytes)
        let action : KWSPacketType = KWSPacketType(rawValue: actionValue.memory)!

        self.delegate?.interfaceDidUpdate(interface: self, command: action, data: body)

        self.peripheralManager.respondToRequest(req, withResult: CBATTError.Success)
    }

} }</pre>

在上述代碼中,發送數據和接收數據是相同的:

發送:

1、獲取原始命令值

2、保存到NSData

3、帶有額外附加指令的數據

4、發送給外圍設備/中心

接收:

1、從中心/外圍設備獲取NSData(如果需要更新請求狀態)

2、獲取第一個字節來識別命令類型

3、通過刪除第一個字節獲取子集且把它存儲為值和指令

4、獲取值的報頭字節且賦值給PacketType

5、把它發送給代理

設置:

func setupGameLogic(becomeServer:Bool) {

    self.isServer = becomeServer

    if self.isServer {

        self.communicationInterface = KWSBluetoothLEServer(ownerController: self, delegate: self)
    }
    else {

        self.communicationInterface = KWSBluetoothLEClient(ownerController: self, delegate: self)
    }

}</pre>

將數據發送到其它成員:

//player is dead notify other player
self.communicationInterface!.sendCommand(command: .GameEnd, data: nil)

//send some basic data about your player state (life, position)

let currentPlayer = self.gameScene.selectedPlayer

var packet = syncPacket()
    packet.healt = currentPlayer!.healt
    packet.posx = Float16CompressorCompress(Float32(currentPlayer!.position.x))

let packetData = NSData(bytes: &packet, length: sizeof(syncPacket))
self.communicationInterface!.sendCommand(command: .HearBeat, data: packetData)

//send some other info

let directionData = NSData(bytes: ¤tPlayer!.movingLeft, length: sizeof(Bool)) self.communicationInterface!.sendCommand(command: .MoveDown, data: directionData)</pre>

接收數據:

func interfaceDidUpdate(interface interface: KWSBluetoothLEInterface, command: KWSPacketType, data: NSData?)
{

switch( command ) {

case .HearBeat: if let data = data {

  let subData : NSData = data.subdataWithRange(NSMakeRange(0, sizeof(syncPacket)))
  let packetMemory = UnsafePointer(subData.bytes)
  let packet = packetMemory.memory

  self.gameScene.otherPlayer!.healt = packet.healt
  self.gameScene.otherPlayer!.applyDamage(0)

  let decoded = Float16CompressorDecompress(packet.posx)
  let realPos = self.gameScene.otherPlayer!.position
  let position = CGPointMake(CGFloat(decoded), CGFloat(realPos.y))

  self.gameScene.otherPlayer!.position = position

}

case .Jump: self.gameScene.otherPlayer!.playerJump()

case .Restart: self.unlockControls()

case .GameEnd: self.lockControls()

} }</pre>

我得到一些很有價值的結果:

游戲運行順利,不存在連接延遲,你可以立刻體驗了!當然它還允許你在游戲里花幾分鐘創建多個玩家。

如果你即將開始游戲開發或iOS開發之旅,或計劃創建一些簡單的支持多人玩家的SpriteKit游戲,這個選擇可能值得考慮。

示例工程可在github上找到

游戲至少需要兩個iPhone5才能測試和上手體驗。簡單打開游戲,一個玩家選擇服務端,另外一個玩家選擇客戶端模式,且保持你們的手機彼此緊挨著。連接成功時會有聲音通知提醒。

</div>

 本文由用戶 lokikiller 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!