Как сделать простую игру для iPhone с помощью Cocos2D

Ray Wenderlich

Пост также есть для: Упрощенный китайский, Английский, Испанский

Ниндзя мочит врагов!

Ниндзя мочит врагов!

Cocos2D – это мощная библиотека, которая позволяет значительно ускорить процесс разработки игр под iPhone.  Она включает в себя поддержку спрайтов, классные графические эффекты, анимации, физические библиотеки, звуковые движки и многое другое.

Я только начинаю изучать  Cocos2D, и хотя существует достаточно полезных учебников для начинающих, я не смог найти то, что подходило бы мне полностью – способ сделать очень простую, но достаточно функциональную игру с анимацией, стрельбой, аудио, и при этом обойтись использованием несложных вещей. В итоге я сделал такую игру самостоятельно и решил написать об этом серию туториалов, которая могла бы быть полезной для других новичков.

Эта серия туториалов покажет вам весь процесс создания простой игры для iPhone с помощью Cocos2D, от начала до конца. Можете читать учебники от корки до корки, а можете сразу обратиться к проекту – образцу игры в конце статьи. И вот что ещё. Будут ниндзя.

(Перейти ко второй или третьей части серии.)

Скачивание и установка Cocos2D

Скачать Cocos2D можно здесь.

После скачивания необходимо установить полезные шаблоны проекта. Откройте Терминал, перейдите к папке, в которую вы скачали Cocos2d и введите следующую команду: ./install-templates.sh-f-u

Обратите внимание, что при желании вы можете передать какой-нибудь параметр установочному скрипту если ваш Xcode стоит не в стандартной папке (такое может быть, например, если у вас на машине более одной версии SDK).

Hello, Cocos2D!

Давайте начнём с того, что создадим простой Hello World проект и запустим его с помощью шаблона Сосоs2d, который мы только что установили. Перейдите к созданию нового проекта в Xcode, выберите cocos2d Application и назовите проект “Cocos2DSimpleGame”.

Cocos2D Templates

Сразу после создания запустите проект на выполнение. Если никаких ошибок нет, вы должны увидеть следующее:

HelloWorld Screenshot

Сocos2D построен на использовании коцепции “сцен” (scenes), которые являются чем-то вроде “уровней” или “экранов” для игры. Например у вас может быть сцена для первоначального меню игры, ещё одна для основного действия и ещё одна для окончания игры. Внутри сцены вы можете иметь некоторое количество слоёв (layers) (вроде как в Фотошопе), а слои могут содержать в себе так называемые “узлы” (nodes), такие как спрайты, метки, меню и так далее. Узлы могут также содержать в себе другие узлы (например спрайт может иметь внутри себя другой спрайт – “потомок”).

Если вы заглянете внутрь нашего проекта, то увидите, что там пока что только один слой – HelloWorldLayer – и мы будем реализовывать наш игровой процесс именно в нём. Давайте откроем его и вы увидите, что на данный момент в его init методе к нему добавляется метка с надписью “Hello World”. Мы её уберем, а на её место поставим спрайт.

Добавление спрайта

Для того, чтобы добавить спрайт, нам нужны будут изображения. Вы можете создать свои собственные или использовать те, которые создала для проекта моя любимая жена : главный герой, звёздочка, и цель.

Как только вы скачали файлы с изображениями – перетащите их в папку ресурсов в XCode и выберите “Copy items into destination group’s folder (if needed)”.

Изображения готовы и нам надо подумать где лучше всего расположить главного героя. Обратите внимание, что в Cocos2D левый нижний угол является начальной точкой отсчёта координат, то есть имеет координаты (0,0), а х и у координаты возрастают по мере продвижения, соответственно, вперёд и вправо. Поскольку наш проект имеет ландшафтную ориентацию, это значит что правый верхний угол имеет координаты (480, 320).

Также обратите внимание, что по умолчанию когда мы задаём позицию для объекта, позиция устанавливается относительно центра добавляемого спрайта. То есть, если мы хотим, чтобы спрайт нашего главного был вровень с левым краем экрана и отцентрирован по вертикали:

  • Х координату зададим как: ширина спрайта главного героя / 2.
  • У координату как: высота экрана / 2.

