你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 有趣的低功耗藍牙

有趣的低功耗藍牙

編輯:IOS開發基礎

012.jpg

本文由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__))
    }
}

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)
        }
  }
}

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

發送:

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)
        }

}

將數據發送到其它成員:

//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)

接收數據:

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()

  }
}

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

multi_btle.gif

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

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

示例工程可在github上找到。

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


  • 更多譯者翻譯文章,請查看:http://www.cocoachina.com/special/translation/

  • 感謝博文視點對本期活動的支持

博文logo-01.jpg

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved