Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить..... |
Устройства ввода, перемещение объектов
Разработка игрового компонента с функциями перемещения и с ограничениями
Можно заметить, что в предыдущих примерах игровые объекты легко пересекали границы игрового поля. В реальных проектах обычно требуется, чтобы подвижный объект не пересекал этих границ. Это значит, что, во-первых – нам нужно узнать координаты границ экрана, а во-вторых – нужно создать такой код, отвечающий за перемещение объекта, который прежде чем переместить объект в новую позицию, проверял бы допустимость такого перемещения.
Создадим новый игровой проект (P3_4), аналогичный проекту P2_3, разработанному в лабораторной работе №2. Единственное отличие – мы не будем выводить фоновое изображение. Этот проект содержит игровой компонент, который в нашем примере и будет содержать весь необходимый код. Измененный код компонента вы можете видеть в листинге 7.7.
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using Microsoft.Xna.Framework.Content; namespace P3_4 { /// <summary> /// This is a game component that implements IUpdateable. /// </summary> public class spriteComp : Microsoft.Xna.Framework.DrawableGameComponent { private Texture2D sprTexture; private Rectangle sprRectangle; private Vector2 sprPosition; private Rectangle scrBounds; public spriteComp(Game game, ref Texture2D newTexture, Rectangle newRectangle, Vector2 newPosition) : base(game) { sprTexture = newTexture; sprRectangle = newRectangle; sprPosition = newPosition; scrBounds = new Rectangle(0, 0, game.Window.ClientBounds.Width, game.Window.ClientBounds.Height); // TODO: Construct any child components here } public override void Initialize() { // TODO: Add your initialization code here base.Initialize(); } public override void Update(GameTime gameTime) { KeyboardState kbState = Keyboard.GetState(); if (kbState.IsKeyDown(Keys.Up)) { sprPosition.Y -= 5; } if (kbState.IsKeyDown(Keys.Down)) { sprPosition.Y += 5; } if (kbState.IsKeyDown(Keys.Left)) { sprPosition.X -= 5; } if (kbState.IsKeyDown(Keys.Right)) { sprPosition.X += 5; } if (sprPosition.X < scrBounds.Left) { sprPosition.X = scrBounds.Left; } if (sprPosition.X > scrBounds.Width - sprRectangle.Width) { sprPosition.X = scrBounds.Width - sprRectangle.Width ; } if (sprPosition.Y < scrBounds.Top) { sprPosition.Y = scrBounds.Top; } if (sprPosition.Y > scrBounds.Height - sprRectangle.Height ) { sprPosition.Y = scrBounds.Height - sprRectangle.Height; } base.Update(gameTime); } public override void Draw(GameTime gameTime) { SpriteBatch sprBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White); base.Draw(gameTime); } } }Листинг 7.7. Код компонента, рассчитанного на перемещения с ограничениями
В разделе объявления переменных мы добавили новую переменную – scrBounds типа Rectangle. В этой переменной мы будем хранить параметры игрового окна. Мы инициализируем эту переменную в конструкторе класса. В качестве левой верхней координаты прямоугольника, ограничивающего игровое окно, мы записали (0,0), в качестве ширины – ширину игрового поля, полученную с помощью команды game.Window.ClientBounds.Width. Здесь объект game был передан в конструктор при создании игрового объекта, объект Window – это окно игры, ClientBounds содержит информацию о параметрах окна, в частности, о ширине (Width) и высоте (Height).
В методе Update() мы получаем состояние клавиатуры и, если нажаты соответствующие клавиши-стрелки, модифицируем позицию спрайта на экране. Однако здесь, после модификации позиции, мы проводим серию проверок. Например, проверяем, не меньше ли новая координата X игрового объекта координаты X прямоугольника, соответствующего игровому окну. Если эта координата меньше, это значит, что объект, при перемещении в соответствующую позицию, окажется левее левой границы экрана. Поэтому, если проверяемое условие выполняется – мы приравниваем координату левой границы экрана координате объекта. При попытке пересечения левой границы экрана объект "упирается" в нее.
Немного сложнее выглядит проверка на пересечение правой границы экрана. Как вы знаете, координаты X и Y прямоугольника, ограничивающего наш объект, соответствуют его левой верхней точке. Объект имеет ширину и высоту, поэтому для проверки пересечения правой границы экрана, мы сравниваем новую позицию объекта с разностью ширины экрана с шириной объекта. Если оказывается, что новая позиция больше, чем вычисленная разность – объект устанавливается в позицию, координата X объекта устанавливается равной разности ширины экрана и ширины объекта – он "упирается" в правую границу экрана своей правой частью.
Таким же образом проверяется координата Y – на пересечение объекта верхней и нижней границ экрана.
Как видите, вся логика по перемещению объекта и по проверке на допустимость перемещения, содержится в игровом компоненте и не перегружает основной программный код. Такой подход имеет свои плюсы и минусы. Плюс заключается в удобстве использования подобной конструкции. Но если мы создадим несколько игровых объектов на основе игрового компонента, то окажется, что все они перемещаются одновременно по нажатию на клавиши-стрелки.
Одинаковый механизм перемещения был бы удобен, если бы мы создали алгоритм автоматического перемещения объекта, который перемещал бы каждый из них в соответствии с каким-то законом, например – случайным образом. Если же нам нужно создать несколько объектов, обладающих самостоятельным управлением, используя один и тот же класс, этот механизм будет нуждаться в переработке. Например, в игре Pong предусматривается управление двумя битами – два пользователя, сидя за одной клавиатурой, управляют каждый своей битой с использованием собственных клавиш. Рассмотрим механизмы организации управления несколькими объектами.