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

Сетевые игры

Теперь рассмотрим код главного класса нашей игры. В листинге 16.3. вы можете найти код класса Game1. Именно он реализует сетевую функциональность для нашей игры. При разработке этого класса мы пользовались справочными материалами Microsoft.

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 P12_1
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //Для управления графическим устройством
        GraphicsDeviceManager graphics;
        //Для вывода изображений
        SpriteBatch spriteBatch;
        //Для объекта-мяча
        Ball b;
        //Для обработки состояния клавиатуры
        KeyboardState Kb;
        //Для сетевой сессии
        NetworkSession networkSession;
        //Для записи и чтения пакетов данных
        PacketWriter packetWriter;
        PacketReader packetReader;


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            //Добавляем новый GamerServiceComponent
            Components.Add(new GamerServicesComponent(this));
        }

        protected override void Initialize()
        {
            //Объекты для записи и чтения данных созданы
            //с конструкторами по умолчанию
            packetWriter = new PacketWriter();
            packetReader = new PacketReader();
            base.Initialize();
        }

        /// <summary>
        protected override void LoadContent()
        {
            
            spriteBatch = new SpriteBatch(GraphicsDevice);
            //Создадим новый мяч
            b = new Ball(Content, this);
        }

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

        protected override void Update(GameTime gameTime)
        {
            //Состояние клавиатуры
            Kb = Keyboard.GetState();
            //Если сетевая сессия не создана
            if (networkSession == null)
            {
                //Если окно приложения активно
                if (IsActive)
                {
                    //Если ни один игрок не вошел в систему
                    if (Gamer.SignedInGamers.Count == 0)
                    {
                       //Если окно регистрации не видно - отобразить его
                        if (!Guide.IsVisible)
                            Guide.ShowSignIn(2, false);
                    }
                    else
                        //Если пользователь вошел в систему
                        //Запустить процедуру, которая сначала пытается
                        //найти существующую сетевую сессию
                        //если не находит - создает новую
                        TryToJoinOrCreate();
                }
            }
            else
                //Если сетевая сессия создана
                //Перейти к процедуре обработки сессии
                WorkWithSession();
            
            base.Update(gameTime);
        }

        //Процедура, которая пытается подключиться к существующей сессии
        //или создать новую
        void TryToJoinOrCreate()
        {
            //Выводим сообщение в заголовок окна
            this.Window.Title = "Ищу доступную сессию";
            //Начинаем поиск новой сессии типа SystemLink - такая сессия
            //позволяет создавать игры для автономных, не подключенных к Интернету,
            //локальных сетей
            using (AvailableNetworkSessionCollection availNetSessions =
                NetworkSession.Find(NetworkSessionType.SystemLink,
                2, null))
            {
                //Если ни одной сессии нет
                if (availNetSessions.Count == 0)
                {
                    this.Window.Title = "Сессия не найдена - создаю новую сессию";
                    //Создаем новую сессию
                    networkSession = NetworkSession.Create(NetworkSessionType.SystemLink,
                        1, 2);
                    //Подключаем обработчики событий
                    JoinEventhandlers();
                    this.Window.Title = "Сессия создана";
                }
               //Иначе, если сессия найдена
                else
                {
                    this.Window.Title = "Сессия найдена, присоединяюсь";
                    //Присоединяемся к ней и так же подключаем обработчики
                    networkSession = NetworkSession.Join(availNetSessions[0]);
                    JoinEventhandlers();

                }
            }

        }
        //Подключение обработчиков событий
        void JoinEventhandlers()
        {
            //Объект типа NetworkSession имеет множество событий
            //В частности, они используются для того, чтобы показать, что к игре присоединился новый пользователь
            //что игра начата, закончен. Здесь мы подключили два события
            //Одно из них происходит, когда к игре присоединяется новый игрок, подключим соответствующий обработчик
            networkSession.GamerJoined += new EventHandler<GamerJoinedEventArgs>(networkSession_GamerJoined);
            //Второе происходит после закрытия сессии
            networkSession.SessionEnded += new EventHandler<NetworkSessionEndedEventArgs>(networkSession_SessionEnded);
        }
        //Обработчик события, происходящего при присоединении нового игрока
        void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e)
        {
            //Получим номер игрока
            int plNumber = networkSession.AllGamers.IndexOf(e.Gamer);
            //Запишем в поле Tag игрока, которое имеет тип Object, то есть - способно
            //принимать любые объекты, новый объект Bat. Номер игрока, который мы передали
            //объекту, используется для его установки. В нашей игре могут участвовать лишь два игрока
            //поэтому номер 0 используется для установки биты в левой части экрана, а номер 1 - 
            //в правой. Игрок, создавший сессию, занимает левую часть экрана
            e.Gamer.Tag = new Bat(plNumber, Content, this);
        }

        //Обработчик события, происходящего при закрытии сессии
        void networkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e)
        {
            //уничтожим сессию
            networkSession.Dispose();
            networkSession = null;
        }

        //Процедура обработки событий в течение сессии
        void WorkWithSession()
        {
            //Вызвать процедуру, вычисляющую новое положение игрового объекта
            //для локального игрока - в нашем случае это один игрок с номером 0
            //и записывающей данные в сетевой поток
            CalcAndWrite(networkSession.LocalGamers[0]);
            //Обновить состояние сессии
     
            networkSession.Update();
            //Если сессия оказалась уничтоженной - выйти из процедуры
            if (networkSession == null) return;
            //Прочесть сетевые данные и модифицировать состояние объектов
            ReadAndImplement(networkSession.LocalGamers[0]);
        }
        //Вычисления и передача данных в сеть
        void CalcAndWrite(LocalNetworkGamer gamer)
        {
            //Рассматривать объект, записанный в Tag текущего игрока
            //как биту - Bat
            Bat bat = gamer.Tag as Bat;
            //Модифицировать координаты в соответствии с клавиатурными командами
            //Клавиша вверх перемещает биту вверх
            if (Kb.IsKeyDown(Keys.Up))
            {
                bat.sprPosition.Y -= 3;
            }
            //Клавиша вниз - вниз
            if (Kb.IsKeyDown(Keys.Down))
            {
                bat.sprPosition.Y += 3;
            }
            //Записать положение биты в сеть
            packetWriter.Write(bat.sprPosition);
            //Если текущий игрок - хост, то есть он создал
            //сетевую сессию, на него возложим обязанности
            //вычисления позиции мяча и обработки столкновений
            if (gamer.IsHost)
            {
                //Если к игре подключено 2 пользователя
                //То есть она началась
                if (networkSession.AllGamers.Count == 2)
                {
                    //Изменить позицию мяча в соответствии с его
                    //скоростью
                    b.sprPosition += b.sprSpeed;
                    //Если вышли за пределы верхней части игрового окна
                    if (b.sprPosition.Y < b.scrBounds.Y)
                    {
                        //Вернуть объект и инвертировать скорость по Y
                        b.sprPosition.Y = b.scrBounds.Y;
                        b.sprSpeed.Y *= -1;
                    }
                    //Если пересекли нижнюю границу окна
                    //Вернуть объект и инвертировать скорость по Y
                    if (b.sprPosition.Y + b.sprRectangle.Height > b.scrBounds.Height)
                    {
                        b.sprPosition.Y = b.scrBounds.Height - b.sprRectangle.Height;
                        b.sprSpeed.Y *= -1;
                    }
                    //Получить биту для игрока №0 - она расположена слева
                    //Берем этот объект из списка всех игроков
                    bat = networkSession.AllGamers[0].Tag as Bat;
                    //Если есть столкновение с битой
                    if (b.sprPosition.X + b.sprRectangle.Width > bat.sprPosition.X &&
                    b.sprPosition.X < bat.sprPosition.X + bat.sprRectangle.Width &&
                    b.sprPosition.Y + b.sprRectangle.Height > bat.sprPosition.Y &&
                    b.sprPosition.Y < bat.sprPosition.Y + bat.sprRectangle.Height)
                    {
                        //Вернем мяч и инвертируем скорость по X
                        b.sprPosition.X = bat.sprRectangle.Width;
                        b.sprSpeed.X *= -1;
                    }
                    //Получим биту для игрока №1 - она расположена справа
                    bat = networkSession.AllGamers[1].Tag as Bat;
                    //Если есть столкновение с ней
                    if (b.sprPosition.X + b.sprRectangle.Width > bat.sprPosition.X &&
                    b.sprPosition.X < bat.sprPosition.X + bat.sprRectangle.Width &&
                    b.sprPosition.Y + b.sprRectangle.Height > bat.sprPosition.Y &&
                    b.sprPosition.Y < bat.sprPosition.Y + bat.sprRectangle.Height)
                    {
                        //Вернем мяч и инвертируем скорость по X
                        b.sprPosition.X = b.scrBounds.Width - bat.sprRectangle.Width - b.sprRectangle.Width;
                        b.sprSpeed.X *= -1;
                    }

                }
                //Запишем позицию мяча в сетевой поток
                packetWriter.Write(b.sprPosition);
            }
            //Отправим данные другим игрокам
            gamer.SendData(packetWriter, SendDataOptions.InOrder);
        }
        //Процедура чтения и применения данных от других объектов
        void ReadAndImplement(LocalNetworkGamer gamer)
        {
            //До тех пор, пока есть данные для чтения
            while (gamer.IsDataAvailable)
            {
                //Новый объект sender типа NetworkGamer
                //Он бует использован для хранения ссылки на объект
                //который отправил данные
                NetworkGamer sender;

                // Прочесть данные
                gamer.ReceiveData(packetReader, out sender);

                //Если данные отправлены не локальным игроком - то есть - 
                //пришли к нам от одного из сетевых игроков (в нашем случае возможно наличие лишь одного
                //такого игрока
                if (!sender.IsLocal)
                {

                    //Получить объект bat из поля Tag игрока, отправившего данные
                    Bat bat = sender.Tag as Bat;
                    //Записать прочитанные данные в качестве позиции этого объекта
                    bat.sprPosition = packetReader.ReadVector2();
                    //Если отправитель данных - хост - дополнительно установить
                    //позицию мяча, прочитав данные из потока
                    //Таким образом хост всегда пишет в поток два фрагмента данных
                    //и если другой объект читает данные хоста - он так же
                    //воспринимает два фрагмента
                    if (sender.IsHost) b.sprPosition = packetReader.ReadVector2();
                }
            }
        }


        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
            //Начало вывода спрайтов
            spriteBatch.Begin();
            //Если создана сетевая сессия и количество игроков равно 2
            //То есть - выполнены условия для начала игры
            if (networkSession!=null && networkSession .AllGamers .Count==2)
            {
                //Для каждого игрока из всех игроков, имеющих отношение к данной сессии
                foreach (NetworkGamer gamer in networkSession.AllGamers)
                {
                    //Получить объект bat для текущего игрока
                    Bat bat = gamer.Tag as Bat;

                    //Вывести объект bat
                    bat.Draw(spriteBatch);
                    //Вывести мяч
                    b.Draw(spriteBatch);
                }
            }
            //Завершить вывод объектов
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}
Листинг 16.3. Код класса Game1

На рис. 16.4. вы можете видеть экран выбора игрока. Мы зарегистрировались как Player1.

Выбор игрока

увеличить изображение
Рис. 16.4. Выбор игрока

На рис. 16.5 вы можете видеть игровой экран после подключения программы к уже созданной сессии.

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

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

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