Вот картинка, которая иллюстрирует это наглядно:

Screen and Sprite Coordinates

Ну что, давайте попробуем! Откройте папку Classes, кликните на HelloWorldLayer.m и замените содержимое метода  init следующим кодом:

-(id) init
{
  if( (self=[super init] )) {
    CGSize winSize = [[CCDirector sharedDirector] winSize];
    CCSprite *player = [CCSprite spriteWithFile:@"Player.png" 
      rect:CGRectMake(0, 0, 27, 40)];
    player.position = ccp(player.contentSize.width/2, winSize.height/2);
    [self addChild:player];		
  }
  return self;
}

Можно компилировать проект и запускать его на выполнение, и спрайт должен появиться на месте как положено, но обратите внимание, что по умолчанию бэкграунд чёрный. А для нашего творчества белый будет смотреться намного лучше. Легким способом задать бэкграунду слоя в  Cocos2D необходимый цвет является использование класса  CCLayerColor. Давайте попробуем. Кликните на  HelloWorldLayer.h и измените интерфейс класса HelloWorld на следующий:

@interface HelloWorldLayer : CCLayerColor

Затем кликните на HelloWorldLayer.m и внесите небольшое изменение в init метод, чтобы бэкграунд изменился на белый:

if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {

Откомпилируйте и запустите проект, и вы должны увидите свой спрайт поверх белого бэкграунда. Ура, наш ниндзя готов к действию!

Sprite Added Screenshot

Передвижение целей

Теперь к нашей сцене надо добавить врагов, с которыми будет сражаться наш ниндзя. Чтобы было интереснее – врагов надо сделать движущимися. Давайте будем создавать врагов немного правее экрана и задавать им движение влево.

Добавьте следующий метод прямо перед инитом:

-(void)addTarget {
 
  CCSprite *target = [CCSprite spriteWithFile:@"Target.png" 
    rect:CGRectMake(0, 0, 27, 40)]; 
 
  // Определяем У координату для создания цели
  CGSize winSize = [[CCDirector sharedDirector] winSize];
  int minY = target.contentSize.height/2;
  int maxY = winSize.height - target.contentSize.height/2;
  int rangeY = maxY - minY;
  int actualY = (arc4random() % rangeY) + minY;
 
  // Создаем цель чуть-чуть за правым краем экрана,
  // и на случайной позиции по оси У, как показано выше
  target.position = ccp(winSize.width + (target.contentSize.width/2), actualY);
  [self addChild:target];
 
  // Задаём скорость движения цели 
  int minDuration = 2.0;
  int maxDuration = 4.0;
  int rangeDuration = maxDuration - minDuration;
  int actualDuration = (arc4random() % rangeDuration) + minDuration;
 
  // Задаём действие
  id actionMove = [CCMoveTo actionWithDuration:actualDuration 
    position:ccp(-target.contentSize.width/2, actualY)];
  id actionMoveDone = [CCCallFuncN actionWithTarget:self 
    selector:@selector(spriteMoveFinished:)];
  [target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
 
}

Я оставил подробные комментарии в коде, чтобы всем всё было понятно.  Основная часть кода основывается на том, с чем мы уже успели разобраться: мы выполняем несложные вычисления, для того, чтобы определить где мы будем создавать объект, задаём позицию объекта и добавляем его к сцене таким же образом, как и спрайт главного героя.

Новым элементом здесь является добавление действий ( actions ). Cocos2D предоставляет множество чрезвычайно удобных, встроенных действий, которые вы можете использовать, чтобы анимировать ваши спрайты – действия движения, прыжков, постепенного исчезновения, появления и многое другое. Мы применяем к нашей цели три действия:

  • CCMoveTo: Мы используем CCMoveTo чтобы направлять объект влево. Обратите внимание, что мы можем задать длительность движения, и в данном случае длительность является случайной величиной в пределах 2 – 4 секунд.
  • CCCallFuncN: Функция CCCallFuncN позволяет задать колбэк метод, который будет выполняться, когда действие выпонено. Мы задаём колбэк, который называется “spriteMoveFinished”, которого пока что нет, мы напишем его позже.
  • CCSequence: Действие CCSequence позволяет нам создать цепочку дейтвий, которые будут выполняться по очереди. Таким образом, сначала выпонится действие CCMoveTo, а после него CCCallFuncN.

Теперь надо добавить колбэк функцию, на которую мы ссылаемся в действии CCCallFuncN. Её можно добавить прямо перед addTarget:

-(void)spriteMoveFinished:(id)sender {
  CCSprite *sprite = (CCSprite *)sender;
  [self removeChild:sprite cleanup:YES];
}

Смысл этой функции в том, чтобы убирать спрайты со сцены, как только они выходят за пределы экрана. Это очень важно, иначе мы получим утечки памяти из-за того, что за экраном у нас куча неиспользуемых спрайтов. Можно заметить, что существуют иные (и лучшие) способы решить эту проблему, например можно использовать массив многократно используемых спрайтов, но для простого туториала для начинающих и так сойдёт.

И ещё кое-что. Нам необходимо, собственно, вызывать метод, создающий цели. И чтобы было веселее, пусть цели создаются регулярно по прошествию промежутка времени. В Cocos2D  этого можно достичь с помощью функции schedule: , в котрую передаётся селектор колбэковой функции, которая будет периодически  вызываться. Один раз в секунду – как раз то, что надо. Добавьте следующую строчку в init метод прямо перед return:

[self schedule:@selector(gameLogic:) interval:1.0];

А теперь осталось просто реализовать колбэк функцию:

-(void)gameLogic:(ccTime)dt {
  [self addTarget];
}

Вот и всё!  Теперь, если запустить проект на выполнение, то можно увидеть, как цели бодро движутся через экран:

Targets Screenshot

Метание звёздочек

В данный момент вступает в действие главный герой-ниндзя – давайте добавим метание звёздочек. Метание можно выполнить различными способами, но для этой игры мы сделаем так – когда пользователь прикасается к экрану, ниндзя бросает звёздочку в направлении касания.

Для выполнения броска я буду использовать действие CCMoveTo, чтобы ничего не усложнять, но чтобы всё получилось, необходимо немного математики, потому что CCMoveTo требует задания конечной точки движения звёздочки, а сама точка касания для этого не годится, она всего лишь задаёт направление броска относительно главного героя, а нам надо чтобы звёздочка пролетала сквозь точку касания и двигалась до тех пор пока не покинет пределы экрана.

Вот подходящая иллюстрация:

Projectile Triangle

Как видите, мы имеем небольшой треугольник, полученый с помощью разницы между позицией ниндзи и точкой касания по осям х и у. Нам всего лишь надо создать больший треугольник, с таким же точно соотношением сторон и чтобы одна из вершин треугольника находилась за пределами экрана.

Перейдём к коду. Сначала нам надо активировать обработку касаний. Добавьте следующую строчку в метод init:

self.isTouchEnabled = YES;

Поскольку обработка касаний активирована, мы теперь можем пользоваться колбэками, вызываемыми при касаниях. Давайте реализуем метод ccTouchesEnded, который будет вызываться всякий раз, когда пользователь убирает убирает палец с экрана, следующим образом:

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
 
  // Выбираем одно из касаний, с которым будем работать
  UITouch *touch = [touches anyObject];
  CGPoint location = [touch locationInView:[touch view]];
  location = [[CCDirector sharedDirector] convertToGL:location];
 
  // Устанавливаем начальное местоположение звёздочки
  CGSize winSize = [[CCDirector sharedDirector] winSize];
  CCSprite *projectile = [CCSprite spriteWithFile:@"Projectile.png" 
    rect:CGRectMake(0, 0, 20, 20)];
  projectile.position = ccp(20, winSize.height/2);
 
  // Определяем смещение
  int offX = location.x - projectile.position.x;
  int offY = location.y - projectile.position.y;
 
  // Убеждаемся, что не стреляем назад
  if (0 >= offX)return;
 
// Можно добавлять звёздочку
  [self addChild:projectile];
 
  // Определяем направление стрельбы
  int realX = winSize.width + (projectile.contentSize.width/2);
  float ratio = (float) offY / (float) offX;
  int realY = (realX * ratio) + projectile.position.y;
  CGPoint realDest = ccp(realX, realY);
 
  // Определяем дистанцию стрельбы
  int offRealX = realX - projectile.position.x;
  int offRealY = realY - projectile.position.y;
  float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
  float velocity = 480/1; // 480pixels/1sec
  float realMoveDuration = length/velocity;
 
  // Передвигаем звёздочку в конечную точку
  [projectile runAction:[CCSequence actions:
    [CCMoveTo actionWithDuration:realMoveDuration position:realDest],
    [CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)],
    nil]];

Сначала мы выбираем одно из касаний, с которым мы будем работать, получаем его позицию на экране, затем вызываем convertToGL, чтобы преобразовать координаты точки касания согласно нашей текущей системе координат. Это необходимо сделать, поскольку мы работаем в ландшафтном режиме.

Затем мы загружаем изображение звёздочки и задаём начальную позицию, как обычно. Затем мы определяем куда нам надо передвигать звёздочку, используя вектор между позицией ниндзя и точкой касания, согласно алгоритму описанному выше.

Следует отметить, что алгоритм не совершенен. Наш снаряд продолжает двигаться до тех пор пока не пересечёт горизонтальную границу экрана, даже если уже пересёк вертикальную! Есть много способов сделать это иначе, например находить кратчайшую дистанцию до края экрана, или в методе gameLogic проверять есть ли звёздочки за пределами экрана и убирать их, но для данного туториала оставим всё как есть.

Последнее, что осталось сделать – определить длительность движения звёздочки. Мы хотим, чтобы она двигалась с постоянной скоростью, независимо от направления движения, так что нам снова нужно немного позаниматься математикой. Мы можем определить дистанцию движения с помощью Теоремы Пифагора. Как вы помните из геометрии, эта теорема состоит в том, что длина гипотенузы треугольника равна корню квадратному из суммы квадратов катетов.

Получив расстояние, мы делим его на скорость и получаем время движения. Поскольку скорость равна расстоянию делённому на время, то время, соответственно, равно расстоянию делённому на скорость.

На последок задаём необходимые действия, в точности как мы сделали это для целей. Запускайте проект, и теперь ваш ниндзя должен уметь пулять звёздочки по наступающим врагам.

Projectiles Screenshot

Уничтожение целей

Теперь наши звёздочки летают вовсю, но что действительно нужно нашему ниндзя – так это мочить врагов. Давайте добавим немного кода, с помощью которого будем определять пересечение звёздочки и цели.

Можно по разному реализовать это с помощью Cocos2D, например можно использовать физические библиотеки Box2D или Chipmunk. Но мы снова обойдёмся без лишних осложнений и реализуем уничтожение целей самостоятельно.

Чтобы выполнить это, нам, в первую очередь, надо тщательно отслеживать движение целей и звёздочек по сцене. Добавьте следующее в объявление класса HelloWorldLayer:

NSMutableArray *_targets;
NSMutableArray *_projectiles;

И инициализируйте эти массивы в методе init:

_targets = [[NSMutableArray alloc] init];
_projectiles = [[NSMutableArray alloc] init];

И пока мы об этом не забыли, надо освободить память в методе dealloc:

[_targets release];
_targets = nil;
[_projectiles release];
_projectiles = nil;

Сейчас модифицируйте метод addTarget таким образом, чтобы добавлять новую цель в массив целей и задавать им теги для использования в будущем:

target.tag = 1;
[_targets addObject:target];

Также модифицируйте метод ccTouchesEnded, чтобы добавлять новые звёздочки в массив звёздочек и также задавать им теги для дальнейшего использования:

projectile.tag = 2;
[_projectiles addObject:projectile];

И, в конце концов, измените метод spriteMoveFinished, чтобы удалять спрайты из соответствующих массивов согласно тегу.

if (sprite.tag == 1) { // target
  [_targets removeObject:sprite];
} else if (sprite.tag == 2) { // projectile
  [_projectiles removeObject:sprite];
}

Запустите проект на выполнение, чтобы убедиться, что всё по прежнему работает как положено. Каких-то заметных изменений быть не должно, но теперь у нас всё под контролем и мы можем реализовать обнаружение пересечений звёздочек и целей.

Сейчас необходимо добавить следующий метод в HelloWorldLayer:

- (void)update:(ccTime)dt {
 
  NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];
  for (CCSprite *projectile in _projectiles) {
    CGRect projectileRect = CGRectMake(
      projectile.position.x - (projectile.contentSize.width/2), 
      projectile.position.y - (projectile.contentSize.height/2), 
      projectile.contentSize.width, 
      projectile.contentSize.height);
 
    NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
    for (CCSprite *target in _targets) {
      CGRect targetRect = CGRectMake(
        target.position.x - (target.contentSize.width/2), 
        target.position.y - (target.contentSize.height/2), 
        target.contentSize.width, 
        target.contentSize.height);
 
      if (CGRectIntersectsRect(projectileRect, targetRect)) {
        [targetsToDelete addObject:target];				
      }						
    }
 
    for (CCSprite *target in targetsToDelete) {
      [_targets removeObject:target];
      [self removeChild:target cleanup:YES];									
    }
 
    if (targetsToDelete.count > 0) {
      [projectilesToDelete addObject:projectile];
    }
    [targetsToDelete release];
  }
 
  for (CCSprite *projectile in projectilesToDelete) {
    [_projectiles removeObject:projectile];
    [self removeChild:projectile cleanup:YES];
  }
  [projectilesToDelete release];
}

