Пользователь
0,0
рейтинг
26 января 2012 в 11:02

Разработка → Сказ о Cocos2d-android

Так уж получилось, что мне пришлось портировать игру с ios. Фреймворк выбирать не пришлось, им стал Cocos2d. До момента использования фреймворка мне довелось почитать отзывы о нем и они настораживали. Но как всегда надеялся на лучшее. И так, о своем опыте портирования и применения данного фреймворка пойдет этот рассказ. Данный пост будет полезен тем, кто присматривается к Cocos2s-android.


Мысль о том, как должно быть



По моему скромному мнению, архитектуру любой игры нужно проектировать по наименее зависимой от платформы схеме. К примеру, используя Cocos2dx можно написать кроссплатформенную графическую часть игры(на С++) и затем с минимальными усилиями подключить ее к проекту на любой ОС, поддерживающей данный язык(Android, Symbian, Meego, Bada, про IOS не знаю, но думаю тоже). Конечно, придется столкнуться с особенностями ОС, но усилия будут меньше, чем переписывать код с одного языка на другой и сталкиваться с различиями в порте фреймворка.

Как было



А было все совсем иначе. Код был на Obj-C, пришлось портировать на Android на Java(тогда я еще не знал о хотя бы надежде в виде NDK от хабраюзера crystax). В интернете можно найти два порта Cocos2d-iphone под Android:
  1. Cocos2d-android Хм, ничего толком не скажу про этот проект. Билд от января 2010 года. Пожалуй все!
  2. Cocos2d-android-1 Данный проект утверждает на своей страничке, что проект в пункте 1 развивается слишком медленно, поэтому автор решил создать собственный. Как я понял, был форкнут проект code.google.com/p/cocos2d-android и на его основе пилится версия с приставочкой "-1". Поэтому для работы был выбран именно этот проект.


Немного об архитектуре игры и фреймворка



Фреймворк оперирует слоями и сценами. Честно говоря, до сих пор не понимаю в чем заключается разница между ними. Разве что по логике: сцена может включать в себя несколько слоев. Оставим этот вопрос для комментариев экспертов по данному фреймворку.

Признаюсь, игры мне раньше не доводилось разрабатывать. Был только фан-проект и то замахнулись аж на 3D. Поэтому выбранную архитектуру прокомментировать не смогу.

Игра состояла из набора слоев. Управлением и переходами между слоями управляла одна сцена. Вся игра строилась по принципу синглтона. Существует один объект, который хранит в памяти все настройки игры и загруженные данные об уровнях. Данные об уровнях хранятся в xml. Каждый раз при каждом запуске данный файл парсится и создается синглтон, который все это хранит и доступ к которому есть всегда. В проекте существует только одна активити, которая содержит немного подпиленную вьюшку GLSurfaceView:
public class MainActivity extends Activity {
	private CCGLSurfaceView mGLSurfaceView;
	
	public static float SCREEN_WIDTH = 480; 
	public static float SCREEN_HEIGHT = 320; 
	
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
       
                requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,		                WindowManager.LayoutParams.FLAG_FULLSCREEN);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 
				                  WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

		mGLSurfaceView = new CCGLSurfaceView(this);
		CCDirector director = CCDirector.sharedDirector();
		director.attachInView(mGLSurfaceView);
		director.setDeviceOrientation(CCDirector.kCCDeviceOrientationLandscapeLeft);
		
		SCREEN_WIDTH = director.winSize().width;
		SCREEN_HEIGHT = director.winSize().height;
		
                CCDirector.sharedDirector().getActivity().setContentView(mGLSurfaceView);
		CCDirector.sharedDirector().setDisplayFPS(true);
		CCDirector.sharedDirector().setAnimationInterval(1.0f / 60);
       
		CCScene scene = CCScene.node();
		IntroScene intro = new IntroScene();
		scene.addChild( intro );
		
		CCDirector.sharedDirector().runWithScene(scene);
       }
    
       @Override 
       public void onPause(){
    	      super.onPause();
    	      CCDirector.sharedDirector().onPause();
       }
    
       @Override
	public void onResume() {
		super.onResume();
		CCDirector.sharedDirector().onResume();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		CCDirector.sharedDirector().end();
	}
	
	@Override
	public void onBackPressed(){
		//показываем диалог или возвращаемся на предыдущий слой
	}
}

Вцелом, типичная архитектура для андроид-приложения и ничего необычного, но в коллбэки добавлены обработчики Cocos2d. Следует отметить, что при использовании 32-битных картинок с градиентом, градиент начинает плыть. Попытки исправить это не к чему не привели. Стандартный способ для андроида(когда переопределяем метод активити onAttachedToWindow и указываем нужную цветовую палитру) не помог. Все исказилось и слетели размеры и цвета. 8-битные картинки тоже не помогли. Хм, такие дела.

