概要
Cocos2d-xでParrot社MAMBOの制御を行ってみました。
このMAMBOはiOS、Android用のSDKが提供されているので、
比較的簡単に飛行制御することが出来ます。
今回は、Cocos2d-x + iOS環境で、このドローンを制御してみたいと思います。
MAMBOの接続はBlueToothを使って行います。
環境
開発環境は以下の通りです。
- Cocos2d-x 3.13
- XCode 8.2.1
- iPod Touch 第6世代
インストール
ダウンロードしてきたSDKをiOSフォルダに保存してください。
proj.ios_mac
└iOS
└Framework
└ARSDK3_iOS_3_11_0
├include
└lib
Other Linker Flagsは
-ObjC
を忘れず、その他に必要なライブラリを記述していきます。
-larcommands -larcontroller -lardiscovery -larnetwork -larnetworkal -larsal -larstream -larstream2 -larmavlink -ljson -larmedia -larutils -lcurl -lardatatransfer -lmux -lpomp -lcrypto -lssl -lz
ExternalAccessory.framework
も忘れずに追加してください。
プログラミング
ドローン検索用のViewContorollerを作成します。
#import <UIKit/UIKit.h>
@interface DeviceListVC : UIViewController
+(DeviceListVC*) sharedInstance;
@property BOOL isConnect;
@end
#import "DeviceListVC.h"
#import "DroneDiscoverer.h"
#import <libARDiscovery/ARDISCOVERY_BonjourDiscovery.h>
#import "MiniDrone.h"
@interface DeviceListVC () <DroneDiscovererDelegate>
@property (nonatomic, strong) NSArray *dataSource;
@property (nonatomic, strong) DroneDiscoverer *droneDiscoverer;
@property (nonatomic, strong) ARService *selectedService;
@end
static DeviceListVC *deveiceListHelper;
@implementation DeviceListVC
+(DeviceListVC*) sharedInstance
{
@synchronized(self)
{
if (deveiceListHelper == nil)
{
deveiceListHelper = [[DeviceListVC alloc] init];
}
return deveiceListHelper;
}
// to avoid compiler warning
return nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
_dataSource = [NSArray array];
_droneDiscoverer = [[DroneDiscoverer alloc] init];
[_droneDiscoverer setDelegate:self];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self registerNotifications];
[_droneDiscoverer startDiscovering];
}
- (void) viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self unregisterNotifications];
[_droneDiscoverer stopDiscovering];
}
#pragma mark DroneDiscovererDelegate
- (void)droneDiscoverer:(DroneDiscoverer *)droneDiscoverer didUpdateDronesList:(NSArray *)dronesList {
if (_isConnect) return;
_dataSource = dronesList;
[dronesList enumerateObjectsUsingBlock:^(ARService *service, NSUInteger idx, BOOL * _Nonnull stop)
{
if (ARDISCOVERY_PRODUCT_MINIDRONE_DELOS3 == service.product) {
if (!_isConnect) {
// MAMBOドローン初期化
[[MiniDrone sharedInstance] initWithService:service];
[[MiniDrone sharedInstance] connect];
_isConnect = YES;
}
}
}];
}
#pragma mark notification registration
- (void)registerNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enteredBackground:) name: UIApplicationDidEnterBackgroundNotification object: nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enterForeground:) name: UIApplicationWillEnterForegroundNotification object: nil];
}
- (void)unregisterNotifications {
[[NSNotificationCenter defaultCenter] removeObserver:self name: UIApplicationDidEnterBackgroundNotification object: nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name: UIApplicationWillEnterForegroundNotification object: nil];
}
#pragma mark - application notifications
- (void)enterForeground:(NSNotification*)notification {
[_droneDiscoverer startDiscovering];
}
- (void)enteredBackground:(NSNotification*)notification {
[_droneDiscoverer stopDiscovering];
_isConnect = NO;
}
@end
次にドローンを見つけるための処理
#import <Foundation/Foundation.h>
@class DroneDiscoverer;
@protocol DroneDiscovererDelegate<NSObject>
/**
* Called when the device found list is updated
* Called on the main thread
* @param droneDiscoverer the drone discoverer concerned
* @param dronesList the list of found ARService
*/
- (void)droneDiscoverer:(DroneDiscoverer*)droneDiscoverer didUpdateDronesList:(NSArray*)dronesList;
@end
@interface DroneDiscoverer : NSObject
@property (nonatomic, weak) id<DroneDiscovererDelegate> delegate;
- (void)startDiscovering;
- (void)stopDiscovering;
@end
#import "DroneDiscoverer.h"
#import <libARDiscovery/ARDISCOVERY_BonjourDiscovery.h>
@implementation DroneDiscoverer
- (void)setDelegate:(id<DroneDiscovererDelegate>)delegate {
_delegate = delegate;
if (_delegate && [_delegate respondsToSelector:@selector(droneDiscoverer:didUpdateDronesList:)]) {
[_delegate droneDiscoverer:self didUpdateDronesList:[[ARDiscovery sharedInstance] getCurrentListOfDevicesServices]];
}
}
- (void)startDiscovering {
[self registerNotifications];
[[ARDiscovery sharedInstance] start];
}
- (void)stopDiscovering {
[[ARDiscovery sharedInstance] stop];
[self unregisterNotifications];
}
#pragma mark notification registration
- (void)registerNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(discoveryDidUpdateServices:) name:kARDiscoveryNotificationServicesDevicesListUpdated object:nil];
}
- (void)unregisterNotifications {
[[NSNotificationCenter defaultCenter] removeObserver:self name:kARDiscoveryNotificationServicesDevicesListUpdated object:nil];
}
#pragma mark ARDiscovery notification
- (void)discoveryDidUpdateServices:(NSNotification *)notification {
// reload the data in the main thread
dispatch_async(dispatch_get_main_queue(), ^{
if (_delegate && [_delegate respondsToSelector:@selector(droneDiscoverer:didUpdateDronesList:)]) {
[_delegate droneDiscoverer:self didUpdateDronesList:[[notification userInfo] objectForKey:kARDiscoveryServicesList]];
}
});
}
@end
AppController.mmに先程作成したViewControllerを呼び出します。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// Add the view controller's view to the window and display.
window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
// Use RootViewController to manage CCEAGLView
_viewController = [[RootViewController alloc]init];
_viewController.wantsFullScreenLayout = YES;
[_viewController.view addSubview: [DeviceListVC sharedInstance].view];
}
ドローン制御用プログラムです
#import <Foundation/Foundation.h>
#import <libARController/ARController.h>
#import <libARCommands/ARCommands.h>
#import <libARDiscovery/ARDISCOVERY_BonjourDiscovery.h>
@class MiniDrone;
@protocol MiniDroneDelegate <NSObject>
@required
/**
* Called when the connection to the drone did change
* Called on the main thread
* @param miniDrone the drone concerned
* @param state the state of the connection
*/
- (void)miniDrone:(MiniDrone*)miniDrone connectionDidChange:(eARCONTROLLER_DEVICE_STATE)state;
/**
* Called when the battery charge did change
* Called on the main thread
* @param MiniDrone the drone concerned
* @param batteryPercent the battery remaining (in percent)
*/
- (void)miniDrone:(MiniDrone*)miniDrone batteryDidChange:(int)batteryPercentage;
/**
* Called when the piloting state did change
* Called on the main thread
* @param miniDrone the drone concerned
* @param batteryPercent the piloting state of the drone
*/
- (void)miniDrone:(MiniDrone*)miniDrone flyingStateDidChange:(eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE)state;
/**
* Called before medias will be downloaded
* Called on the main thread
* @param miniDrone the drone concerned
* @param nbMedias the number of medias that will be downloaded
*/
- (void)miniDrone:(MiniDrone*)miniDrone didFoundMatchingMedias:(NSUInteger)nbMedias;
/**
* Called each time the progress of a download changes
* Called on the main thread
* @param miniDrone the drone concerned
* @param mediaName the name of the media
* @param progress the progress of its download (from 0 to 100)
*/
- (void)miniDrone:(MiniDrone*)miniDrone media:(NSString*)mediaName downloadDidProgress:(int)progress;
/**
* Called when a media download has ended
* Called on the main thread
* @param miniDrone the drone concerned
* @param mediaName the name of the media
*/
- (void)miniDrone:(MiniDrone*)miniDrone mediaDownloadDidFinish:(NSString*)mediaName;
@end
@interface MiniDrone : NSObject
@property (nonatomic, weak) id<MiniDroneDelegate>delegate;
+ (MiniDrone*) sharedInstance;
- (id)initWithService:(ARService*)service;
- (eARCONTROLLER_DEVICE_STATE)connectionState;
- (eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE)flyingState;
- (uint8_t)batteryLevel;
- (BOOL)takeOff;
- (BOOL)land;
- (void)setPitch:(uint8_t)pitch;
- (void)setRoll:(uint8_t)roll;
- (void)setYaw:(uint8_t)yaw;
- (void)setGaz:(uint8_t)gaz;
- (void)setFlag:(uint8_t)flag;
- (ARService*)getService;
@end
@interface MiniDrone ()<MiniDroneDelegate>
@property (nonatomic, assign) ARCONTROLLER_Device_t *deviceController;
@property (nonatomic, assign) ARCOMMANDS_Decoder_t *commandsDecoder;
@property (nonatomic, strong) ARService *service;
@property (nonatomic, assign) eARCONTROLLER_DEVICE_STATE connectionState;
@property (nonatomic, assign) eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE flyingState;
@property (nonatomic, assign) uint8_t batteryLevel;
@end
@implementation MiniDrone
+ (MiniDrone *)sharedInstance
{
static MiniDrone *drone = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^ {
drone = [[MiniDrone alloc] init];
});
return drone;
}
-(id)initWithService:(ARService *)service
{
self = [super init];
if (self) {
_service = service;
_flyingState = ARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_LANDED;
}
return self;
}
- (void)dealloc
{
if (_deviceController) {
ARCONTROLLER_Device_Delete(&_deviceController);
}
}
- (void)connect {
if (!_deviceController) {
// call createDeviceControllerWithService in background
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// if the product type of the service matches with the supported types
eARDISCOVERY_PRODUCT product = _service.product;
eARDISCOVERY_PRODUCT_FAMILY family = ARDISCOVERY_getProductFamily(product);
if (family == ARDISCOVERY_PRODUCT_FAMILY_MINIDRONE) {
// create the device controller
[self createDeviceControllerWithService:_service];
}
});
} else {
ARCONTROLLER_Device_Start (_deviceController);
}
}
- (void)disconnect {
ARCONTROLLER_Device_Stop (_deviceController);
}
- (eARCONTROLLER_DEVICE_STATE)connectionState {
return _connectionState;
}
- (eARCOMMANDS_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE)flyingState {
return _flyingState;
}
- (uint8_t)batteryLevel
{
return _batteryLevel;
}
- (void)createDeviceControllerWithService:(ARService*)service {
// first get a discovery device
ARDISCOVERY_Device_t *discoveryDevice = [self createDiscoveryDeviceWithService:service];
if (discoveryDevice != NULL) {
eARCONTROLLER_ERROR error = ARCONTROLLER_OK;
eARCOMMANDS_DECODER_ERROR errorDecoder = ARCOMMANDS_DECODER_OK;
// create the device controller
_deviceController = ARCONTROLLER_Device_New (discoveryDevice, &error);
_commandsDecoder = ARCOMMANDS_Decoder_NewDecoder(&errorDecoder);
// add the state change callback to be informed when the device controller starts, stops...
if (error == ARCONTROLLER_OK) {
error = ARCONTROLLER_Device_AddStateChangedCallback(_deviceController, stateChanged, (__bridge void *)(self));
}
// add the command received callback to be informed when a command has been received from the device
if (error == ARCONTROLLER_OK) {
error = ARCONTROLLER_Device_AddCommandReceivedCallback(_deviceController, onCommandReceived, (__bridge void *)(self));
}
// start the device controller (the callback stateChanged should be called soon)
if (error == ARCONTROLLER_OK) {
error = ARCONTROLLER_Device_Start (_deviceController);
}
// we don't need the discovery device anymore
ARDISCOVERY_Device_Delete (&discoveryDevice);
// if an error occured, inform the delegate that the state is stopped
if (error != ARCONTROLLER_OK) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate miniDrone:self connectionDidChange:ARCONTROLLER_DEVICE_STATE_STOPPED];
});
}
} else {
// if an error occured, inform the delegate that the state is stopped
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate miniDrone:self connectionDidChange:ARCONTROLLER_DEVICE_STATE_STOPPED];
});
}
}
- (ARDISCOVERY_Device_t *)createDiscoveryDeviceWithService:(ARService*)service {
ARDISCOVERY_Device_t *device = NULL;
eARDISCOVERY_ERROR errorDiscovery = ARDISCOVERY_OK;
device = ARDISCOVERY_Device_New (&errorDiscovery);
if (errorDiscovery == ARDISCOVERY_OK) {
// get the ble service from the ARService
ARBLEService* bleService = service.service;
// create a BLE discovery device
errorDiscovery = ARDISCOVERY_Device_InitBLE (device, service.product, (__bridge ARNETWORKAL_BLEDeviceManager_t)(bleService.centralManager), (__bridge ARNETWORKAL_BLEDevice_t)(bleService.peripheral));
}
if (errorDiscovery != ARDISCOVERY_OK) {
NSLog(@"Discovery error :%s", ARDISCOVERY_Error_ToString(errorDiscovery));
ARDISCOVERY_Device_Delete(&device);
}
return device;
}
- (ARCOMMANDS_Decoder_t*) createCommandsDecoder {
return nil;
}
- (void)createSDCardModule {
}
#pragma mark commands
// 離陸
- (BOOL)takeOff {
if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
_deviceController->miniDrone->sendPilotingTakeOff(_deviceController->miniDrone);
return YES;
}
return NO;
}
// 着陸
- (BOOL)land {
if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
_deviceController->miniDrone->sendPilotingLanding(_deviceController->miniDrone);
return YES;
}
return NO;
}
- (void)setPitch:(uint8_t)pitch {
if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
_deviceController->miniDrone->setPilotingPCMDPitch(_deviceController->miniDrone, pitch);
}
}
- (void)setRoll:(uint8_t)roll {
if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
_deviceController->miniDrone->setPilotingPCMDRoll(_deviceController->miniDrone, roll);
}
}
- (void)setYaw:(uint8_t)yaw {
if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
_deviceController->miniDrone->setPilotingPCMDYaw(_deviceController->miniDrone, yaw);
}
}
- (void)setGaz:(uint8_t)gaz {
if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
_deviceController->miniDrone->setPilotingPCMDGaz(_deviceController->miniDrone, gaz);
}
}
- (void)setFlag:(uint8_t)flag {
if (_deviceController && (_connectionState == ARCONTROLLER_DEVICE_STATE_RUNNING)) {
_deviceController->miniDrone->setPilotingPCMDFlag(_deviceController->miniDrone, flag);
}
}
#pragma mark Device controller callbacks
// called when the state of the device controller has changed
static void stateChanged (eARCONTROLLER_DEVICE_STATE newState, eARCONTROLLER_ERROR error, void *customData) {
MiniDrone *miniDrone = (__bridge MiniDrone*)customData;
if (miniDrone != nil) {
switch (newState) {
case ARCONTROLLER_DEVICE_STATE_RUNNING:
break;
case ARCONTROLLER_DEVICE_STATE_STOPPED:
break;
default:
break;
}
dispatch_async(dispatch_get_main_queue(), ^{
miniDrone.connectionState = newState;
[miniDrone.delegate miniDrone:miniDrone connectionDidChange:newState];
});
}
}
// ドローンから送られてくる情報を取得します
static void onCommandReceived (eARCONTROLLER_DICTIONARY_KEY commandKey, ARCONTROLLER_DICTIONARY_ELEMENT_t *elementDictionary, void *customData) {
MiniDrone *miniDrone = (__bridge MiniDrone*)customData;
NSLog(@"onCommandReceived=%u %u", commandKey, ARCONTROLLER_DICTIONARY_KEY_MINIDRONE_USBACCESSORYSTATE_GUNSTATE);
// バッテリー残量
if ((commandKey == ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED) &&
(elementDictionary != NULL)) {
ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
if (element != NULL) {
HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_COMMON_COMMONSTATE_BATTERYSTATECHANGED_PERCENT, arg);
if (arg != NULL) {
uint8_t battery = arg->value.U8;
dispatch_async(dispatch_get_main_queue(), ^{
[miniDrone.delegate miniDrone:miniDrone batteryDidChange:battery];
miniDrone.batteryLevel = battery;
});
}
}
}
// 飛行状態
else if ((commandKey == ARCONTROLLER_DICTIONARY_KEY_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED) &&
(elementDictionary != NULL)) {
ARCONTROLLER_DICTIONARY_ARG_t *arg = NULL;
ARCONTROLLER_DICTIONARY_ELEMENT_t *element = NULL;
HASH_FIND_STR (elementDictionary, ARCONTROLLER_DICTIONARY_SINGLE_KEY, element);
if (element != NULL) {
HASH_FIND_STR (element->arguments, ARCONTROLLER_DICTIONARY_KEY_MINIDRONE_PILOTINGSTATE_FLYINGSTATECHANGED_STATE, arg);
if (arg != NULL) {
miniDrone.flyingState = arg->value.I32;
dispatch_async(dispatch_get_main_queue(), ^{
[miniDrone.delegate miniDrone:miniDrone flyingStateDidChange:miniDrone.flyingState];
});
}
}
}
}
- (ARService*)getService
{
return _service;
}
static void onChargeRate(eARCOMMANDS_COMMON_CHARGERSTATE_CURRENTCHARGESTATECHANGED_STATUS status, eARCOMMANDS_COMMON_CHARGERSTATE_CURRENTCHARGESTATECHANGED_PHASE phase, void *custom) {
NSLog(@"onChargeRate=%d", status);
}
@end
呼び出し
Cocos2d-xから呼び出す為のブリッジ処理です。
#include "cocos2d.h"
using namespace cocos2d;
class MiniDroneBridge;
using MiniDroneBridgeCompletionHandler = std::function<void (bool isSuccess)>;
using MiniDroneBridgeBatteryLevelHandler = std::function<void (int level)>;
using MiniDroneBridgeStatHandler = std::function<void (int stat)>;
using MiniDroneBridgeValueHandler = std::function<void (int val)>;
class MiniDroneBridge
{
public :
static void takeOff(const MiniDroneBridgeCompletionHandler handler);
static void land(const MiniDroneBridgeCompletionHandler handler);
static void setGazUD(int val);
static void setYawLR(int val);
static void setPitchFB(int val);
static void setRollLR(int val);
static void connect();
static void disconnect();
static void dispBatteryLevel(const MiniDroneBridgeBatteryLevelHandler handler);
static void dispConnectStat(const MiniDroneBridgeStatHandler handler);
static void dispFlyingStat(const MiniDroneBridgeStatHandler handler);
};
#import "MiniDrone.h"
#import "MiniDroneBridge.h"
// 離陸
void MiniDroneBridge::takeOff(const MiniDroneBridgeCompletionHandler handler)
{
if ([[MiniDrone sharedInstance] takeOff])
{
handler(true);
} else {
handler(false);
}
}
// 着陸
void MiniDroneBridge::land(const MiniDroneBridgeCompletionHandler handler)
{
if ([[MiniDrone sharedInstance] land])
{
handler(true);
} else {
handler(false);
}
}
// 上昇/下降
void MiniDroneBridge::setGazUD(int val)
{
[[MiniDrone sharedInstance] setGaz:val];
}
// ドローンの向きを左右に変える
void MiniDroneBridge::setYawLR(int val)
{
[[MiniDrone sharedInstance] setYaw:val];
}
void MiniDroneBridge::setPitchFB(int val)
{
// 前進/後退
if (val == 0) {
[[MiniDrone sharedInstance] setFlag:0];
} else {
[[MiniDrone sharedInstance] setFlag:1];
}
[[MiniDrone sharedInstance] setPitch:val];
}
void MiniDroneBridge::setRollLR(int val)
{
// 左移動、右移動
if (val == 0) {
[[MiniDrone sharedInstance] setFlag:0];
} else {
[[MiniDrone sharedInstance] setFlag:1];
}
[[MiniDrone sharedInstance] setRoll:val];
}
// バッテリー残数取得
void MiniDroneBridge::dispBatteryLevel(const MiniDroneBridgeBatteryLevelHandler handler)
{
handler([[MiniDrone sharedInstance] batteryLevel]);
}
// 接続状態取得
void MiniDroneBridge::dispConnectStat(const MiniDroneBridgeStatHandler handler)
{
handler([[MiniDrone sharedInstance] connectionState]);
}
// 飛行状態取得
void MiniDroneBridge::dispFlyingStat(const MiniDroneBridgeStatHandler handler)
{
handler([[MiniDrone sharedInstance] flyingState]);
}
void MiniDroneBridge::connect()
{
[[MiniDrone sharedInstance] connect];
}
void MiniDroneBridge::disconnect()
{
[[MiniDrone sharedInstance] disconnect];
}
離陸/着陸処理
離陸と着陸を行うために、画面にボタンを設置します。
#include "cocos2d.h"
#define WINSIZE Director::getInstance()->getWinSize()
#define WINCENTER Point(WINSIZE.width*0.5, WINSIZE.height*0.5)
class HelloWorld : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
static HelloWorld* sharedScene(void);
virtual bool init();
CREATE_FUNC(HelloWorld);
private:
CC_SYNTHESIZE(MenuItemSprite*, _btnTakeOFF, BtnTakeOFF);
CC_SYNTHESIZE(MenuItemSprite*, _btnLand, BtnLand);
CC_SYNTHESIZE(Label*, _lblBattery, LblBattery);
CC_SYNTHESIZE(bool, _connectStat, ConnectStat);
void onEnterTransitionDidFinish();
void update(float dt);
void takeOffTapped(MenuItemSprite* sender);
void landTapped(MenuItemSprite* sender);
};
#include "HelloWorldScene.h"
#include "MiniDroneBridge.h"
USING_NS_CC;
static HelloWorld *g_Scene;
HelloWorld* HelloWorld::sharedScene() {
return g_Scene;
}
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
g_Scene = HelloWorld::create();
// add layer as a child to scene
scene->addChild(g_Scene);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
_connectStat = false;
// TakeOFF
Sprite* btnTakeOff_ON = Sprite::create();
btnTakeOff_ON->setTextureRect(Rect(0, 0, 200, 100));
btnTakeOff_ON->setColor(Color3B::GRAY);
Sprite* btnTakeOff_OFF = Sprite::create();
btnTakeOff_OFF->setTextureRect(Rect(0, 0, 200, 100));
btnTakeOff_OFF->setColor(Color3B(0,0,200));
_btnTakeOFF = MenuItemSprite::create(btnTakeOff_OFF,
btnTakeOff_ON,
[this](Ref* sender) {this->takeOffTapped((MenuItemSprite*)sender);});
Label* lblTakeOff = Label::createWithSystemFont("離陸", "arial", 26);
lblTakeOff->enableOutline(Color4B::BLACK, 1);
lblTakeOff->setPosition(Vec2(_btnTakeOFF->getContentSize().width*0.5, _btnTakeOFF->getContentSize().height*0.5));
_btnTakeOFF->addChild(lblTakeOff);
_btnTakeOFF->setPosition(Vec2(WINCENTER.x, _btnTakeOFF->getContentSize().height*0.5));
_btnTakeOFF->setVisible(false);
// Land
Sprite* btnLand_ON = Sprite::create();
btnLand_ON->setTextureRect(Rect(0, 0, 200, 100));
btnLand_ON->setColor(Color3B::GRAY);
Sprite* btnLand_OFF = Sprite::create();
btnLand_OFF->setTextureRect(Rect(0, 0, 200, 100));
btnLand_OFF->setColor(Color3B(0,0,200));
_btnLand = MenuItemSprite::create(btnLand_OFF,
btnLand_ON,
[this](Ref* sender) {this->landTapped((MenuItemSprite*)sender);});
Label* lblLand = Label::createWithSystemFont("着陸", "arial", 26);
lblLand->enableOutline(Color4B::BLACK, 1);
lblLand->setPosition(Vec2(_btnLand->getContentSize().width*0.5, _btnLand->getContentSize().height*0.5));
_btnLand->addChild(lblLand);
_btnLand->setPosition(Vec2(WINCENTER.x, _btnLand->getContentSize().height*0.5));
_btnLand->setVisible(false);
Menu* menu = Menu::create();
menu->setPosition(this->getPosition());
menu->setContentSize(this->getContentSize());
this->addChild(menu);
menu->addChild(_btnTakeOFF);
menu->addChild(_btnLand);
_lblBattery = Label::createWithSystemFont("Battery 0%", "Arial", 26);
_lblBattery->setAnchorPoint(Vec2(0, 1));
_lblBattery->setAlignment(TextHAlignment::LEFT);
_lblBattery->setPosition(Vec2(10, WINSIZE.height));
this->addChild(_lblBattery);
return true;
}
void HelloWorld::onEnterTransitionDidFinish()
{
scheduleUpdate();
// 0.1秒毎に呼び出す
this->runAction(Repeat::create(Sequence::create(DelayTime::create(0.1), CallFunc::create([=]{
MiniDroneBridge::dispBatteryLevel([=](int level)
{
// バッテリー数値表示
_lblBattery->setString(StringUtils::format("Battery %d%%", level));
});
MiniDroneBridge::dispConnectStat([=](int stat)
{
// 接続状態かどうか
if (stat == 2) {
_connectStat = true;
_btnTakeOFF->setVisible(true);
} else {
_connectStat = false;
_btnTakeOFF->setVisible(false);
}
});
// 飛行状態
MiniDroneBridge::dispFlyingStat([=](int stat)
{
if (_connectStat) {
if (stat == 0) {
// 着陸
_btnTakeOFF->setVisible(true);
_btnLand->setVisible(false);
} else if (stat == 4) {
// 着陸中
_btnTakeOFF->setVisible(false);
_btnLand->setVisible(false);
} else if (stat >= 2 && stat <= 3) {
// 離陸
_btnTakeOFF->setVisible(false);
_btnLand->setVisible(true);
}
}
});
}) , NULL), -1));
}
// 離陸処理
void HelloWorld::takeOffTapped(cocos2d::MenuItemSprite *sender)
{
MiniDroneBridge::takeOff([=](bool isSuccess)
{
log("takeoff=%d", isSuccess);
});
}
// 着陸処理
void HelloWorld::landTapped(cocos2d::MenuItemSprite *sender)
{
MiniDroneBridge::land([](bool isSuccess)
{
log("land=%d", isSuccess);
});
}
まとめ
基本的なドローンの制御はMiniDrone.mに記述しています。
それをCocos2d-xから呼び出してあげるだけで、簡単に制御することが出来ました。
ドローン自体に超音波センサーが内蔵されているので、自立飛行も自動的にしてくれます。
MiniDroneBridge::dispConnectStat
やMiniDroneBridge::dispFlyingStat
で、
ドローンの接続状態と飛行状態を取得しています。
リアルタイムで状態を判断して、適切なボタンを表示してあげると良いです。
このMAMBOドローンは、BB弾が発射出来たり、4gまでの物をつかむためのグラバーが付属品としてあります。
もちろん、プログラムで制御も可能なので、そちらも是非試してみてください!