Код достаточно простой. Мы проходим в цикле по нашим звёздочкам, создавая прямоугольники, соответствующие их рамкам и используем CGRectIntersectsRect для проверки на пересечение. Если пересечение найдено, мы удаляем спрайты со сцены и из массивов. Обратите внимание, что мы должны добавлять объекты в специальные массивы (targetsToDelete и projectilesToDelete), потому что невозможно удалять элементы массива при прохождении по нему в цикле. Опять-таки, существуют более оптимальные способы для осуществления такого рода вещей, но я буду придерживаться простого подхода.

Для полной готовности не хватает одного – надо сделать так, чтобы этот метод вызывался как можно чаще. Добавьте следующую строчку в инит:

[self schedule:@selector(update:)];

Откомпилируйте проект, запустите его и теперь если звёздочка и цель пересекаются – они должны исчезать!

Последние детали

1Мы очень близки к тому, чтобы получить полноценную (хотя и чрезвычайно простую) игру. Нам надо добавить немного звуковых эффектов и музыки (что за игра без звука?), а таже простую игровую логику.

Если вы читали мою серию туториалов о программировании звука для iPhone, то вам будет очень приятно узнать насколько разработчики Cocos2D упростили работу со звуком.

В первую очередь перетащите файлы c фоновой музыкой и звуковыми эффектами для стрельбы в папку ресурсов. Можете использовать >классную фоновую музыку, которую я сделал или мои прикольные звуковые эффекты для стрельбы или сделать свои.