Проблемы в порте фреймворка, которые вылезли



Первый трабл был описан в пункте выше, а здесь все оставшиеся.
Слой не отображается на экране. В практике использования данного фреймворка существует такая схема создания сцены:
public class MainScene extends CCLayer {
      public MainScene(){
             //добавляем текстуры, привязываем обработчики кнопок
      }
	
      public static CCScene scene() {
           CCScene scene = CCScene.node();
	   //CCLayer layer = MainScene.node(); //BUG in Cocos
	   CCLayer layer = new MainScene();
	   scene.addChild(layer);
	   return scene;
     }
}

так вот, в версии фреймворка под ios закоменченная строка есть и прекрасно работает. Я нашел решение в виде создания нового экземпляра сцены, но не факт, что это правильно.

Двигаемся дальше.
Возникла необходимость в создании кастомной вьюшки, которая будет при таче красиво перелистывать уровни игры. В IOS-версии это реализовано просто: наследуется класс от UIScrollView и добавляется поверх вьюшки, отрисовывающей графику(GLSurfaceView). У GLSurfaceView есть метод addView(). В порте под андроид такого метода нет, да и вообще пожалуй добавить свою вьюшку нельзя никак. Об этом следует помнить.

Звук. При использовании SoundEngine постоянно выскакивает IllegalStateException, который правда перехватывается. На этом пожалуй все. Хотя нет, это ппц! Создается дерево объектов исключений, оно кидается, перехватывается. Да, какая там производительность игры, о чем вы?

Белая полоса вместо картинки в слое. В игре использовались кастомные диалоги, которые представляли собой слой, добавленный поверх другого слоя. Ничего необычного. Кроме того, что порой, вместо картинок с кнопками меню выскакивала белая полоса. Код один в один как в ios-версии. Ладно бы это проявлялось на слабых устройствах, но эта штука проявляется и на топовых моделях. В issues на гуглокоде есть проблема, связанная со сборщиком мусора. Сдается мне эта относится к ней же, ибо в логах при сравнении появления данной ошибки появлялась строка со сборкой объектов сборщиком мусора. Либо руки кривые у меня(что вполне вероятно), либо порт не учитывает особенностей архитектуры Андроида.

Названия методов и классов фреймворка. Вот что меня удивило так это именование классов и методов в порте. Это веселье просто. Основные классы названы также как и в IOS-версии, но к примеру класс работы со звуком называется иначе(SoundEngine вместо SimpleAudioEngine). Методы потеряли свои приставки, не все конечно, но многие. Сидишь и гадаешь оно или не оно. Смотришь исходники и разбираешься, теряя время. Странное решение выбрал автор, ИМХО.

Поле _rotate или любое другое поле класса. Ничего особенного: полю наследуемого класса присваивается значение угла. Все идентично ios-версии, но спрайт улетал за пределы игрового поля. Несколько часов было потрачено на дебаг и прочие свистопляски. Как оказалось, подобных присваиваний следует избежать. Свойства были заменены на сеттеры и все заработало. Магия портирования да и только!

Работа алгоритма. Очень важный пункт. Логика движения главного персонажа была реализована с помощью акселерометра. Довольно стандартный прием. Но появился серьезный баг, который мешал корректному прохождению уровня. Суть в чем: Cocos2d по тику обновляет данные об игре, графику и прочее. Для этого в классе нужно написать метод: public void update( float delta ). И тут снова вступило различие в архитектуре двух ОС. Из-за задержек связанных со сборкой мусора(а она была частой, ибо много объектов, много текстур) delta была огромной и рушила весь алгоритм, что приводило просто к ужасным последствиям. Опытным путем была подобрана нужна дельта для корректной работы алгоритма и если она больше, то она просто усекалась до нужной.

Заключение



Из основных моментов, это все. Но следует также добавить, что под Андроид портированы не все классы, а многие уже портированные содержат заглушки. По моему мнению, данный фреймворк далеко не лучший кандидат для разработки игр под Андроид. Есть более удачные кандидаты. Хотя, сколько людей — столько и мнений.

Да прибудет с вами сила!

UPD
По просьбе трудящихся указываю БЕСПЛАТНЫЕ фреймворки на которые стоит обратить внимание. Но есть много других. Просто эти содержат больше материалов на StackOverflow и в интернете вцелом.



