Опубликован: 14.08.2012 | Доступ: свободный | Студентов: 880 / 20 | Оценка: 5.00 / 5.00 | Длительность: 09:59:00
Специальности: Программист
Самостоятельная работа 12:

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

В листинге 18.7 вы можете найти код класса gBaseClass

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.Media;


namespace P12_2.GameObjects
{
    /// <summary>
    /// Это игровой компонент, реализующий интерфейс IUpdateable.
    /// </summary>
    public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
    {
        Texture2D sprTexture;
        public Vector2 sprPosition;
        public Rectangle sprRectangle;
        public SpriteFont myFont;
        public string message="";
        //Прямоугольник, представляющий игровое окно
        public Rectangle scrBounds;
        //Скорость объекта
        public float sprSpeed;
        public gBaseClass(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle, SpriteFont _myFont)
            : base(game)
        {
            sprTexture = _sprTexture;
            //Именно здесь производится перевод индекса элемента массива
            //в координаты на игровом экране
            sprPosition = _sprPosition * 64;
            sprPosition.X += 16;
            sprRectangle = _sprRectangle;
            myFont = _myFont;
            scrBounds = new Rectangle(16, 0, 768, 448);
        }

        /// <summary>
        /// Позволяет игровому компоненту выполнить необходимую инициализацию перед\r\запуском.  
        Здесь можно запросить нужные службы и загрузить контент.
        /// 
        /// </summary>
        public override void Initialize()
        {
            // ЗАДАЧА: добавьте здесь код инициализации

            base.Initialize();
        }

        /// <summary>
        /// Позволяет игровому компоненту обновиться.
        /// </summary>
        /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
        public override void Update(GameTime gameTime)
        {
            // ЗАДАЧА: добавьте здесь код обновления

            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            SpriteBatch sprBatch =
                (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            sprBatch.Draw(sprTexture, sprPosition, Color.White);
            //Если строка сообщения не пуста
            if (this.message != "")
            {
                sprBatch.DrawString(myFont, message, new Vector2(0, 455), Color.Red);
            }
            base.Draw(gameTime);
        }
        //Проверка на столкновение с каким-либо объектом
        public 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);
        }
        //Процедуры для перемещения объекта
        public void MoveUp(float speed)
        {
            this.sprPosition.Y -= speed;
        }
        public void MoveDown(float speed)
        {
            this.sprPosition.Y += speed;
        }
        public void MoveLeft(float speed)
        {
            this.sprPosition.X -= speed;
        }
        public void MoveRight(float speed)
        {
            this.sprPosition.X += speed;
        }
        //Проверка на столкновение со стеной
        public bool IsCollideWithWall()
        {
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Wall)))
                {
                    if (IsCollideWithObject(spr)) return true;
                }
            }

            return false;
        }
        //Проверка на столкновение с границами экрана
        public void Check()
        {
            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;
            }
        }

        public bool CheckBounds()
        {
            return ((sprPosition.X <= scrBounds.Left |
                sprPosition.X >= scrBounds.Width - sprRectangle.Width |
                sprPosition.Y <= scrBounds.Top |
                sprPosition.Y >= scrBounds.Height - sprRectangle.Height));

        }

    }
}
Листинг 18.7. Код класса gBaseClass

Листинг 18.8. содержит код класса Wall.

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.Media;


namespace P12_2.GameObjects
{
    /// <summary>
    /// Это игровой компонент, реализующий интерфейс IUpdateable.
    /// </summary>
    public class Wall : gBaseClass
    {
        public Wall(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle, SpriteFont _myFont)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle, _myFont)
        {
            // ЗАДАЧА: здесь создаются дочерние компоненты
        }

        /// <summary>
        /// Позволяет игровому компоненту выполнить необходимую инициализацию перед\r\запуском. 
         Здесь можно запросить нужные службы и загрузить контент.
        /// 
        /// </summary>
        public override void Initialize()
        {
            // ЗАДАЧА: добавьте здесь код инициализации

            base.Initialize();
        }

        /// <summary>
        /// Позволяет игровому компоненту обновиться.
        /// </summary>
        /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
        public override void Update(GameTime gameTime)
        {
            // ЗАДАЧА: добавьте здесь код обновления
            
            base.Update(gameTime);
        }
    }
}
Листинг 18.8. Код класса Wall