Затем добавьте следующий импорт в HelloWorldLayer.m:

#import "SimpleAudioEngine.h"

В методе инит, запустите музыку следующим образом:

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"];

А в метод ccTouchesEnded добавьте проигрывание звуковых эффектов:

[[SimpleAudioEngine sharedEngine] playEffect:@"pew-pew-lei.caf"];

Теперь давайте создадим ещё одну сцену, которая будет появляться по окончании игры. Кликните на папке Classes, выберите FileNew File, затем выберите Objective-C класс и убедитесь что выбрано наследование этого класса от NSObject. Нажмите Next, введите GameOverScene в качестве имени файла и убедитесь, что напротив “Also create GameOverScene.h” стоит галочка.

Теперь замените содержимое GameOverScene.h следующим кодом:

#import "cocos2d.h"
 
@interface GameOverLayer : CCLayerColor {
  CCLabelTTF *_label;
}
@property (nonatomic, retain) CCLabelTTF *label;
@end
 
@interface GameOverScene : CCScene {
  GameOverLayer *_layer;
}
@property (nonatomic, retain) GameOverLayer *layer;
@end

А содержимое GameOverScene.m таким вот кодом:

#import "GameOverScene.h"
#import "HelloWorldLayer.h"
 
@implementation GameOverScene
@synthesize layer = _layer;
 
