Начало истории
Пришел мне на доработку проект написанный на cocos2d. Игра для детей, в которой необходимо собирать пазлы и учить слова. Работа как работа, но главная проблема заключалась в том, что до меня над проектом работала некая девушка из Индии. И тут у меня начался очень веселый период. Пример того, на что мне пришлось смотреть, что делать и чем все это закончилось, будет под катом.
//SelectPuzzle.h
- (void)imagePickerController:(UIImagePickerController *)pickerView didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[[NSUserDefaults standardUserDefaults]setObject:nil forKey:@"cameraImage"];
UIImage *resiZedImage=[info objectForKey:UIImagePickerControllerOriginalImage];
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(interfaceOrientation))
{
isportrait=YES;
resiZedImage=[self ImageResize:resiZedImage];
}
else
{
isportrait=NO;
resiZedImage=[self imageByCropping:resiZedImage toRect:CGRectMake(800,300,1006,1258)];
}
[[NSUserDefaults standardUserDefaults]setInteger:preDefined6 forKey:imageIndex];
[[NSUserDefaults standardUserDefaults]setObject:[NSData dataWithData:UIImagePNGRepresentation(resiZedImage)] forKey:@"cameraImage"];
[[NSUserDefaults standardUserDefaults]synchronize];
[self performSelector:@selector(dismissCameraView:) withObject:pickerView afterDelay:1.0];
[[CCDirector sharedDirector]replaceScene:[PlayScene scene]];
}
//PlayScene.m
-(void)init{
self = [super init];
if(self){
//...
//...
camera = YES;
NSData* imageData = [[NSUserDefaults standardUserDefaults] objectForKey:@"cameraImage"];
UIImage* cameraImage = [[[UIImage alloc] initWithData:imageData]autorelease];
puzzleObject=[CCSprite spriteWithCGImage:cameraImage.CGImage key:nil];
//...
//...
}
return self;
}
Однако пост не об ошибках, которые мне пришлось исправлять, а совсем о другой проблеме.
Через несколько месяцев работы над проектом, когда основные проблемы были решены, заказчик попросил собрать билд под его Android девайс. Далее шел удивительный разговор о том, что первый разработчик должен был писать под cocos2d-x и приложение должно легко запускаться на любом устройстве. Решать проблему пришлось уже мне и мой выбор пал на Apportable.
Собственно тема
Как утверждает официальный сайт, Apportable SDK — это система, которая позволяет запускать один и тот же xCode проект на iOS и Android устройствах. В первую очередь, это касается игр написаных на cocos2d. C портирование некоторых системных Core фреймворков есть проблемы.
Наша игра была написана практически на чистом кокосе и только в двух местах использовались UIKit элементы.
Ну что же. Приступим.
Первое, что необходимо сделать — установить SDK на Mac. Для этого, следуя инструкциям на сайте, необходимо просто ввести в терминале
(echo; echo PATH=~/.apportable/SDK/bin:$PATH) >> ~/.bash_profile; source ~/.bash_profile
SDK требует около 2гб свободного места на диске и установленного xCode 5.
Пока все это скачивается, можно дописать в наш проект необходимый код.
Сначала установим режим эммулирования экрана. Тут есть несколько варианта.
Например:
UIScreenIPhone3GEmulationMode — используется, если ваше приложение не будет поддерживать графику от retina устройств. Размер виртуального экрана 320px/480px, scale — 1.0
UIScreenScaledAspectFitEmulationMode — растягивает ваше приложение в соответсвии с размером экрана Android устройства. Использует не retina графику.
UIScreenBestEmulatedMode — растягивает ваше приложение в соответсвии с размером экрана Android устройства. Использует наиболее подходящую графику. Для корректной работы этого режима должна быть графика для всех вариантов iPhone и iPad экранов. Иначе может выглядеть совсем не best.
int main(int argc, char *argv[]) {
@autoreleasepool {
#ifdef ANDROID
[UIScreen mainScreen].currentMode = [UIScreenMode emulatedMode:UIScreenAspectFitEmulationMode];
#endif
int retVal = UIApplicationMain(argc, argv, nil, @"AppController");
return retVal;
}
}
typedef NS_ENUM(NSUInteger, UIScreenEmulationMode) {
UIScreenIPhone3GEmulationMode,
UIScreenIPhone4EmulationMode,
UIScreenIPhone5EmulationMode,
UIScreenIPadEmulationMode,
UIScreenIPadRetinaEmulationMode,
UIScreenAspectFitEmulationMode,
UIScreenScaledAspectFitEmulationMode,
UIScreenBestEmulationMode,
UIScreenBestEmulatedMode = UIScreenBestEmulationMode,
UIScreenNativeMode,
UIScreenNativeRetinaMode,
UIScreenBestNativeMode,
};
Следующее что пришлось переделывать — позиции элементов.
[item setPosition:ccp(400, 600)];
на
[item setPosition:ccp(puzzle.contentSize.width * 0.2, puzzle.contentSize.height * 0.772)];
Тоже самое при отслеживании нажатий.
После этого надо перейти в терминале в папку с проектом и ввести:
apportable load
Эта команда создаст apk файл, загрузит его на подключенное устройство, установит и запустит. В идеальном варианте. Он бывает не всегда. Я не буду рассматривать ошибки линковки файлов, они очень индивидуальны. Однако бывает ситуация когда apportable не видит какой-то определенный девайс. На офисе из 7 устройств, на которых я тестировал приложение, не смог установить на 3.
Что делать в таком случае? Все очень просто. Apportable сначала создает apk файл и только потом пытается загрузить его на устройство. Значит этот файл где-то есть. Если более точно, то вот здесь:
/Users/username/.apportable/SDK/Build/android-armeabi-debug/ProjectName
В этой папке лежит apk файл и его можно просто перекинуть на девайс и установить. Для этого я использовал приложение Android File Transfer(в маке из коробки нельзя обращаться к памяти Android устройств).
Все? Профит? К сожалению нет.
Возникла проблема с фотокамерой. Камера вызывалась, делала фотографию, но при нажатии кнопки подтверждения, приложение падало. Проблема не гуглилась и обещала принести большие проблемы, но… Помощь пришла от наших android разработчиков. Я узнал, что такое манифест и пермишены.
И так. После создания билда с помощью apportable, в директории с проектом появилась папка ProjectName.aproj. В этой папке лежат конфигурационные файлы и файлы, которые не компилируются(например ресурсы).
Нас интересует файл configuration.json. В нем можно изменить настройки компиляции проекта для android. Например, добавить пермишены. Для работы фотокамеры нужны права на запись.
И изменение строки
"FEATURES": ["opengles2","landscape"]
на
"FEATURES": ["opengles2","landscape","write_external_storage"]
волшебным образом решило проблему.
Также есть нюанс с локализацией имени приложения на экране устройства. Для каждого языка необходимо добавить файл strings.xml и положить в папку ProjectName/java/res/values-[lang_description]
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Localization name</string>
</resources>
И добавить в configuration.json:
//A list of java resource directories
"java_res_dirs": ["java/res"]
Несколько моментов на последок:
- Рамеров экранов у Android устройств очень много, и кое-где приходилось использовать следующие вставки:
Скрытый текст
#ifdef ANDROID CGRect frame = [UIScreen mainScreen].applicationFrame;//1.7031 - pad 1.778 - phone CGFloat ratio = frame.size.height / frame.size.width; if (ratio < 1.72) { x = 16; } else{ x = 29; } #endif
- Если в файле configuration.json не стоит флаг «portrait», а Device Orientation в настройках xCode проекта стоит галочка напротив «Portrait», то портретная ориентация поддерживаться будет. Будьте внимательны.
- Официальная документация docs.apportable.com/
- В Apportable дописаны многие системные фреймворки. Официальная документация содержит мало информации об этом. Новые .h файлы с новыми доступными для android методами можно посмотреть в папке
/Users/username/.apportable/SDK/sysroot/System/Library
Автор: HexArt