Всем добро. Сегодня расскажу как сделать джойстик на cocos2d 2.0b.
И с чего состоит джостик:
- Окружность – для ограничение движения джойстика.
- Контроллер – то что будем двигать в окружности.
План действия:
- Проверить прикоснулись ли к джойстику.
- Следить за движением пальца и двигать контролер.
- Если палец вышел за окружность решить что делать.
- Если палец убран вернуть контролер в центр.
Ну что ? Поехали!
#import "CCLayer.h" #import "CCSprite.h"@protocol GGJoystickDelegate -(void)joystickControlBegan:(CGPoint)Point; -(void)joystickControlMoved:(CGPoint)Point; -(void)joystickControlEnded:(CGPoint)Point; -(CGFloat)joystickControlSpeedMultiplier; -(void)joystickControlDidUpdate:(CGFloat)xSpeedRatio toYSpeedRatio:(CGFloat)ySpeedRatio; @end
@interface GGJoystick : CCSprite { CGFloat _radius; CGFloat _speedMultiplier; BOOL _isJoystickEnabled; BOOL _isBlock; CCSprite *controller; CGPoint actualPoint; id _delegate; int fx; int fy; }-(id)initWithFile:(NSString *)filename controller:(CCSprite*)controllerS; @property (nonatomic,readwrite,retain) id delegate; @property (nonatomic,readwrite) BOOL isBlock; @property (nonatomic,readwrite) CGFloat radius; @property (nonatomic,readwrite) CGFloat speedMultiplier; @property (nonatomic,readonly) BOOL isJoystickEnabled; @end
Из чего состоит интерфейс:
- CGFloat _radius; -радиус окружности.
- BOOL _isJoystickEnabled; — активирован ли джостик.
- CCSprite *controller; контроллер и в Африке контроллер;
- id<GGJoystickDelegate> _delegate; вкусный делегат.
- Int fx,fy –коэффицент четвертей окружностей
- CGFloat speedMultiplier – множитель скорости
- CGPoint actualPoint — запоминаем точку при прикосновение относительно оси координат джойстика.
Делегат GGJoystickDelegate:
- -(void)joystickControlBegan:(CGPoint)Point; — когда контролер стартует.
- -(void)joystickControlMoved:(CGPoint)Point; — когда контролер двигается.
- -(void)joystickControlEnded:(CGPoint)Point; -когда пальчик отжали от контролера.
- -(void)joystickControlDidUpdate:(CGFloat)xSpeedRatio toYSpeedRatio:(CGFloat)ySpeedRatio; дает коээфиценты для прироста кординат у обьекта или обьектов( допусти для управление главного персонажа (пвсевдо код) hero.x + xSpeedRatio , hero.y +ySpeedRatio и т д.
- -(CGFloat)joystickControlSpeedMultiplier; — вкусный метод вызращаем множитель скорости , для чего нужно? ну… думайте мне нужен)
Реализация:
Разжевываем тут!
Метод:
-(id)initWithFile:(NSString *)filename controller:(CCSprite*)controllerS
Инициализирует спрайт джостика ,а также передает спрайт контролера.
Метод:
-(CGFloat)distanceBetweenTwoPoints:(CGPoint)point1 pointTwo:(CGPoint)point2
Узнаем расстояние между двумя точками по теореме Пифагора.
Метод:
-(CGFloat)slopeBetweenTwoPoints:(CGPoint)point1 pointTwo:(CGPoint)point2
Узнаем угловой коэффициент прямой, проходящей через две данные точки.
Метод:
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Если джойстик активен выходим из обработки прикосновения
if (_isJoystickEnabled) { return; }
Находи точку со прикосновением экрана.
UITouch *touch = [touches anyObject];
Получем координаты прикосновение экрана
CGPoint location = [touch locationInView:touch.view];
Переводим координаты с UI на GL.
location =[[CCDirector sharedDirector] convertToGL:location];
Находим дистанцию между двумя точками при помощи теоремы Пифагора . 1 точка начал координат от джойстика (0.0) до точки соприкосновения.
CGFloat distance =[self distanceBetweenTwoPoints:self.position pointTwo:location];
Если найденная дистанция меньше или равна радиуса окружности тогда выполняем код ниже.
if (distance<=self.radius) { Активируем джойстик. _isJoystickEnabled = true;
Находим актуальную точку от оси координат джойстика. Что это значит?
У нас две оси координат 1) Экрана 2) это центра спрайта джойстика) При прикосновения к экрану нам дает координаты относительно оси экрана, чтоб высчитать относительно джойстика, нам нужно точки прикосновения относительно оси координат экрана вычесть точки спрайта джойстика относительно оси координат экрана.
actualPoint = CGPointMake(location.x-self.position.x, location.y -self.position.y);
Так как точки спрайта джойстика это его половинки, то есть начала координат начинается в центре спрайта на нужно прибавить к найденной точки еще половинку спрайта джойстика , тогда мы найдем нужную точку.
CGPoint point = CGPointMake(self.boundingBox.size.width/2 +actualPoint.x, self.boundingBox.size.height/2+actualPoint.y);
Вызываем делегат и передает точку .(зачем не знаю, пригодиться).
if ([self.delegate respondsToSelector:@selector(joystickControlBegan:)]) { [self.delegate joystickControlBegan:point]; }
Меняем положение контролера.
[controller setPosition:point];
Создаем цикл который будет обрабатывать движения контролера и давать коэффициенты передвижения.
[[[CCDirector sharedDirector]scheduler]scheduleSelector:@selector(update1:) forTarget:self interval:0 paused:false]; }
Метод:
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
Находит точку со прикосновением экрана.
UITouch *touch = [touches anyObject];
Получаем координаты прикосновение экрана
CGPoint location = [touch locationInView:touch.view];
Переводим координаты с UI на GL.
location =[[CCDirector sharedDirector] convertToGL:location];
Находим дистанцию между двумя точками при помощи теоремы Пифагора . 1 точка начал координат от джойстика (0.0) до точки соприкосновения.
CGFloat distance =[self distanceBetweenTwoPoints:self.position pointTwo:location];
Сразу же получаем актуальные координаты прикосновения относительно оси джойстика
actualPoint= CGPointMake(location.x-self.position.x, location.y -self.position.y);
Если джойстик был активирован идем дальше
if (self.isJoystickEnabled) {
Чистая и вкусная точка.
CGPoint point;
Находим в какой четверти окружности находимся
- I) x+ y+
- II) x- y+
- III) x- y-
- IV) x+ y —
Закономерность если x <=0 , то x- ,если y <= 0 ,то y-fx и fy — это коэффиценты в какой четверти будут х и y на окружности. Помните тригонометрию ?
if (actualPoint.x <=0) { fx =-1; }else{ fx =1; } if (actualPoint.y <=0) { fy = -1; }else{ fy = 1; }
Если найденная дистанция меньше или равна радиуса окружности тогда выполняем код ниже.
if (distance<=self.radius) {
Находим точку я вам уже говорил что это за точка.Не помните ? Нужно читать внимательно!
point = CGPointMake(self.boundingBox.size.width/2 +actualPoint.x, self.boundingBox.size.height/2+actualPoint.y);
Передвигаем контролер!
[controller setPosition:point]; }else{
Здесь читать внимательно!
Если палец был не убран а передвижения не прекращаются и палец выпал(вышел) за приделы радиуса , то тогда мы рассчитываем чтоб контролер следил за пальцем.
Узнаем угловой коэффициент прямой, проходящей через две данные точки. 1 точка начала координат относительно джойстика , вторая точка точка прикосновения относительно оси координат джойстика.
CGFloat slope = [self slopeBetweenTwoPoints:CGPointMake(0, 0) pointTwo:actualPoint];
Если угол на клона равен ±∞ ,то тогда точка будет равна (0,R) , где R — радиус
Почему так происходит? Честно не помню . Единственное что приходит в голову когда прямая не под наклоном .
if (slope == (-INFINITY || INFINITY)) { point = CGPointMake(0, _radius); }else{
Иначе мы выключаем силу тьмы и вспоминаем тригонометрию.
- Уравнение прямой с угловым коэффициентом имеет вид y = kx+b, где k — угловой коэффициент прямой. Этот коэффициент и определяет угол наклона прямой. Этот коэффициент равен k = tgφ, где φ — угол между лучом прямой, расположенным выше оси абцисс и положительным направлением оси абцисс. Это и есть угол наклона прямой. Он равен φ = arctg(k).
- Абсцисса X, Ордината Y,R — радиус,Уголь альфа — это угловой коэффицент ,у нас slope;
Нам нужно узнать точку абсциссу X , мы знаем угловой коэффицент и радиус . А это значит x = cos(Å)*R; Следовательно x = cos(arctg(Å))*R;
Тоже самое для любимой ординаты sin(альфа) =Y/R Y = sin(arctg(альфа)) *R;
Не забываем нам нужен модуль X и Y для того чтоб в каждой части окружности делал свой поворот , тоесть направление против часовой стрелки считается положительным, по часовой стрелке отрицательным.
CGFloat newX = cosf(atanf(slope))*_radius; CGFloat newY = sinf(atanf(slope))*_radius;
Создаем точку
point =CGPointMake(ABS(newX), ABS(newY)); }
Мы умножаем на коэффициент четверти окружности .
point =CGPointMake(point.x*fx, point.y*fy);
Вспоминаем то что нам надо добавить еще половинку спрайта джойстика.
point = CGPointMake(self.boundingBox.size.width/2 +point.x,self.boundingBox.size.height/2+point.y);
Перемещаем контролер.
[controller setPosition:point]; } if ([self.delegate respondsToSelector:@selector(joystickControlMoved:)]) { [self.delegate joystickControlMoved:point]; } }
Метод:
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Здесь вызываем метод
[self ccTouchesCancelled:touches withEvent:event];
Мой совет лучше всего завершения прикосновения реализовать в ccTouchesCancelled , а в ccTouchesEnded вызывать ccTouchesCancelled.
Так надежнее ,если что нибудь случиться вызовется ccTouchesCancelled и корректно сбросит прикосновение.
Метод:
-(void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event if (self.isJoystickEnabled) {
Выключаем джойстик.
_isJoystickEnabled = false;
Вызываем метод делегата
if ([self.delegate respondsToSelector:@selector(joystickControlEnded:)]) { [self.delegate joystickControlEnded:controller.position]; }
Меняем позицию контролера на середину спрайта джойстика.
[controller setPosition:CGPointMake(self.boundingBox.size.width/2, self.boundingBox.size.height/2)];
Выключаем цикл который будет обрабатывать движения контролера и давать коэффициенты передвижения.
[[[CCDirector sharedDirector]scheduler]unscheduleSelector:@selector(update1:) forTarget:self]; } }
Метод:
-(void)onEnter [super onEnter];
Включаем обработчик прикосновения.
CCTouchDispatcher *touchDis= [[CCDirectorIOS sharedDirector] touchDispatcher]; [touchDis addStandardDelegate:self priority:0];
Метод:
-(void)onExit [super onExit];
Выключаем обработчик прикосновения.
CCTouchDispatcher *touchDis= [[CCDirectorIOS sharedDirector] touchDispatcher]; [touchDis removeDelegate:self];
Метод:
-(void)dealloc[controller release]; [self.delegate release]; [super dealloc];
Я думаю тут все понятно.
Метод:
-(void)update1:(ccTime)time{
Реализация проста здесь вызывается метод делегата и возращает множитель скорости.
if ([self.delegate respondsToSelector:@selector(joystickControlSpeedMultiplier)]) { [self setSpeedMultiplier:[self.delegate joystickControlSpeedMultiplier]]; }
Находим коэффициент прироста координат для изменение положения объекта Х и Y .
Косинусом угла α называется отношение ОА/OB (отношение прилежащего катета к гипотенузе).
Тогда получаем cos * умножаем на множитель скорости все это в модуле и умножаем на коэффициент четверти окружности .
|X/R*speedMultiplier| *fx ;
float xSpeedRatio = ABS(actualPoint.x /_radius*_speedMultiplier)*fx;
float ySpeedRatio = ABS(actualPoint.y/_radius*_speedMultiplier)*fy;
Здесь вызываем делегат и передаем коэффициент прироста координат.
if ([self.delegate respondsToSelector:@selector(joystickControlDidUpdate:toYSpeedRatio:)]) { [self.delegate joystickControlDidUpdate:xSpeedRatio toYSpeedRatio:ySpeedRatio]; }
Ну что думаю на этом все. Это моя 1 статья, надеюсь не последняя и не худшая. Всем удачи , и спасибо .