- (id)init {
 
  if ((self = [super init])) {
    self.layer = [GameOverLayer node];
    [self addChild:_layer];
  }
  return self;
}
 
- (void)dealloc {
  [_layer release];
  _layer = nil;
  [super dealloc];
}
 
@end
 
@implementation GameOverLayer
@synthesize label = _label;
 
-(id) init
{
  if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {
 
    CGSize winSize = [[CCDirector sharedDirector] winSize];
    self.label = [CCLabelTTF labelWithString:@"" fontName:@"Arial" fontSize:32];
    _label.color = ccc3(0,0,0);
    _label.position = ccp(winSize.width/2, winSize.height/2);
    [self addChild:_label];
 
    [self runAction:[CCSequence actions:
      [CCDelayTime actionWithDuration:3],
      [CCCallFunc actionWithTarget:self selector:@selector(gameOverDone)],
      nil]];
 
  }	
  return self;
}
 
- (void)gameOverDone {
 
  [[CCDirector sharedDirector] replaceScene:[HelloWorldLayer scene]];
 
}
 
- (void)dealloc {
  [_label release];
  _label = nil;
  [super dealloc];
}
 
@end

Обратите внимание на присутствие двух разных классов: сцены и слоя. Сцена может содержать любое количество слоёв, хотя в данном случае содержит только один. Весь функционал слоя состоит в демонстрации посреди экрана метки в течении 3-х секунд, а затем возврату к сцене Hello World.

