Спонсор: Microsoft
Опубликован: 10.04.2009 | Доступ: свободный | Студентов: 3247 / 243 | Оценка: 4.49 / 4.39 | Длительность: 17:21:00
Специальности: Программист
Самостоятельная работа 17:

Игровой мир, освещение, тени

В листинге 22.3. вы можете найти код класса Game1.

using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;

namespace P17_2
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        //Матрицы
        Matrix viewMatrix;
        Matrix projMatrix;
        //Модели
        modCls[] cls;
        //Положение моделей в пространстве
        Vector3[] offset;
        //Соотношение сторон окна вывода
        float aspectRatio;
        //Плоскость
        modCls plane;
        //Направление света
        Vector3 LightDirection;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            //Установить формат буфера трафаретов
            graphics.PreferredDepthStencilFormat = SelectStencilMode();
        }

       //Проверка доступного буфера трафаретов и возврат соответствующей переменной типа
       //DepthFormat. Если буфер трафаретов не найден - выдадим исключение
        private static DepthFormat SelectStencilMode()
        {
            //Графическая карта
            GraphicsAdapter adapter = GraphicsAdapter.DefaultAdapter;
            //Формат поверхности
            SurfaceFormat format = adapter.CurrentDisplayMode.Format;
            //Проверка имеющихся технических средств карты
            if (adapter.CheckDepthStencilMatch(DeviceType.Hardware, format, format, DepthFormat.Depth24Stencil8))
                return DepthFormat.Depth24Stencil8;
            else if (adapter.CheckDepthStencilMatch(DeviceType.Hardware, format, format, DepthFormat.Depth24Stencil8Single))
                return DepthFormat.Depth24Stencil8Single;
            else if (adapter.CheckDepthStencilMatch(DeviceType.Hardware, format, format, DepthFormat.Depth24Stencil4))
                return DepthFormat.Depth24Stencil4;
            else if (adapter.CheckDepthStencilMatch(DeviceType.Hardware, format, format, DepthFormat.Depth15Stencil1))
                return DepthFormat.Depth15Stencil1;
            else
                throw new InvalidOperationException("Could Not Find Stencil Buffer for Default Adapter");
        }

        protected override void Initialize()
        {
            //Отобразим указатель мыши
            IsMouseVisible = true;
            base.Initialize();
        }

        
        protected override void LoadContent()
        {           
            //Вычислим соотношение сторон
            aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
                (float)graphics.GraphicsDevice.Viewport.Height;
            //Матрица вида
            viewMatrix = Matrix.CreateLookAt(new Vector3(0, 10, 15),
                new Vector3(0, 0, 0),
                new Vector3(0.0f, 1.0f, 0.0f));
            //Матрица проекции
            projMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45),
                aspectRatio, 1.0f, 1000.0f);
            LightDirection = new Vector3(-2, -2, 0);
            //Создаем плоскость
            plane = new modCls(this, Content.Load<Model>("plane"), graphics, new Plane ());
            //Настраиваем параметры плоскости
            plane.WorldMatrix = Matrix.CreateScale(25) * Matrix.CreateRotationY(MathHelper.ToRadians(90)) *
    Matrix.CreateRotationZ(MathHelper.ToRadians(90)) *
    Matrix.CreateTranslation(0, -4, 0);
            plane.ViewMatrix = viewMatrix;
            plane.ProjectMatrix = projMatrix;
            plane.LightDirection = LightDirection;
            Components.Add(plane);
            //Плоскость, на которую проецируется тень
            //Она немного выше плоскости, которую мы выводим на экран 
            //для того, чтобы тень была видна
            Plane pl1 = new Plane(new Vector3(0, -3.9f, 0), new Vector3(2, -3.9f, 1), new Vector3(-1, -3.9f, -2));
            //Три элемента в массиве объектов
            cls = new modCls[3];
            //Три элемента в массиве положений объектов
            offset = new Vector3[3];
            //Зададим начальные положения моделей
            offset[0] = new Vector3(0, 2, 0);
            offset[1] = new Vector3(-6, 3, -4);
            offset[2] = new Vector3(6, 4, -6);
            //Создадим модели
            cls[0] = new modCls(this, Content.Load<Model>("ball2"), graphics,pl1);
            cls[1] = new modCls(this, Content.Load<Model>("ball2"), graphics, pl1);
            cls[2] = new modCls(this, Content.Load<Model>("ball2"), graphics, pl1);
            //Задаем параметры матриц и направления света каждой из моделей
            for (int i = 0; i < 3; i++)
            {
                cls[i].WorldMatrix = Matrix.CreateTranslation(offset[i]);
                cls[i].ProjectMatrix = projMatrix;
                cls[i].ViewMatrix = viewMatrix;
                cls[i].LightDirection = LightDirection;
                Components.Add(cls[i]);
            }
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        
        protected override void Update(GameTime gameTime)
        {
            //Для случайных колебаний моделей
            Random rand = new Random();
            //В каждом проходе цикла
            //Смещаем каждую модель вниз на 0.04 единицы
            //И на небольшое случайное число вдоль оси X и Y - модели
            //слегка колеблются при добавлении этого изменения
            //Мы не ввели ограничения на изменение X и Z - 
            //При практической реалиации данного примера это
            //желательно сделать
            for (int i = 0; i < 3; i++)
            {
                offset[i].Y -= 0.04f;
                offset[i].X -= (0.08f * (float)rand.NextDouble() - 0.08f * (float)rand.NextDouble());
                offset[i].Z -= (0.08f * (float)rand.NextDouble() - 0.08f * (float)rand.NextDouble());
                cls[i].WorldMatrix = Matrix.CreateTranslation(offset[i]);
            }
            //Проверяем попадание мыши по объекту
            CheckMouseClick();
            //Проверяем пересечение с плоскостью
            CheckPlane();
            //Меняем позицию источника света по нажатию клавиш
            LightSourceControl();
            base.Update(gameTime);
        }
        //Изменение позиции источника света
        void LightSourceControl()
        {
            //Получим состояние клавиатуры
            KeyboardState key = Keyboard.GetState();
            //Клавиша "вверх" - уменьшим позицию по Z
            if (key.IsKeyDown(Keys .Up ))
            {
                LightDirection.Z -= 0.5f;
            }
            //Клавиша "вниз" - увеличим Z
            if (key.IsKeyDown(Keys.Down))
            {
                LightDirection.Z += 0.5f;
            }
            //"Влево" - уменьшим X
            if (key.IsKeyDown(Keys.Left))
            {
                LightDirection.X -= 0.5f;
            }
            //"Вправо" - увеличим X
            if (key.IsKeyDown(Keys.Right))
            {
                LightDirection.X += 0.5f;
            }
            //"W" - увеличим Y
            if (key.IsKeyDown(Keys.W))
            {
                LightDirection.Y += 0.5f;
            }
            //"S" - уменьшим Y
            if (key.IsKeyDown(Keys.S))
            {
                LightDirection.Y -= 0.5f;
            }
            //Изменим направление источника света для плоскости
            plane.LightDirection = LightDirection;
            //Изменим направление источника света для моделей
            for (int i = 0; i < 3; i++)
            {
                cls[i].LightDirection = LightDirection;
            }
            //Выведем в заголовок окна информацию о направлении
            this.Window.Title = "Light source: " + LightDirection.ToString();

        }
        //Проверка на нажатие клавиши мыши
        void CheckMouseClick()
        {
            //получим состояние мыши
            MouseState mouseState = Mouse.GetState();
            //если нажата левая кнопка
            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                //Получим луч, идущий от позиции мыши на экране в пространство
                Ray pickRay = GetPickRay();
                //Переменная для хранения сферы, соответствующей объекту
                BoundingSphere b1;
                //Переменная для хранения вектора размера модели
                Vector3 scale;
                //Переменная для хранения информации о повороте модели
                Quaternion rotation;
                //Переменая для хранения информации о позиции модели
                Vector3 translation;
                for (int i = 0; i < 3; i++)
                {
                    //Получить BoundingSphere для текущего объекта
                    b1 = cls[i].myModel.Meshes[0].BoundingSphere;
                    //Получить параметры - размер, поворот, позицию для объекта
                    cls[i].WorldMatrix.Decompose(out scale, out rotation, out translation);
                    //Установить центр сферы в соответствии с позицией объекта
                    b1.Center = translation;
                    //Получить результат пересечения луча и сферы
                    Nullable<float> result = pickRay.Intersects(b1);
                    //Если луч и сфера пересеклись - "поднять" объект до позиции Y=4
                    if (result.HasValue)
                    {
                        offset[i].Y = 4;
                        cls[i].WorldMatrix = Matrix.CreateTranslation(offset[i]);
                    }
                }


            }
        }
        //Проверка на столкновение с плоскостью
        void CheckPlane()
        {
            //Создадим плоскость по трем точкам - она пересекает ось Y в точке -4 и не пересекает
            //другие оси
            Plane pl = new Plane(new Vector3(0, -4, 0), new Vector3(2, -4, 1), new Vector3(-1, -4, -2));
            //Переменная для хранения типа пересечения с плоскостью
            //Она может сигнализировать о нахождении объекта перед плоскостью (выше в нашем случае)
            //за плоскостью и о пересечении с плоскостью
            PlaneIntersectionType plType;
            //Сфера для объекта
            BoundingSphere b1;
            //Переменная для хранения вектора размера модели
            Vector3 scale;
            //Переменная для хранения информации о повороте модели
            Quaternion rotation;
            //Переменая для хранения информации о позиции модели
            Vector3 translation;
            for (int i = 0; i < 3; i++)
            {
                //Получить BoundingSphere для текущего объекта
                b1 = cls[i].myModel.Meshes[0].BoundingSphere;
                //Получить параметры - размер, поворот, позицию для объекта
                cls[i].WorldMatrix.Decompose(out scale, out rotation, out translation);
                //Установить центр сферы в соответствии с позицией объекта
                b1.Center = translation;
                //Получить тип пересечения сферы объекта и плоскости
                plType = pl.Intersects(b1);
                //Если сфера пересекла плоскость
                if (plType == PlaneIntersectionType.Intersecting)
                {
                    //Удалить соответствующий игровой объект из коллекции
                    Components.Remove(cls[i]);
                }
            }

        }
        //Функция для вычисления луча, исходящего из точки, в которой
        //находился указатель мыши в момент щелчка
        Ray GetPickRay()
        {
            //получить состояние мыши
            MouseState mouseState = Mouse.GetState();
            //Сохранить координаты
            int mouseX = mouseState.X;
            int mouseY = mouseState.Y;
            //Точки для вычисления направления луча - одна, соответствующая координате мыши
            //вторая - направленная по оси Z
            Vector3 nearsource = new Vector3((float)mouseX, (float)mouseY, 0f);
            Vector3 farsource = new Vector3((float)mouseX, (float)mouseY, 1f);
            //Мировая матрица для вычислений
            Matrix world = Matrix.CreateTranslation(0, 0, 0);
            //Переведем координаты мыши в координаты трехмерного пространства
            Vector3 nearPoint = graphics.GraphicsDevice.Viewport.Unproject(nearsource, projMatrix, viewMatrix, world);
            Vector3 farPoint = graphics.GraphicsDevice.Viewport.Unproject(farsource, projMatrix, viewMatrix, world);
            // Получим направление луча
            Vector3 direction = farPoint - nearPoint;
            //Нормализуем его (преобразуем к единичному вектору)
            direction.Normalize();
            //Создадим новый луч, начинающийся в точке, соответствующей координате
            //указателя мыши в объектном пространстве, с направлением, 
            //соответствующим вычисленному направлению
            Ray pickRay = new Ray(nearPoint, direction);
            //возвратим луч в процедуру, вызывавшую данную функцию
            return pickRay;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }
    }
}
Листинг 22.3. Код класса Game1

На рис. 22.4. вы можете видеть игровой экран проекта P17_2.

Игровой экран проекта P17_2

увеличить изображение
Рис. 22.4. Игровой экран проекта P17_2

Задание

Разработайте на основе проекта P17_2 собственную игру, которая реализует следующие возможности:

  1. Систему меню
  2. Систему подсчета очков пользователя
  3. Увеличение сложности игры при прохождении в следующий уровень
Alina Lasskaja
Alina Lasskaja

Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить.....