В предыдущих работах мы занимались созданием примеров компьютерных игр, которые состояли из одного игрового уровня. Реальные же игры обычно состоят из нескольких уровней. В этой лабораторной работе мы займемся разработкой многоуровневых игр и вопросами, связанными с созданием конструктора игровых уровней.
Цель работы
Научиться создавать многоуровневые игры
Задачи работы
Создать игру, игровой процесс которой делится на несколько уровней
Разработать конструктор уровней
Организация многоуровневой игры
В рамках этой лабораторной работы мы создадим игру – клон популярной игры Arkanoid. Наша игра будет обладать следующим набором характеристик.
Имеется 2 вида блоков – один из них уничтожается при попадании в него мяча, причем, мяч отскакивает, второй – не уничтожается.
При уничтожении всех блоков текущего уровня осуществляется переход на следующий уровень.
Ппосле того, как мяч выйдет за пределы поля – игра окончится и произойдет загрузка первого уровня.
Игрок может играть в стандартном режиме и в режиме пользовательского уровня. Пользовательский уровень создается игроком самостоятельно с помощью конструктора уровней.
Здесь мы создадим каркас игры и конструктор уровней для создания пользовательских уровней. Создадим новый проект – P11_1. На рис. 11.1. вы можете видеть окно Project Explorer этого проекта.
Рис. 11.1. Окно Project Explorer для проекта P11_1
Игровая функциональность и функциональность конструктора уровней реализована в классе Game1. Класс gBaseClass используется в качестве базового класса для игровых объектов. В предыдущих работах мы уже пользовались подобным классом, здесь мы расширили его функциональность для того, чтобы он позволял размещать на экране объекты, которым соответствуют спрайты различных размеров.
Классы Ball, Bat, Brick1, Brick2 – это игровые компоненты, соответственно, для мяча (Ball), биты (Bat), и двух видов блоков – обычного, который уничтожается при попадании в него мяча (Brick1), и неуничтожимого (Brick2). Класс BrickBuilder используется для создания указателя, который применяется в конструкторе уровней.
На рис. 11.2. вы можете видеть изображения графических ресурсов, примененных в игре.
Рис. 11.2. Игровые ресурсы
Изображения BrickBuild используются в конструкторе уровней. Указатель с изображением BrickBuild0.png очищает ячейку игрового поля, расположенную под ней. Указатель с BrickBuild1.png устанавливает в текущую позицию игрового поля объект Brick1, указатель с изображением BrickBuild2.png устанавливает в текущую ячейку объект Brick2.
На рис. 11.3. вы можете видеть игровой экран.
Рис. 11.3. Игровой экран проекта P11_1
На рис. 11.4 представлен экран в режиме конструктора уровней.
Рис. 11.4. Конструктор уровней
Рассмотрим коды классов, которые используются в игре.
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Audio;usingMicrosoft.Xna.Framework.Content;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Input;namespace P11_1{publicclass Game1 : Microsoft.Xna.Framework.Game{ GraphicsDeviceManager graphics; SpriteBatch spriteBatch;//Текстуры Texture2D txtBack, txtBrick1, txtBrick2, txtBat, txtBall; Texture2D txtBrBuild1, txtBrBuild2, txtBrBuild0;//Массивы для хранения данных об уровняхpublicbyte[,] Layer1, Layer2, Layer3, UserLayer;//Прямоугольник для фона Rectangle recBack =new Rectangle(0, 0, 512, 640);//Прямоугольник для "блока" Rectangle recBrick =new Rectangle(0, 0, 64, 16);//Прямоугольник для "мяча" Rectangle recBall =new Rectangle(0, 0, 10, 10);//Прямоугольник для биты Rectangle recBat =new Rectangle(0, 0, 96, 16);//Номер текущего уровня//1 - первый//2 - второй//3 - третий//-1 - пользовательскийint currentLayer;//Установлена в false, если программа находится в //режиме игры, при установке в True включается режим конструктораbool IsBuild;//Позиция указателя в режиме конструктора Vector2 PositionBuild;//Тип блока, который будет добавлен на игровое поле//при нажатии соответствующей клавиши в режиме конструктораbyte typeOfBuildBlock;public Game1(){ graphics =new GraphicsDeviceManager(this); Content.RootDirectory="Content";}protectedoverridevoid Initialize(){//Программа находится в игровом режиме IsBuild = false;//Позиция для указателя в режиме конструктора (0,0) PositionBuild =new Vector2(0, 0);//Тип блока по умолчанию в режиме конструктора - 1 typeOfBuildBlock =1;//пользовательский уровень по умолчанию пуст - ячейки//заполнены нулями UserLayer =newbyte[40, 8];//Заполняем массивы для разных уровней//0 - пустая ячейка//1 - блок первого типа (уничтожимый)//2 - блок второго типа (неуничтожимый) Layer1 =newbyte[40, 8]{{1,1,1,1,1,1,1,1},{0,1,0,1,0,1,0,1},{1,0,1,0,1,0,1,0},{0,1,0,1,0,1,0,1},{1,0,1,0,1,0,1,0},{0,1,0,1,0,1,0,1},{0,0,0,0,0,0,0,0},{2,0,0,2,0,0,2,0},{0,1,0,0,0,0,0,1},{0,0,1,0,0,0,1,0},{0,0,0,1,0,1,0,0},{0,0,0,0,1,0,0,0},{1,1,1,1,1,1,1,1},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}}; Layer2 =newbyte[40, 8]{{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,1,2,1,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}}; Layer3 =newbyte[40, 8]{{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,1,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,1,0,1,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,2,0,1,0,2,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,1,0,0,0,1,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};//Текущий уровень - 1 currentLayer =1;//Устанавливаем разрешение игрового окна 640х512 graphics.PreferredBackBufferWidth=512; graphics.PreferredBackBufferHeight=640; graphics.ApplyChanges();base.Initialize();}/// /// LoadContent will be called once per game and is the place to load/// all of your content./// protectedoverridevoid LoadContent(){// Загружаем ресурсы spriteBatch =new SpriteBatch(GraphicsDevice); Services.AddService(typeof(SpriteBatch), spriteBatch); txtBack = Content.Load<Texture2D>("Back"); txtBrick1 = Content.Load<Texture2D>("Brick1"); txtBrick2 = Content.Load<Texture2D>("Brick2"); txtBall = Content.Load<Texture2D>("Ball"); txtBat = Content.Load<Texture2D>("Bat"); txtBrBuild0 = Content.Load<Texture2D>("BrickBuild0"); txtBrBuild1 = Content.Load<Texture2D>("BrickBuild1"); txtBrBuild2 = Content.Load<Texture2D>("BrickBuild2");//Создаем уровень AddSprites(Layer1);//Добавляем биту и мяч addBallAndBAts();}//Процедура создания уровня//расставляет элементы в соответствии с массивомvoid AddSprites(byte[,] RenderLayer){//Просматриваем массивfor(int i =0; i <40; i++){for(int j =0; j <8; j++){if(RenderLayer[i, j]==1) Components.Add(new Brick1(this, ref txtBrick1, new Vector2(j, i), recBrick, 64, 16));if(RenderLayer[i, j]==2) Components.Add(new Brick2(this, ref txtBrick2, new Vector2(j, i), recBrick, 64, 16));}}}//Процедура для установки биты и мячаvoid addBallAndBAts(){ Components.Add(new Ball(this, ref txtBall, new Vector2(300, 550), recBall, 1, 1)); Components.Add(new Bat(this, ref txtBat, new Vector2(300, (640-16)), recBat, 1, 1));}protectedoverridevoid UnloadContent(){// TODO: Unload any non ContentManager content here}protectedoverridevoid Update(GameTime gameTime){//получим состояние клавиатуры KeyboardState kb = Keyboard.GetState();//если IsBuild = false и нажата клавиша A//переходим в режим конструктора уровнейif(kb.IsKeyDown(Keys .A)&!IsBuild){ IsBuild = true; builder();}//Если находимся в режиме конструктора уровнейif(IsBuild){//При нажатой клавише "вверх" уменьшить текущую позицию Y//указателя на 1 если позиция больше 0if(kb.IsKeyDown(Keys.Up)){if(PositionBuild.Y>0) PositionBuild.Y--;}//При нажатой клавише "вниз" увеличить позицию//Y если она меньше 39if(kb.IsKeyDown(Keys.Down)){if(PositionBuild.Y<39) PositionBuild.Y++;}//При нажатой клавише "влево"//уменьшить позицию X если текущая позиция//больше 0if(kb.IsKeyDown(Keys.Left)){if(PositionBuild.X>0) PositionBuild.X--;}//При нажатой клавише "вправо"//увеличить позицию X если текущая позиция меньше 7if(kb.IsKeyDown(Keys.Right)){if(PositionBuild.X<7) PositionBuild.X++;}//Нажатие клавиши Z устанавливает тип блока в 0//этот блок предназначен для "стирания"//других блоков, то есть - для установки элемента //массива, соответствующего текущему состоянию //конструируемого уровня, в 0if(kb.IsKeyDown(Keys.Z)){ typeOfBuildBlock =0;}//При нажатии клавиши X установим 1-й тип блокаif(kb.IsKeyDown(Keys.X)){ typeOfBuildBlock =1;}//При нажатии клавиши С установим 2-й тип блокаif(kb.IsKeyDown(Keys.C)){ typeOfBuildBlock =2;}//При нажатии клавиши W запишем в //ячейку массива, соответствующую позиции указателя//текущий тип блокаif(kb.IsKeyDown(Keys .W)){ UserLayer[(int)PositionBuild.Y, (int)PositionBuild.X]= typeOfBuildBlock;}//очищаем массив компонентов Components.Clear();//выводим пользовательский уровень AddSprites(UserLayer);//уничтожаем указатель и изменяем его в соответствии с типом выбранного блока BrickBuilder br = null;if(typeOfBuildBlock ==1) br =new BrickBuilder(this, ref txtBrBuild1, PositionBuild, recBrick, 64, 16);if(typeOfBuildBlock ==2) br =new BrickBuilder(this, ref txtBrBuild2, PositionBuild, recBrick, 64, 16);if(typeOfBuildBlock ==0) br =new BrickBuilder(this, ref txtBrBuild0, PositionBuild, recBrick, 64, 16); Components.Add(br);//При нажатии клавиши W//отключаем режим конструктора//выводим пользовательский уровень и начинаем игруif(kb.IsKeyDown(Keys.E)){ IsBuild = false; Components.Clear(); AddSprites(UserLayer); addBallAndBAts(); currentLayer =-1;}}//Если находимся в режиме игры//проверим - завершен ли текущий уровень//признак завершения уровняif(!IsBuild){ IsLayerCompleted();}base.Update(gameTime);}//Процедура старта конструктора уровней//очищаем набор компонентов Создаем и выводим указательpublicvoid builder(){ Components.Clear(); BrickBuilder br =new BrickBuilder (this, ref txtBrBuild1,PositionBuild, recBrick, 64, 16); Components.Add(br);}//Процедура проверки на завершенность текущего уровня//Если на поле отсутствует мяч - игра начинается сначала//если на поле нет ни одного блока типа Brick1 - //переход на следующий уровень или переход на первый уровень//с выводом надписи "Вы выиграли"publicvoid IsLayerCompleted(){//Предположим, что на поле нет ни одного блока//типа Brick1bool f = false;//Предположим, что на поле нет мячаbool Ball = false;foreach(gBaseClass spr inthis.Components){//Если найден хотя бы один блок типа 1//установим F в Trueif(spr.GetType()==(typeof(Brick1))){ f = true;}//Если найден мяч - установим Ball в Trueif(spr.GetType()==(typeof(Ball))){ Ball = true;}}//Если мяч не обнаружен игра начинается сначалаif(!Ball){ Components.Clear(); currentLayer =1; AddSprites(Layer1); addBallAndBAts();}//иначеelse{//Если не было найдено ни одного блока типа Brick1if(!f){//Очистим набор компонентов Components.Clear();//Увеличим номер уровня currentLayer++;//в зависимости от номера//запустим соответствующий уровеньif(currentLayer ==2){ AddSprites(Layer2); addBallAndBAts();}if(currentLayer ==3){ AddSprites(Layer3); addBallAndBAts();}//4 - если был пройден 3-й уровень//0 - при прохождении пользовательского уровняif(currentLayer ==4| currentLayer ==0){ currentLayer =1; AddSprites(Layer1); addBallAndBAts();this.Window.Title="Вы выиграли!";}}}}protectedoverridevoid Draw(GameTime gameTime){ spriteBatch.Begin();//выведем фоновое изображение spriteBatch.Draw(txtBack, recBack, Color.White);//Выведем игровые объектыbase.Draw(gameTime); spriteBatch.End();}}}
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Audio;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Input;usingMicrosoft.Xna.Framework.Content;namespace P11_1{publicclass gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent{ Texture2D sprTexture;public Vector2 sprPosition;public Rectangle sprRectangle;public gBaseClass(Game game, ref Texture2D _sprTexture, Vector2 _sprPosition, Rectangle _sprRectangle, int X_Coord, int Y_Coord):base(game){ sprTexture = _sprTexture;//Здесь производится перевод индекса элемента массива//в координаты на игровом экране//Так как спрайты имеют различные размеры, для их размещения на экране//используются коэффициенты X_Coord и Y_Coord sprPosition.X= _sprPosition.X* X_Coord; sprPosition.Y= _sprPosition.Y* Y_Coord; sprRectangle = _sprRectangle;}publicoverridevoid Draw(GameTime gameTime){ SpriteBatch sprBatch =(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); sprBatch.Draw(sprTexture, sprPosition, Color.White);base.Draw(gameTime);}}}
В листинге 11.3. приведен код класса Bat. Он занимается обработкой перемещений биты по горизонтали и контролем за пересечением битой левой и правой границ экрана.
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Audio;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Input;usingMicrosoft.Xna.Framework.Content;namespace P11_1{publicclass Ball : gBaseClass{//для хранения границ экрана Rectangle scrBounds;//скорость мячаpublic Vector2 speed; Random rand =new Random();public Ball(Game game, ref Texture2D _sprTexture, Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c):base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c){ speed =new Vector2(1, -1); scrBounds =new Rectangle(0, 0, game.Window.ClientBounds.Width, game.Window.ClientBounds.Height);}//Проверка на столкновение с границами экранаvoid Check(){if(sprPosition.X< scrBounds.Left){ sprPosition.X= scrBounds.Left; RandomiseSpeed(); speed.X*=-1;}if(sprPosition.X> scrBounds.Width- sprRectangle.Width){ sprPosition.X= scrBounds.Width- sprRectangle.Width; RandomiseSpeed(); speed.X*=-1;}if(sprPosition.Y< scrBounds.Top){ sprPosition.Y= scrBounds.Top; RandomiseSpeed(); speed.Y*=-1;}if(sprPosition.Y> scrBounds.Height- sprRectangle.Height){this.Dispose();}}//Проверка на столкновение с объектомbool IsCollideWithObject(gBaseClass spr){return(this.sprPosition.X+this.sprRectangle.Width> spr.sprPosition.X&&this.sprPosition.X< spr.sprPosition.X+ spr.sprRectangle.Width&&this.sprPosition.Y+this.sprRectangle.Height> spr.sprPosition.Y&&this.sprPosition.Y< spr.sprPosition.Y+ spr.sprRectangle.Height);}//Процедуры случайного изменения скорости при контакте с объектами//Для всех объектов - скорость может либо возрасти, либо упастьvoid RandomiseSpeed(){ speed.Y+=(float)((rand.NextDouble()- rand.NextDouble())*0.2); speed.X+=(float)((rand.NextDouble()- rand.NextDouble())*0.2);}//Для объектов типа Brick1 скорость лишь растетvoid RandomiseSpeedB1(){ speed.Y+=(float)(0.3*Math.Sign(speed.Y)* Math.Abs(((rand.NextDouble()- rand.NextDouble())))); speed.X+=(float)(0.3*Math.Sign(speed.X)* Math.Abs(((rand.NextDouble()- rand.NextDouble()))));}//Проверка на столкновенияpublicvoid IsCollide(){ gBaseClass FindObj=null; Bat FindedBat;foreach(gBaseClass spr in Game.Components){if(IsCollideWithObject(spr)){//Если столкнулись с блоком Brick1if(spr.GetType()==(typeof(Brick1))){//сохраним найденный объект FindObj = spr;//Изменим скорость RandomiseSpeedB1();//Обратим скорость speed *=-1;}//Если столкнулись с блоком 2//обращаем скоростьif(spr.GetType()==(typeof(Brick2))){ speed *=-1;}//если столкнулись с битойif(spr.GetType()==(typeof(Bat))){ FindedBat =(Bat) spr; RandomiseSpeed();//Если бита двигалась влево//Мяч отразится от нее влевоif(FindedBat.direction){ speed.Y= Math.Abs(speed.Y); speed.X= Math.Abs(speed.X); speed *=-1;}//Если двигалась вправо//отразится вправоelse{ speed.Y= Math.Abs(speed.Y); speed.X= Math.Abs(speed.X); speed.Y*=-1;}}}}//Если был найден блок Block1if(FindObj !=null){//Уничтожить его FindObj.Dispose(); FindObj = null;}}publicoverridevoid Update(GameTime gameTime){// TODO: Add your update code here//Переместить мяч в соответствии со скоростьюthis.sprPosition+=this.speed;//Ограничить скорость мяча 1.5 пикселямиif(speed.Y>2) speed.Y=(float)2;if(speed.Y<-2) speed.Y=(float)-2;if(speed.X>2) speed.X=(float)2;if(speed.X<-2) speed.X=(float)-2;//Проверить столкновение с границами Check();//проверить столкновение с объектами IsCollide();base.Update(gameTime);}}}
Листинг 11.5. содержит коды классов Brick1, Brick2 и BrickBuilder. Эти классы наследованы от gBaseClass и не вносят в его функциональность никаких дополнений.
Листинг 11.5. Коды классов Brick1, Brick2 и BrickBuilder
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Audio;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Input;usingMicrosoft.Xna.Framework.Content;namespace P11_1{publicclass Brick1 : gBaseClass{public Brick1(Game game, ref Texture2D _sprTexture, Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c):base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c){// TODO: Construct any child components here}}publicclass Brick2 : gBaseClass{public Brick2(Game game, ref Texture2D _sprTexture, Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c):base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c){// TODO: Construct any child components here}}publicclass BrickBuilder : gBaseClass{public BrickBuilder(Game game, ref Texture2D _sprTexture, Vector2 _sprPosition, Rectangle _sprRectangle, int x_c, int y_c):base(game, ref _sprTexture, _sprPosition, _sprRectangle, x_c, y_c){// TODO: Construct any child components here}}}
Выводы
В этой лабораторной работе мы создали игру, предусматривающую несколько игровых уровней и конструктор уровней для пользователя. Как правило, разработка реальных игр включает в себя создание конструктора уровней, с помощью которого разработчик создает игровые уровни. Этот конструктор можно включить в поставку игры, предоставив пользователю возможность самостоятельно создавать новые уровни к игре. Конструкторы уровней так же разнообразны, как сами игры.
Задание
В этой лабораторной работе мы создали макет игры, допускающей использование нескольких уровней и содержащей конструктор уровней. Доработайте этот макет. А именно, выполните следующие улучшения исходного проекта:
Переработайте процедуры обработки столкновений мяча с блоками. Мяч должен отскакивать от блоков точно так же, как он отскакивает от границ экрана.
Создайте интерфейс игры – меню, системусправки.
Предусмотрите сохранение пользовательского уровня в файл (с помощью сериализации специально созданного объекта) и загрузку его из файла.
Создайте систему подсчета очков, предусмотрите сохранение данных о набранных очках в файле.
Улучшите процесс прохождения игры – разработайте дополнительные уровни, сделайте игровой процесс разнообразнее, разработав бонусные объекты, которые появляются при уничтожении некоторых из блоков. Игрок должен «поймать» бонусный объект битой для того, чтобы активировать соответствующий бонус.
1205 Прочтений • [Уровни в играх] [08.08.2012] [Комментариев: 0]