Пятница, 26 Апреля 2024, 22:52

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [355]
Статьи об общих понятиях связанных с созданием игр.
Программирование [82]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [145]
Статьи о программах для создания игр, уроки и описания.
Софт [43]
Различные программы, в том числе в помощь игроделам.
2D-графика [14]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [16]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [161]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [129]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Разработка игр для вас:
Всего ответов: 11093
Главная » Статьи » Создание игр

Разработка клиент-серверного приложения на Ас3
Тема моей статьи — разработка клиент-серверного приложения на Ас3. В этой статье я постараюсь поделиться основными моментами с которыми я сталкивался при создании таких приложений. Надеюсь, эта статья окажется полезной.

Что содержит статья:
• Основные моменты при разработке клиентской части.
• Понятие игрового протокола. Выводим протокол для нашей будущей игры.
• Разработка клиентской части, создание игрового поля.
• Программирование серверной части, обработка данных, синхронизация.
• Доработка клиентской части. It's Still Alive!

Основные моменты при разработке клиентской части.
Мы будем делать приложение «Рисовалка», когда несколько человек в режиме реального времени могут рисовать на одном флеш объекте.
Пакет – набор данных «упакованных» нашим протоколом. В дальнейшем я буду использовать только это понятие.

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

Что из себя представляет игровой протокол?
Я пишу эту статью на примере создания простейшего клиент-серверного приложения с движением объектов. Для того, чтобы добиться отображения движения одного клиента у остальных, мы должны сообщать серверу текущие координаты, а он в свою очередь передавать их другим клиентам.
Как будет выглядеть протокол:



Теперь подробнее:
• ID — идентификатор пакета. Позднее, при разработке клиента и сервера мы столкнемся с проблемой определения пакетов (что и какой значит).
• PId – ид игрока.
• X, Y – новые координаты.
Вот эти данные мы и будем отдавать серверу.

Разработка клиентской части, создание игрового поля.
Давайте определимся с поставленной задачей. У нас есть игровое поле и наш персонаж. Но мы же делаем сетевую игру! А это значит? Это значит то, что таких персонажей может быть множество. Для этого мы будем использовать динамическое создание объектов.
Для начала набросаем функции рисования, повесим движение/клик мыши на события:

Опишем переменные:
Code

  private var nowdraw:Boolean = false;
  private var server:String = "localhost";
  private var serverPort:int = 443;
  private var colorClient:uint;
  private var serverSocket:XMLSocket = new XMLSocket();
  private var MyId:String = "0";
  private var TexteField:TextField = new TextField();

Подробнее о них в процессе использования smile

Теперь напишем функцию для обработки событий:
Code

  private function initGraph():void {
  graphics.lineStyle(1,colorClient);
  stage.addEventListener(MouseEvent.MOUSE_DOWN, start);
  stage.addEventListener(MouseEvent.MOUSE_MOVE, draw);
  stage.addEventListener(MouseEvent.MOUSE_UP, stop);  
  }


Как вы видите, я повесил на события: нажатие кнопки мыши, перемещение курсора, отжатие кнопки – 3 функции.

Теперь добавим эти функции:
Code
public function start(event:MouseEvent):void {
  graphics.lineStyle(1,colorClient);
  graphics.moveTo( mouseX, mouseY);
  nowdraw = true;
  }
   
  public function draw(event:MouseEvent):void {
  if(nowdraw){
  graphics.lineTo(mouseX, mouseY);
  }
  }
   
  public function stop(event:MouseEvent):void {
  nowdraw = false;  
  }


Переменная nowdraw – булевая переменная, её значение определяется в зависимости от того нажата или отпущена кнопка мыши. Если значение nowdraw равняется true, тогда при движении мыши мы рисуем линии.

Добавим функцию подключения к серверу:
Code
public function connectionserver():void {
  serverSocket.addEventListener(Event.CONNECT, serverConnected);  
  serverSocket.addEventListener(Event.CLOSE, serverDeconnected);  
  serverSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, serverForbidden);  
  serverSocket.addEventListener(DataEvent.DATA, serverRecv);
  serverSocket.connect(server, serverPort);
  }


На каждое событие мы повесили функции. Ниже эти функции.

Функции «повешенные» на события с сервером:
Code
private function serverConnected(e:Event):void {
  TexteField.text = "Подключено ;)";
  addChild(TexteField);
  }
   
  private function serverDeconnected(e:Event):void {
  TexteField.text = "Вы были отключены от сервера ;(";
  addChild(TexteField);
  }
   
  private function serverForbidden(e:Event):void {
  TexteField.text = "Невозможно подключиться к серверу ;(";  
  }


Пока все.

Программирование серверной части, обработка данных, синхронизация.

Серверную часть я буду писать на языке Python. Но вы можете писать на чем угодно, принцип одинаковый.

Используемые модули: twisted.