Листинг 18.9. содержит код класса Me.

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.GameObjects
{
    /// <summary>
    /// Это игровой компонент, реализующий интерфейс IUpdateable.
    /// </summary>
    public class Me : gBaseClass
    {

        public Me(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle, SpriteFont _myFont)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle, _myFont)
        {
            sprSpeed = 2;
        }

        public void IsCollideWithAny()
        {
            //Заводим переменную для временного хранения
            //ссылки на объект, с которым столкнулся игровой объект
            gBaseClass FindObj = null;
            //Проверка на столкновение с объектами Enemy
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Enemy)))
                {
                    if (IsCollideWithObject(spr))
                    {
                        FindObj = spr;
                    }
                }
            }

            if (FindObj != null)
            {

                this.Dispose();
            }
        }

        /// <summary>
        /// Позволяет игровому компоненту выполнить необходимую инициализацию перед\r\запуском.  
        Здесь можно запросить нужные службы и загрузить контент.
        /// 
        /// </summary>
        public override void Initialize()
        {
            // ЗАДАЧА: добавьте здесь код инициализации

            base.Initialize();
        }

        //Перемещение объекта
        void Move(int Direction)
        {

            //Вверх
            if (Direction == 1)
            {

                MoveUp(sprSpeed);
                //При прыжке проводится проверка на контакт со стеной
                //Которая может быть расположена над объектом
                //при необходимости его координаты корректируются
                while (IsCollideWithWall())
                {
                    MoveDown((sprSpeed / 10));
                }



            }
            //Вниз
            //Происходит обычная процедура перемещения
            //объекта с проверкой
            if (Direction == 2)
            {
                MoveDown(sprSpeed);
                while (IsCollideWithWall())
                {
                    MoveUp((sprSpeed / 10));
                }
            }
            //Точно так же обрабатывается
            //движение Влево
            if (Direction == 3)
            {


                MoveLeft(sprSpeed);
                while (IsCollideWithWall())
                {
                    MoveRight((sprSpeed / 10));
                }
            }
            //Аналогично - перемещение вправо
            if (Direction == 4)
            {

                MoveRight(sprSpeed);
                while (IsCollideWithWall())
                {
                    MoveLeft((sprSpeed / 10));
                }
            }

        }
        //Обработка сенсорного ввода
        void TouchInput()
        {
            //Реализуем управление объектом
            //Получаем коллекцию объектов, содержащих информацию о касаниях экрана
            TouchCollection touchLocations = TouchPanel.GetState();
            //Перебираем коллекцию, присваивая объекту координаты касания
            foreach (TouchLocation touchLocation in touchLocations)
            {
                if (touchLocation.State == TouchLocationState.Pressed || touchLocation.State == TouchLocationState.Moved)
                {
                    //Стрелка "Влево"
                    if (touchLocation.Position.X > 500 && touchLocation.Position.X < 600
                        && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480)
                    {
                        Move(3);
                    }
                    //Стрелка "Вправо"
                    if (touchLocation.Position.X > 700 && touchLocation.Position.X < 800
                        && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480)
                    {
                        Move(4);
                    }
                    //Стрелка "Вниз"
                    if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700
                        && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480)
                    {
                        Move(2);
                    }
                    //Стрелка "Вверх"
                    if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700
                        && touchLocation.Position.Y > 280 && touchLocation.Position.Y < 380)
                    {
                        Move(1);
                    }
                }

            }
        }

        /// <summary>
        /// Позволяет игровому компоненту обновиться.
        /// </summary>
        /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
        public override void Update(GameTime gameTime)
        {
            //Обработаем касания экрана для реализации перемещения объекта
            TouchInput();
            //Проверим, не вышел ли он за границы экрана, если надо
            //исправим его позицию
            Check();
            //Применим к объекту "силу тяжести"
            //Проверим на другие столкновения
            IsCollideWithAny();

            message = "Try to escape!";

            base.Update(gameTime);
        }


    }
}
Листинг 18.9. Код класса Me

Комментарии к коду раскрывают особенности алгоритма.

18.4. Выводы

Задача реализации искусственного интеллекта в игровых программах сталкивается с двумя ограничениями. Во-первых – чем сложнее и продуманнее эта система, и чем лучше она, в результате, работает – тем естественнее ведут себя персонажи. Во-вторых – игровые программы требуют немало вычислительных ресурсов, поэтому любое усложнение – в том числе – усложнение системы ИИ – ведет к падению производительности. Поэтому программисты вынуждены идти на компромисс – максимально упрощать систему ИИ таким образом, чтобы ее работа не тормозила выполнение игры.

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

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

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

18.5. Задание

Разработайте игровой проект – клон игры "Battle City" - симулятор танкового боя на карте, подобной карте, применяемой в наших примерах. Создайте следующие объекты карты:

  1. Кирпичные стены – объекты не могут преодолевать их, выстрел из пушки уничтожает один сегмент стены.
  2. Бетонные стены – непроницаемы для объекта, не уничтожаются выстрелами.
  3. Лес – объект движется по лесу замедленно, лес частично скрывает объект, выстрел уничтожает сегмент леса
  4. Вода – объект не может преодолеть водную преграду, однако снаряд беспрепятственно преодолевает воду.
  5. База игрока – несколько выстрелов врага уничтожают базу

Два вида танков врага

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

Систему бонусных объектов

  1. Бонус, при подборе которого вражеские танки, находящиеся на карте, уничтожаются
  2. Бонус, добавляющий одну "жизнь" игроку

Снаряд

Гулич Анна
Гулич Анна
Невозможно пройти тесты, в окне с вопросами пусто