И наконец, давайте добавим немного крайне простой игровой логики. Давайте будем отслеживать звёздочки, которые уничтожает игрок. Добавьте переменную в HelloWorldLayer класс в HelloWorldLayer.h:

int _projectilesDestroyed;

Внутри HelloWorldLayer.m импортируйте класс GameOverScene

#import "GameOverScene.h"

Увеличивайте счётчик и проверяйте условие победы в методе update внутри цикла targetsToDelete сразу же после removeChild:target

_projectilesDestroyed++;
if (_projectilesDestroyed > 30) {
  GameOverScene *gameOverScene = [GameOverScene node];
  _projectilesDestroyed = 0;
  [gameOverScene.layer.label setString:@"You Win!"];
  [[CCDirector sharedDirector] replaceScene:gameOverScene];
}

И вдобавок давайте сделаем так, что если хотя бы один враг достигает противоположного края – вы проиграли. Модифицируйте метод spriteMoveFinished, добавив следующий код внутрь условия tag == 1 сразу после removeChild:sprite:

GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose :["];
[[CCDirector sharedDirector] replaceScene:gameOverScene];

Запустите игру и теперь вы можете выигрывать, проигрывать и видеть соответствующую сцену окончания игры.

Дай мне код!

И вот мы всё сделали! А вот здесь весь код простая игра для iPhone на Cocos2D, который мы на данный момент разработали.

Что дальше?

Этот проект может быть прекрасной основой для дальнейшего знакомства с Cocos2D, в него можно добавить несколько новых деталей. Можно попробовать добавить гистограмму, чтобы показывать сколько ещё целей осталось уничтожить для победы, можно добавить прикольные анимации для уничтожения монстров, можно добавить больше звуков, изображений или расширить игровую логику. Предела нет!

Если хотите продвигаться дальше с этой серией туториалов – гляньте вторую часть, Как добавить вращающуюся башню, или третью – Монстры сложнее, уровней больше.

Также, если хотите узнать больше о Cocos2D, гляньте мои туториалы как создавать кнопки в Cocos2D или как создать простую игру наподобие Breakout.

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

Комментарии Пользователя

1 Comment

  • Hm... Looks like someone stole it from here :(
    akonakov

Other Items of Interest

Ежемесячная рассылка Рэя

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Разместите у Нас Рекламу!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Загрузка ... Загрузка ...

Наши книги

Наша Команда

Туториал Команда

  • Sam Davies

... 49 общее количество!

Update Team

  • Riccardo D'Antoni
  • Zouhair Mahieddine

Команда Редакторов

... 23 общее количество!

Code Team

  • Orta Therox

... 3 общее количество!

Команда переводчиков

  • Przemysław Rembelski
  • David Hidalgo

... 33 общее количество!

Эксперты

  • Richard Casey

... 4 общее количество!