В этой последней части мы закончим делать собственную игру для приставки Xbox 360 и добавим в исходный код программы недостающие элементы. Сегодня нам предстоит изучить много разного материала, в частности механизм смены игровых состояний, добавление в игру заставки и меню, реализацию окончания игры. Кроме того, нас ждет небольшая реструктуризация программного кода, связанная со стартом игры. Чтобы было легче работать с исходным кодом программы, весь сегодняшний материал разбит на логические части, и начнем мы с изучения смены состояния игры.
Смена состояния игры
Смена состояния игры на самом деле подразумевает простой переход в программе, допустим, из меню в игровой процесс или перевод игрового процесса в режим паузы и так далее. То есть, любой переход в игре ведет к естественной смене ее состояния. На этом принципе и строится основной подход реализации игровых заставок, меню, режима паузы и многого другого (рис. 1). Чтобы осуществить в нашей игре смену игровых состояний, в исходном коде необходимо сформировать структуру данных, содержащую определенный набор переменных. Эта структура и ее структурные переменные будут отвечать за смену состояний в игре. Объявление структуры в исходном коде программы приводит к созданию набора данных. Эти данные впоследствии могут использовать любой объект структуры, или, как принято говорить, структурные переменные. Откройте с DVD-диска новый проект Menu и давайте будем вместе следить за изменениями исходного кода программы. Прежде всего, чтобы создать нужную нам структуру в игре, мы запишем в исходном коде класса Game1 вот такой блок кода:
private enum CurentGameState
{
SplashScreen,
MenuScreen,
GameScreen,
}
Как видно из объявления, названия трех переменных соответствуют определенным фазам игрового состояния. В нашем примере эти переменные означают вот что:
SplashScreen – это первая игровая заставка, появляющаяся при старте игры. Эта заставка, как правило, отображает на экране название работающей над проектом компании, спонсоров, рекламодателей…
MenuScreen – это наше игровое меню, с помощью которого мы будем запускать игру.
GameScreen – это весь игровой процесс.
При таком подходе при старте игры в исходном коде класса Game1 создается структурная переменная gameState и инициализируется значением SplashScreen, что в итоге приводит нас к показу первой заставки в игре.
Чтобы в программе перейти из состояния показа заставки к состоянию показа меню, нам необходимо просто создать проверку значения переменной gameState в игровом цикле:
switch(gameState)
{
case CurentGameState.SplashScreen:
{
// заставка
break;
}
case CurentGameState.MenuScreen:
{
// меню
break;
}
case CurentGameState.GameScreen:
{
// играем
break;
}
}
Теперь, где бы мы ни находились в игре, для перехода из одного состояния в другое нам нужно будет изменить значение переменной gameState на необходимое в данный момент. Как именно это делается, вы узнаете позже, а сейчас для продолжения работы над игрой необходимо внести в код класса Game1 небольшое изменение.
Рис. 1Рис. 6
Новая игра
Поскольку в игру у нас добавился механизм смены игрового состояния, нам необходимо все начальные инициализации игровых объектов, которые находятся в методе Initialize(), перенести в новый метод под названием NewGame(). Что это даст? Дело в том, что теперь пользователь из игрового процесса может в любой момент вернуться в меню, а затем опять запустить игру. Если мы оставим инициализацию игровых объектов в методе Initialize(), то эта самая инициализация будет происходить только единожды в самом начале старта игры. Поэтому, чтобы пользователь имел возможность при каждом новом запуске игры из меню программы начинать игровой процесс вновь и вновь, создается новый метод NewGame(). Вызов этого метода непосредственно на этапе выбора команды меню «Играть» приведет игру в первоначальное состояние инициализации всех игровых объектов. Посмотрите на исходный код класса Game1 и метод NewGame() внимательно, как видно, все изменения связаны только с переносом блока кода из метода Initialize() в новый метод NewGame().
Добавляем в игру заставку и меню
Теперь пришло время добавить в игру заставку и меню, а также закончить написание исходного кода для перехода от одного игрового состояния к другому. Первоначально в исходном коде Game1 мы объявим три новых объекта splash, start и menu класса Texture2D. Эти три объекта будут содержать изображение заставки, надпись «Нажмите START» и меню игры.
public Texture2D splash;
public Texture2D start;
public Texture2D menu;
Далее переходим к методу LoadGraphicsContent(), который отвечает за загрузку графики в игру, и делаем в его исходном коде следующие записи, не забывая предварительно добавить все новые графические изображения в каталог текущего проекта.
Рис. 2
Затем необходимо в методе Draw() написать код для рисования на экране телевизора, как заставки и надписи, так и меню игры. Делается это непосредственно в блоках кода, отвечающих за то или иное игровое состояние. Заставку и надпись игры мы рисуем тут:
case CurentGameState.SplashScreen:
{
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.Draw(splash, new Vector2(screenWidth/2 - splash.Width/2, screenHeight/2 - splash.Height/2), Color.White);
В нашей игре графическое изображение заставки рисуется прямо посередине экрана на черном фоне, которым у нас закрашивается весь экран (рис. 2). Дополнительная надпись «Нажмите START» показывает пользователю, какую именно он должен нажать кнопку джойстика, чтобы перейти из режима показа заставки в режим отображения игрового меню (рис. 3). Эта надпись рисуется внизу с отступом в 30 пикселей от нижней кромки экрана телевизора. В свою очередь, меню игры выполнено одним графическим изображением в размер экрана телевизора (рис. 4).
case CurentGameState.MenuScreen:
{
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.Draw(menu, new Vector2(0, 0), Color.White);
spriteBatch.End();
break;
}
Само графическое изображение меню имеет надписи с командами «Играть» и «Выход», и сейчас нам нужно позаботиться о реализации механизма управления программой непосредственно из меню игры.
Рис. 5
Механизм управления игрой
Переходим в исходном коде класса Game1 к методу Update() и смотрим, какие изменения претерпел код программы. Как видите, в исходном коде метода Update() была также добавлена проверка состояния игры на базе структурных переменных. Первый блок кода связан с игровой заставкой.
case CurentGameState.SplashScreen:
{
// Нажимаем кнопку Start
if (currentState.Buttons.Start == ButtonState.Pressed)
gameState = CurentGameState.MenuScreen;
break;
}
В этом блоке кода мы добавляем в игру обработку нажатия кнопки START на джойстике приставки Xbox 360. То есть первая игровая заставка будет показана на экране до тех пор, пока пользователь не нажмет кнопку START. Эта кнопка меняет значение переменной gameState на CurentGameState.MenuScreen, что незамедлительно переводит работу программы к показу меню игры или к смене текущего игрового состояния. Соответственно, в блоке кода меню игры мы добавляем опрос нажатия еще двух кнопок джойстика.
case CurentGameState.MenuScreen:
{
// Нажимаем кнопку А
if (currentState.Buttons.A == ButtonState.Pressed)
{
this.NewGame();
gameState = CurentGameState.GameScreen;
}
// Нажимаем кнопку B
if (currentState.Buttons.B == ButtonState.Pressed)
{
this.Exit();
}
break;
}Как видно, из этого блока кода мы создаем команды для двух кнопок – с буквами А и В. Когда пользователь нажимает кнопку с буквой В, проиcходит выход из игры. В свою очередь, когда пользователь нажимает кнопку с буквой А, начинается выполнение метода NewGame(), то есть происходит инициализация всех игровых объектов, а затем значение переменной gameState изменяется на значение CurentGameState.GameScreen, что приводит нас к запуску игрового процесса, который начинает работать уже в следующем блоке кода.
case CurentGameState.GameScreen:
{
// досрочный выход в меню игры
if (currentState.Buttons.Back == ButtonState.Pressed)
{
gameState = CurentGameState.MenuScreen;
}
….
break;
}
В этом блоке была опущена большая часть строк исходного кода, поскольку она попросту не вместилась бы в колонку журнала. Поэтому ознакомьтесь с кодом непосредственно в студии разработки игр. Как видно из кода, никаких изменений не произошло, лишь был добавлен блок, реализующий досрочный выход из игрового процесса в меню игры. Эта распространенная практика, имеющаяся в любой игре. Теперь нам осталось только рассмотреть две фазы окончания игры.
Рис. 4
Спасаем корабль
Обе фазы окончания игры содержат простейшую игровую логику окончания и продолжения всего игрового процесса. Так, для удачного завершения миссии или окончания игры, пользователю нужно будет выжить и дождаться появления на экране спасаемого космического корабля. Сам корабль в игре мы «закинем высоко наверх», на расстояние 5000 пикселей от нулевых координат экрана телевизора, и будем перемещать его сверху вниз со скоростью в два пикселя (рис. 5). Как только корабль дойдет до середины экрана, миссия считается завершенной и будет осуществлен выход в меню игры и воспроизведение звукового сигнала. Таким образом, мы задаем временной промежуток, отведенный пользователю на всю игру. Давайте посмотрим, как это реализовать на практике. Итак, вначале нам нужно нарисовать графическое изображение спасаемого корабля (рис. 6), добавить его в каталог проекта и написать несколько строк кода в разных местах класса Game1 для объявления объекта, его создания, загрузки изображения в игру, назначения позиции и отображения на экране.
shipBig.spritePosition = new Vector2(rand.Next(200, screenWidth - 200), -5000);
// рисуем спасаемый корабль на экране
shipBig.DrawSprite(spriteBatch);
Теперь нам нужно в методе Update() заставить двигаться корабль. Для этого мы будем просто уменьшать значение переменной shipBig.spritePosition.Y на два пикселя в каждой итерации игрового цикла.
shipBig.spritePosition.Y += 2;
Дополнительно в коде мы также организуем сначала проверку условия по достижении спасаемым кораблем середины экрана и, как следствие, успешного окончания миссии и выхода в меню игры.
if (shipBig.spritePosition.Y > screenHeight / 2)
{
gameState = CurentGameState.MenuScreen;
Sound.PlayCue(soundList.GameOver);
}
И проверку окончания жизней или жизненной энергии корабля.
// проверяем жизни корабля
if (life <= 0)
{
gameState = CurentGameState.MenuScreen;
Sound.PlayCue(soundList.GameOver);
}
В этом случае, если жизнь корабля или его защитное поле стало равно нулю, следует окончание всей игры. Конечно, в более сложном проекте вам обязательно стоит добавить еще ряд игровых состояний для показа заставок «Вы выиграли» или «Вы проиграли». В нашей несложной игре это сделано не было, поскольку исходный код программы тогда очень сильно усложнится, а он рассчитан на изучение простыми пользователями. Но если вас интересует более серьезный подход, то рекомендую обратиться к моим книгам по схожей тематике. Всю информацию по книгам вы найдете на моем сайте по адресу http://www.gornakov.ru. На этом изучение процесса создания собственной двухмерной игры подошло к концу. Надеюсь, предлагаемый материал показался вам интересным и вы сумели сделать свою первую игру для приставки Xbox 360. Желаю удачи и творческих успехов!
Рис. 3<<7-я часть «Игры своими руками»
1176 Прочтений • [Игра для Xbox 360 своими руками. Часть 8] [12.04.2012] [Комментариев: 0]