Давайте условно определимся с идентификаторами пакетов:
• 02 – подключение к серверу, в этом пакете сервер будет передавать ид пользователя и цвет его линий.
• 03 – пакет, содержащий координаты мыши в момент нажатия.
• 04 – пакет, содержащий координаты мыши при движении курсором (отправляется, когда зажата ЛКМ).

Теперь набросаем простой пример сервера:
Code

from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor

class Server(Protocol):
  def dataReceived(self, data):
  loc1 = data.split('\x01')
  loc2 = loc1[0]
  loc3 = loc1[1]
  loc4 = loc1[2]
  loc5 = loc1[3]
  if loc1 == "\x02":
  colorShema = "0x000000"
  self.factory.uids[len(self.factory.uids) + 1] = colorShema
  packet = '\x02'
  packet += '\x01'
  packet += colorShema
  packet += '\x01'
  packet += str(len(self.factory.uids) + 1)
  packet += '\x00'
  self.transport.write(packet)
  if loc1 == "\x03":
  colorShema2 = "0xff00ff"
  packet = "\x03\x01" + colorShema2 + "\x01" + loc3 + "\x01" + loc4 + "\x01" + loc5
  for client in self.factory.users:
  client.send(packet)

   
  if loc1 == "\x04":
  colorShema2 = "0xff00ff"
  packet = "\x04\x01" + colorShema2 + "\x01" + loc3 + "\x01" + loc4 + "\x01" + loc5
  for client in self.factory.users:
  client.send(packet)

  def connectionMade(self):
  print "Connected"
  self.factory.users.append(self)
   
  def connectionLost(self, reason):
  print "Exit;("
   
  def send(self, packet):
  self.transport.write(packet)

def main():
  factory = Factory()
  factory.protocol = Server
  factory.users = []
  factory.uids = {}
  reactor.listenTCP(443, factory)
  reactor.run()

if __name__ == '__main__':
  main()

Что мы сделали? Мы принимаем наши пакеты и разбиваем их согласно нашему протоколу: id + x + y
Далее, если это пакет 2 – мы вернем клиенту его ид и цвет (черный).
Если это пакет 3 – отправим всем клиентам пакет о нажатии кнопки мыши.
Если это пакет 4 – отправляем всем клиентам пакеты о координатах мыши.

Дорабатываем клиентскую часть:

Добавим функцию формирования и отправки данных:
Code
private function query(id:int, x:int, y:int):void {
  var packet:ByteArray = new ByteArray();
  packet.writeByte(id);
  packet.writeByte(1);
  packet.writeUTFBytes(MyId);
  packet.writeByte(1);
  packet.writeUTFBytes(x.toString());
  packet.writeByte(1);
  packet.writeUTFBytes(y.toString());
  serverSocket.send(packet);
  }


Эта функция принимает параметры: id, x, y, потом формирует пакет и отправляет на сервер.

Добавим вызов этой функции к событию «подключено»:

Code
private function serverConnected(e:Event):void {
  TexteField.text = "Все ок ;)";
  addChild(TexteField);
  query(2, 0, 0);
  }


Как вы видите, мы отправили серверу пакет с идентификатором «2».

Теперь допишем функцию принятия пакетов, введем условия и реакцию на них:

Code
private function serverRecv(e:DataEvent):void {
  var loc1:*;
  var loc2:*;
  var loc3:*;
  var loc4:*;
  var loc5:*;
  var x:*;
  var y:*;
  loc1 = e.data;
  loc2 = loc1.split(String.fromCharCode(1));
  loc3 = loc2[0].charCodeAt(0);
  loc4 = loc2[1];
  loc5 = loc2[2];
  if (loc3 == 2) {
  colorClient = loc4;
  MyId = loc5;
  TexteField.text = "Теперь вы можете рисовать!";
  this.addChild(TexteField);
  initGraph();
  }
  if (loc3 == 3) {
  if (loc5 != MyId) {
  graphics.lineStyle(1, loc4);
  x = loc2[3];
  y = loc2[4];
  if (x) {graphics.moveTo(x, y);}
  }
  }
  if (loc3 == 4) {
  if (loc5 != MyId) {
  graphics.lineStyle(1, loc4);
  x = loc2[3];
  y = loc2[4];
  if (x) {graphics.lineTo(x, y);}
  }
  }
  }


Здесь ситуация как с сервером, с точностью да наоборот. Каждый раз, когда приходит пакет и идентификатором 3, мы перемещаем точку начала рисования. Каждый раз, когда приходит пакет с идентификатором 4, мы начинаем рисовать линию (согласно координатам этого пакета).

Чтобы сервер получал эти данные, мы будет отправлять пакеты с координатами в процессе рисования. Для этого немного изменим функции рисования:

