ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

20. Создание игр для iOS

Пишем игру для iPhone

 

В этом уроке я расскажу как написать несложную игру для iPhone без каких-либо сторонних фреймверков. Все, что вам для этого понадобиться — знание уроков, которые уже есть на нашем сайте и несколько картинок, которые можно скачать по этой ссылке. Создадим новый проект на основе шаблона Single View Application и назовем его PinPong (это будет игра, что-то вроде тенниса). Прежде чем объяснить сколько нам нужно переменных и для чего каждая из них служит, я напишу часть кода, точнее описание интерфейса класса игры, а после этого все по частям объясню:

 

ViewController.h

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;

#define AIMoveSpeed 5
#define defoultBallSpeed CGPointMake(8.0f, 8.0f)

typedef enum {
    Paused,
    Running,
} GameStates;

@interface ViewController : UIViewController {
    GameStates gameState;
    CGPoint ballSpeed;
}

@property (strong, nonatomic) IBOutlet UIImageView *ball;
@property (strong, nonatomic) IBOutlet UIImageView *myBoard;
@property (strong, nonatomic) IBOutlet UIImageView *opponentBoard;
@property (strong, nonatomic) IBOutlet UIButton *beginer;

- (IBAction)startGame;
- (void)resetGame;

@end</code>

 

А теперь по-подробнее. AIMoveSpeed и defoultBallSpeed представляют из себя дэфайны. В AIMoveSpeed мы храним скорость реакции компьютера (таким образом можно установить сложность игры), а в defoultBallSpeed — скорость движения мячика. Затем следует перечисление GameStates, оно нам понадобится для управления игрой. То есть, у игры может быть два состояния, игровой процесс и пауза. А в переменной gameState будет храниться текущее состояние игры. Несложно догадаться, что в ballSpeed мы будем ханить скорость мячика и его направление. Далее следуют объекты интерфейса (мячик, наша доска, доска противника и кнопка начала игры). Завершают описание интерфейса методы начала и сброса игры.

 

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

ViewController.m

<code data-result="[object Object]">#import "ViewController.h"

@implementation ViewController

@synthesize ball;
@synthesize myBoard;
@synthesize opponentBoard;
@synthesize beginer;

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.ball = nil;
    self.myBoard = nil;
    self.opponentBoard = nil;
    self.beginer = nil;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    gameState = Paused;
    ballSpeed = defoultBallSpeed;
    [NSTimer scheduledTimerWithTimeInterval:0.05f 
                                     target:self 
                                   selector:@selector(gameLoop) 
                                   userInfo:nil 
                                    repeats:YES];
}

- (IBAction)startGame {
    beginer.hidden = YES;
    gameState = Running;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{
    UITouch *touch = [[event allTouches] anyObject];
    CGPoint location = [touch locationInView:touch.view];
    CGPoint xLocation = CGPointMake(location.x, myBoard.center.y);
    myBoard.center = xLocation;
}

-(void)resetGame {
    gameState = Paused;
    ball.center = self.view.center;
}

@end</code>

 

Здесь я привел описание всех методов, кроме основного, в котором и происходит обработка всего игрового процесса. Но все по порядку. В первую очередь, мы синтезировали методы доступа и огранизовали очистку памяти в методе viewDidUnload. В методе viewDidLoadтекущее состояние игры ставим в режим паузы, задаем начальную скорость и направление мячика и стартуем таймер, который каждые 0.05 секунды будет вызывать метод упраления игрой. В метод startGame мы прячем кнопку, которая нужна для старта игры (beginer) и меняем состояние игры. О методе touchesMoved я уже писал в уроке Обработка касаний, в нем мы реализуем движение доски подобно тому как двигали надпись в вышеупомянутом уроке. А в методе resetGame происходит обратное методу startGame, только кроме смены состояния игры, мы переносим мячик на центр экрана.

 

Теперь рассмотрим тот самый метод gameLoop:

 

<code data-result="[object Object]">-(void)gameLoop {
    if(gameState == Running) {
        //Меняем положение мячика
        ball.center = CGPointMake(ball.center.x + ballSpeed.x, 
                                  ball.center.y + ballSpeed.y);

        //Отталкивание мячика от левого и правого краев экрана
        if(ball.center.x &gt;= self.view.bounds.size.width || ball.center.x &lt;= 0) {
            ballSpeed.x = -ballSpeed.x;
        }

        //Отталкивания мячика от верхнего и нижнего краев экрана
        if(ball.center.y &gt;= self.view.bounds.size.height || ball.center.y &lt;= 0) {
            ballSpeed.y = -ballSpeed.y;
        }

        //Отталкивание мячика от нашей доски
        if(CGRectIntersectsRect(ball.frame, myBoard.frame)) {
            if(ball.center.y+18.0f &gt; myBoard.center.y) {
                ballSpeed.y = -ballSpeed.y;
            }
        }

        //Отталкивание мячика от доски противника
        if(CGRectIntersectsRect(ball.frame, opponentBoard.frame)) {
            if(ball.center.y-18.0f &lt; opponentBoard.center.y) {
                ballSpeed.y = -ballSpeed.y;
            }
        }

        //Реализация игры бота
        if(ball.center.y &lt;= self.view.center.y) {
            if(ball.center.x &lt; opponentBoard.center.x) {
                CGPoint compLocation = CGPointMake(opponentBoard.center.x - AIMoveSpeed, 
                                                   opponentBoard.center.y);
                opponentBoard.center = compLocation;
            }

            if(ball.center.x &gt; opponentBoard.center.x) {
                CGPoint compLocation = CGPointMake(opponentBoard.center.x + AIMoveSpeed, 
                                                   opponentBoard.center.y);
                opponentBoard.center = compLocation;
            }
        }

        //Мы забили гол
        if(ball.center.y &lt;= 0) {
            [self resetGame];
        }

        //Нам забили гол
        if(ball.center.y &gt; self.view.bounds.size.height) {
            [self resetGame];
        }
    } else {
        if(beginer.hidden) {
            beginer.hidden = NO;
        }
    }
}</code>

 

В основном все изложено в комментариях, но я считаю, что было бы правильно высветить некоторые моменты. Метод CGRectIntersectsRect вам скорее всего не известен. Он принимает два параметра (фрейм двух объектов), а возвращает логическое значение (положительное в случае если фреймы этих объектов пересекаются, и отрицательное если нет). Таким образом, мы определяем, каснулся мячик доски или нет. Следующий вопрос, который вас может озадачить — это работа бота. Доска бота начинает двигаться после того, как мячик пересек центр экрана по горизонтали. Далее мы проверяем направление в котором летит мячик. И в зависимости от этого направления смещаем доску бота на шаг, который установлен в AIMoveSpeed.

 

Осталось внести изменений в xib-файл, чтобы он был похож на картинку в начале урока и настроить в нем все связи.

 

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

Comments are closed.