Опубликован: 14.08.2012 | Уровень: специалист | Доступ: платный
Самостоятельная работа 12:

Искусственный интеллект в играх

18.3. Реализация алгоритма перемещения с обходом препятствий

Существует немало алгоритмов поиска пути на карте с препятствиями. Один из них заключается в следующем – объект двигается по карте, "держась рукой" за стену. Объект перемещается вдоль стен, выполняя повороты лишь в одну сторону, таким образом он гарантированно обойдет все места на карте, вдоль которых находятся стены или другие границы.

Это достаточно простой алгоритм, подходящий для несложных игр. В играх более сложных его применение может вызвать отрицательные эмоции у игрока – поэтому в таких играх следует применять более сложные алгоритмы. Например, для поиска кратчайшего пути между двумя точками можно применить популярный алгоритм A*, для исследования игрового мира – алгоритм на основе "сенсоров", которыми обладает игровой персонаж.

Мы используем комбинированный алгоритм – он рассчитан на действия в трех ситуациях. Во-первых, если объект-преследователь "видит" объект-цель – он перемещается к ней в свободном пространстве – так же, как в вышеописанном примере. Если объект-преследователь теряет цель – например – она ушла из пределов прямой досягаемости – он действует различным образом в зависимости от того, находится ли он в свободном пространстве или около стены или границы экрана. Если преследователь находится в свободном пространстве – он перемещается в нем на случайные расстояния, делая повороты. Если он находится около стены – он продолжает обход игрового мира. Как правило, объект, находящийся в свободном пространстве некоторое время "блуждает" по нему – если он снова "увидит" объект-цель – он начнет преследовать его, если он столкнется со стеной или с границей экрана – он продолжит обход игрового мира.

Создадим новый проект P12_2 на основе проекта P12_1. Ранее объект-преследователь перемещался в направлении объекта-цели, мог передвигаться вдоль вертикальных "стен", но даже простейшая конфигурация из препятствий была способна задержать его. Теперь же мы реализуем в объекте-преследователе алгоритм обхода препятствий

Состав проекта, по сравнению с проектом P12_1, не изменился, однако код объектов и основного игрового класса претерпел некоторые изменения. Ниже приведен полный код проекта P12_2.

В листинге 18.6 приведен код класса Game1.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace P12_2
{
    /// <summary>
    /// Это главный тип игры
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D txtBackground;

        Texture2D txtEnemy;

        Texture2D txtMe;
        Texture2D txtWall;
        Texture2D txtArrows;
        SpriteFont myFont;
        //Массив для конструирования уровня
        public int[,] Layer;
        Rectangle recBackround = new Rectangle(16, 0, 768, 448);
        Rectangle recSprite = new Rectangle(0, 0, 64, 64);
        Rectangle recArrows = new Rectangle(500, 280, 300, 200);
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.IsFullScreen = true;
            // Частота кадра на Windows Phone по умолчанию — 30 кадров в секунду.
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            // Дополнительный заряд аккумулятора заблокирован.
            InactiveSleepTime = TimeSpan.FromSeconds(1);
        }

        /// <summary>
        /// Позволяет игре выполнить инициализацию, необходимую перед запуском.
        /// Здесь можно запросить нужные службы и загрузить неграфический
        /// контент.  Вызов base.Initialize приведет к перебору всех компонентов и
        /// их инициализации.
        /// </summary>
        protected override void Initialize()
        {
            Layer = new int[7, 12] { 
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, 
            { 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0 }, 
            { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
            { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, 
            { 0, 5, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1 }, 
            { 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 6 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            };

            base.Initialize();
        }

        /// <summary>
        /// LoadContent будет вызываться в игре один раз; здесь загружается
        /// весь контент.
        /// </summary>
        protected override void LoadContent()
        {
            // Создайте новый SpriteBatch, который можно использовать для отрисовки текстур.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Services.AddService(typeof(SpriteBatch), spriteBatch);
            txtBackground = Content.Load<Texture2D>("background");
            txtEnemy = Content.Load<Texture2D>("enemy");
            txtMe = Content.Load<Texture2D>("me");
            txtWall = Content.Load<Texture2D>("wall");
            txtArrows = Content.Load<Texture2D>("arrows");
            myFont = Content.Load<SpriteFont>("MyFont");
            //Вызываем процедуру расстановки объектов в игровом окне
            AddSprites();

        }

        void AddSprites()
        {
            //Переменные для временного хранения адреса
            //объекта-игрока
            int a = 0, b = 0;
            //Просматриваем массив Layer
            for (int i = 0; i < 7; i++)
            {
                for (int j = 0; j < 12; j++)
                {
                    //Если элемент с индексом (i,j) равен 1 - 
                    //устанавливаем в соответствующую позицию элемент с
                    //номером 1, то есть - стену
                    if (Layer[i, j] == 1)
                        Components.Add(new GameObjects.Wall(this, ref txtWall, new Vector2(j, i), recSprite, myFont));

                    if (Layer[i, j] == 5)
                        Components.Add(new GameObjects.Enemy(this, ref txtEnemy, new Vector2(j, i), new Rectangle(0, 0, 32, 32), myFont));
                    //Если обнаружен объект игрока - запишем его координаты
                    if (Layer[i, j] == 6)
                    {
                        a = i;
                        b = j;
                    }
                }
            }
            //Последним установим объект игрока - так он гарантированно
            //расположен поверх всех остальных объектов
            Components.Add(new GameObjects.Me(this, ref txtMe, new Vector2(b, a), new Rectangle(0, 0, 32, 32), myFont));
        }

        /// <summary>
        /// UnloadContent будет вызываться в игре один раз; здесь выгружается
        /// весь контент.
        /// </summary>
        protected override void UnloadContent()
        {
            txtBackground.Dispose();
            txtEnemy.Dispose();
            txtMe.Dispose();
            txtWall.Dispose();
            spriteBatch.Dispose();
        }

        /// <summary>
        /// Позволяет игре запускать логику обновления мира,
        /// проверки столкновений, получения ввода и воспроизведения звуков.
        /// </summary>
        /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
        protected override void Update(GameTime gameTime)
        {
            // Позволяет выйти из игры
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // ЗАДАЧА: добавьте здесь логику обновления

            base.Update(gameTime);
        }

        /// <summary>
        /// Вызывается, когда игра отрисовывается.
        /// </summary>
        /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // ЗАДАЧА: добавьте здесь код отрисовки
            spriteBatch.Begin();
            //выведем фоновое изображение
            spriteBatch.Draw(txtBackground, recBackround, Color.White);
            //Выведем игровые объекты
            base.Draw(gameTime);
            //Стрелки
            spriteBatch.Draw(txtArrows, recArrows, Color.White);
            spriteBatch.End();

        }
    }
}
Листинг 18.6. Код класса Game1

На рис. 18.3 вы можете видеть карту этого проекта.

Карта проекта P12_2

Рис. 18.3. Карта проекта P12_2
Гулич Анна
Гулич Анна
Невозможно пройти тесты, в окне с вопросами пусто
Сашечка Огнев
Сашечка Огнев
Россия, Красноярский край
Андрей Корягин
Андрей Корягин
Россия, Пенза, Вазерская средняя школа, 2001