Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить..... |
Опубликован: 10.04.2009 | Уровень: специалист | Доступ: свободно
Самостоятельная работа 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.5 вы можете видеть игровой экран после подключения программы к уже созданной сессии.