Про AndEngine помнится уже была серия туториалов на Хабре. Так что самую полезную информацию поможет найти гугл и хабрапоиск.
Александр @Pyjamec
карма
20,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (18)

  • 0
    Вот в заключении очень бы хотелось перечисления более удачных фреймворков. Кокос на хабре уже упоминался неоднократно, остальные как-то то ли редко, то ли вскользь. Кто какие щупал, отпишитесь, сэкономите другим начинающим гейдвелоперам время.
  • 0
    А игру покажете?
    • 0
      Поддерживаю, хотелось бы посмотреть игру, хотя бы ссылку на маркет
  • 0
    А я сейчас рассматриваю возможность использования cocos2d-x поверх marmelade для следующего проекта (для замены самопального фреймворка). Кто-нибудь может про него чего нибудь хорошего/плохого сказать?
    • 0
      Фреймворк достаточно хороший. Используем его для игр и интерактивных книг. Но в больших проектах нужно хорошо разбираться как он устроен внутри, т.к. есть и баги, и не совсем очевидные места.

      А зачем вам при работе с cocos2d-x нужен marmelade?
      • +2
        Чистый cocos2d-x в отличии от мармелада оборачивает только графику и ввод, и то не полностью. Потом мармелад позволяет полностью абстрагироваться от конкретной платформы. Например, с ним я разработал, оттестил и выпустил iOS приложение на PC с Windows. Понятно, что тестилось все на настоящем железе, но возможность в один клик откомпилировать приложение для десятка платформ — очень ценная вещь для разработки кроссплатформенных игр. Но мармелад — это низкоуровневая платформа, в графике немногим выше чистого OpenGL, по этому и присматриваюсь, чего бы поверх запустить, а то поддержка своего фреймворка занимает слишком много времени, а качество и гибкость оставляет желать лучшего (писалось под конкретную задачу, а потом пришлось выдумывать костыли, чтобы поддерживать более широкий спектр задач).
        • 0
          Для меня главное преимущество мармелада — это абстрагирование от графической системы (может использовать как opengl es, так и soft-рендеринг). Но cocos2d-x не использует графическую библиотеку мармелада, а всегда вызывает opengl es 1.2. Поэтому преимущество теряется.
          Какие еще полезные для игр абстракции дает мармелад?

          Свои приложения мы тоже разрабатываем и отлаживаем на PC с Windows под Visual C++.
          • 0
            Да там все обернуто, файловая система, storage, IO, сенсоры, биллинг и т.д. Плюс маскируются многие различия и глюки разных систем. А разве с чистом cocos2d-x можно откомпилировать iOS приложение не выходя из Visual Studio?
  • 0
    Я так понял, что сцену можно представить себе как View Controller, а слой как View. Не знаю насколько такая модель точна, но я думаю примерно так. Слои содержать вроде как индекс, это значит их можно накладывать один на другой.
    • 0
      Кокос работает с иерархией нод.
      Сцена — это корневая нода. Текущая сцена устанавливается в директоре. Для сцен можно использовать переходы (CCTransition) между сценами.
      Слой — это нода, поддерживающая touch, акселерометр и клавиатуру (но эту поддержку легко добавить и в другие ноды).
      • 0
        читаю с телефона, хотел поставить плюс, но иконки маленькие и промахнулся по минусу. А отменить нельзя! Отдавите мне ногу в ответ, пожалуйста :)
  • 0
    Если движок не дружит с коллектором, то вообще нет смысла его использовать. Кто захочет рубиться в игрушку с простоянными тормозами
  • +2
    Похоже вы плохо понимаете различие между Objective-C и Java.

    >> Поле _rotate или любое другое поле класса.
    В ObjC есть свойства, в Java их нет. Когда в ObjC вы пишите
    node.rotate = 10
    это на самом деле превращается в вызов сеттера:
    [node setRotate:10]
    Т.к. в java свойств нет — все обращения к ним нужно менять вызовы геттеров и сеттеров.

    >> Слой не отображается на экране. В практике использования данного фреймворка существует такая схема создания сцены
    В ObjC в статических методах переменная self указывает на класс (в вашем случае MainScene). В коде метода node объект этого класса и создается ([[self alloc] init]).
    В Java нельзя узнать в статическом методе класс для которого он был вызван. Поэтому код в методе node (return new Layer()) создает всегда объект класса Layer, хотя и был вызван для MainScene.
    Нужно или переопределять статический метод node() для всех ваших наследников от Layer, или не использовать его, а всегда пользоваться конструкторами.
    • 0
      Различия я прекрасно понимаю. Было много времени узнать тонкости этого чудо языка. Нужно было портировать максимально похоже(задача такая стояла) — я портировал, а когда были какие-то траблы — разбирался почему и как. И собственно описал их здесь, в топике.

      Если капнуть в теорию, то статический метод не может иметь доступ к экземпляру. А в обж-си self — просто паттерн, используемый повсеместно. То бишь смысл вашего заключения мне не ясен, так как именно конструктором я и пользовался.
      • 0
        в ObjC статический метод имеет доступ не к экземпляру класса (объекту), а к самому классу.
  • 0
    А исходная игра какой фреймворк использовала?
    • 0
      cocos2d-iphone
      • +5
        А почему тогда не выбрали cocos2d-x?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.