Code
public function start(event:MouseEvent):void {
  graphics.lineStyle(1,colorClient);
  graphics.moveTo( mouseX, mouseY);
  query(3, mouseX, mouseY);
  nowdraw = true;
  }
   
  public function draw(event:MouseEvent):void {
  if(nowdraw){
  graphics.lineTo(mouseX, mouseY);
  query(4, mouseX, mouseY);
  }
  }


Теперь добавим в функцию Main:
Code
TexteField.width = 300;
  connectionserver();


Запустим сервер, 2 окошка с клиентом и попробуем *-*

На этом все. Код в статье демонстрирует взаимодействие клиента с сервером. Обмена данными, работы нескольких человек в режиме реального времени. Исходный код сервера и клиента: скачать
Категория: Создание игр | Добавил: cougraAcc (26 Сентября 2012)
Просмотров: 13077 | Комментарии: 18 | Рейтинг: 2.9/7 |
Теги: multiplayer, client, Socket, Action Script, AS3, Клиентская часть, программирование, Action Script 3, server, Flash
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «Разработка клиент-серверного приложения на Ас3», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 18
+-1-
16 max92   (04 Ноября 2012 23:03) [Материал]
Хватит критиковать статью, вот мне кажется она очень даже полезной и понятной, кстати не могли бы вы перезалить исходники?

+0-
17 cougraAcc   (05 Ноября 2012 21:16) [Материал]

+0-
18 max92   (06 Ноября 2012 22:23) [Материал]
Огромное вам спасибо, разобрался smile

+0-
9 TreinDSM   (27 Октября 2012 12:00) [Материал]
TreinDSMПо большому счету, инфу о работе с сокетами можно найти хоть в любимой новичками книге К.Мука, хоть в официальной документации от Adobe. Лучше бы рассказали о том, как эти самые сокеты поведут себя в реально действующей сети. Потому как описанный Вами вариант в реальных условиях будет падать или выдавать некорректный результат, когда Ваши пакеты данных начнут приходить по 3 пакета в одном TCP пакете или даже по полтора пакета.

+0-
10 cougraAcc   (28 Октября 2012 16:36) [Материал]
cougraAccПроблема нескольких пакетов решается допиливанием сервера. Возможно, в будущем, я напишу еще одну статью, где на примерах покажу как мною решались подобные фейлы.

+1-
12 TreinDSM   (28 Октября 2012 22:14) [Материал]
TreinDSMОдним допиливанием сервера не обойтись. Нужно менять внутренний протокол сетевого взаимодействия, что соответственно затронет и сервер и клиента.

+0-
13 cougraAcc   (28 Октября 2012 22:47) [Материал]
cougraAccКогда сервер будет слать "склеенные" пакеты, то придется править и клиент, это очевидно.

+0-
14 TreinDSM   (29 Октября 2012 20:02) [Материал]
TreinDSMТак для кого это очевидно, тому эта статья бесполезна. А тем кому она полезна - это совсем не очевидно.

+0-
15 cougraAcc   (30 Октября 2012 06:07) [Материал]
cougraAccЯ о данном контексте.

+1-
7 Master_Chief   (06 Октября 2012 23:01) [Материал]
Master_Chiefавтор молодец что постарался и написал, но непонятен выбор XML сокетов. зачем нагружать сеть лишними данными?

+-1-
11 cougraAcc   (28 Октября 2012 16:47) [Материал]
cougraAccКак вариант.

+2-
6 KVinS   (05 Октября 2012 22:14) [Материал]
Код как код, кому-то может помочь.

+3-
4 Cpt_Flash   (30 Сентября 2012 22:50) [Материал]
Cpt_FlashНе понятно ведь ... cry sad

+-3-
5 cougraAcc   (01 Октября 2012 16:56) [Материал]
cougraAccСкидывал друзьям, которые вообще не знакомы с ас. Они все поняли. Стадный инстинкт?

+1-
8 JSent   (07 Октября 2012 10:59) [Материал]
JSentА тебе всё разжевать надо? Нужно уметь самому разбираться.

+-1-
3 cougraAcc   (26 Сентября 2012 15:19) [Материал]
cougraAccДокументации полно, писать очередной многостраничный мануал по сокетам нет смысла. Я поставил конкретную задачу - работа с координатами. И на примере рисовалки я показал как это делается, код предельно прост.

+0-
1 Gnomov   (26 Сентября 2012 15:04) [Материал]
GnomovОчень кратко и нелогично – для новичков малополезно, а для знающих, соответственно, неинтересно.

+-3-
2 cougraAcc   (26 Сентября 2012 15:16) [Материал]
cougraAccКакой из момента нелогичен?

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • Esenthel Engine
  • DreamSDK
  • Kochol
  • Supertuxkart
  • Open 3D Engine
  • AndEngine
  • Game Editor
  • PureBasic
  • FIFE
  • Raydium Engine
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2024 Рейтинг