[Из песочницы] Танчики в консоли, статья первая: «От спора к написанию кода»

habr.png

Пожалуй нужно начать с небольшой предыстории: сижу я как-то на паре и решили мы с одногруппником поспорить о возможности создания простейших танчиков в консоли (по типу дендивских), но для игры по сети.

Так как компьютерных сетей у нас ещё не было, мне пришлось самой учить всё с нуля. Прочитав, пожалуй, страниц 30 отборного текста и прослушав четыре лекции по этой теме, мне стало очень скучно и лениво слушать это дальше, и я наконец приступила к проекту.

Ну что, все готовы? Начинаем!


Эта статья будет короткой, но информативной (для новичков, как я).

На момент написания статьи я знала всего несколько языков и рассуждала о выборе каждого из них и насколько он подходит для разработки этих самых танчиков. Но опираясь на знания я решила распределить всё так:

C# — клиент (так как самый лёгкий в изучении язык)
Rust — сервер (так как самый безопасный и быстрый)
Php/html/css/javascript — сайт (который мы ВОЗМОЖНО будем делать)

Часть первая: постановка задачи


Главное что я должна была сделать, дабы доказать правоту — это простые танчики, но я решила сделать универсальный клиент. Как это? — это когда сервер одинаково оптимизирован как и для WinForm, так и для консоли (потому что я хочу хорошие танчики в винформ).

Так что наша задача звучит так: Необходимо разработать три приложения, первое — для WinForm (стандартное окошко виндовс), второе — консольное (эмулятор денди) и третье — сам сервер.

Часть вторая: идеи и огрехи…


Что необходимо делать хорошему приложению? — этот вопрос я задала при проектировании и в моей голове прозвучал ответ: «Быть быстрым».

Что это значит? — то, что нам придётся работать с несколькими потоками приёма/передачи данных. Снаряд не может ждать, пока отрисуется танчик, танк не может ждать пока рисуется стена, чтобы пошевелиться.

Мы все знаем как это обидно, когда у тебя падает фпс или не успевает что-то отрисоваться и тебя убивают. Таких ситуаций быть не должно!

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

1-й поток (мэйн) — должен отправлять нажатую клавишу на сервер.
2-й поток должен принимать координаты танков.
3-й координаты стен.
4-й координаты снарядов.

Также желательно было создать чат для общения танкистов во время игры (вдруг в разных кабинетах будем), но это пока не реализовано.

Теперь немного конкретики с потоками, первый должен быть простым отправителем (то есть организовывать минимальны вычисления и отправлять их на сервер), остальные же должны вечно принимать и отрисовывать всё в нашем приложении. В виде кода всё будет примерно так:

static async void Tank_coordinate()
        {
            //Приём координат танков
            await Task.Run(
              () =>
              {


              });
        }

        static async void Coordinate_wall()
        {
            //Приём координат стен
            await Task.Run(
             () =>
             {


             });
        }

        static async void Shot()
        {
            //Приём координат снарядов
            await Task.Run(
            () =>
            {
            /*   ДЛЯ СПРАВКИ: ТУТ МЫ ПИШЕМ СВОЙ КОД, КОТОРЫЙ БУДЕТ ВЫПОЛНЕН АСИНХРОННО  */

            });
        }

        static void To_key()
        {
            //Приём нажатой клавиши           
        }


После всего этого у меня возникли несколько вариантов организации данных, для их отправки на сервер, и тут в бой пошли лекции. Выбор стоял великий: или всё организовать в интовских/стринговских переменных и рисовать через них, или создать структуру для данных, объекты я не рассматривала т.к. не хотела возится с ссылками. Спустя часик гугляния на форумах я остановилась на втором, так как организация данных в виде структуры гораздо легче, да и писать документацию будет удобнее (совет: если есть группы данных, которые схожи по назначению — лучше будет объединить их в структуру, ибо так гораздо легче читать код и искать переменные). Наша новая задача звучит так: создать структуру в которой будут поля отвечающие за нажатую клавишу, координату игрока, угол поворота танка и (для консоли) — позицию последнего символа и желательно какой-то регулятор отрисовки. Для чего нам последнее? — чтобы не использовались координаты с которыми мы работаем (увеличиваем/уменьшаем/отправляем на сервер)

В чём же были ошибки? — спросите меня вы.

Первоначально я начала лезть в дебри http модели (хотелось сделать на http), но спустя количество времени n мне стало ясно что лучше сделать на tcp (и проблем поменьше и с растом возится легче будет).

Мы определились с потоками и с идеей, что же дальше?

А дальше, друзья, будет самое интересное.

Часть третья: создание структуры и метода Main (). Конец первого этапа разработки


Сразу кину код, чтобы нетерпеливые читатели сразу его скопипастили:

//структура наша будет приватной
//и через методы мы присваиваем ей значения
/// 
       /// Координаты игрока(численные значения)
       /// 
        public struct player_coor
        {
            
            public static void new_player_coor (int x_, int y_, string dir_, ConsoleKey key_, int last_x_, int last_y_)
            {
                 x = x_; y = y_; dir = dir_; key = key_; last_x = last_x_; last_y = last_y_;
            }

            static int x = 2;//стартовые координаты
            static int y = 2;//
            static string dir;//стартовое положене

            static ConsoleKey key;//нажатая клавиша

            static int last_x;//
            static int last_y;//последний 'y' и 'x'

        }
static void Main(string[] args)
        {

        }


Это самый простецкий код, но он делает огромную работу: он распоточивает наше будущее приложение.

Небольшое описание потоков: любой поток создаётся через пространство System.Threading. Создаём мы его так же, как и экземпляр класса, но в аргументе потока указываем void функцию.
После создания потока его можно запустить методом .Start () и отключить (вызвать исключение) методом .Abort (), но это есть синхронная модель (то есть едим и ножом и вилкой), но есть асинхронная (мы едим и пылесосим, а ноги наши при этом делают жим лёжа по +100500 подходов), которую мы и возьмём к использованию.

Вот мы написали свой первый «псевдокод» и основные положения/идеи нашего проекта. Первая стадия подошла к концу, а так же подошло к концу наше бездумство, далее мы будем разрабатывать функции и нам придётся попотеть.

Огромное спасибо:
lair, unsafePtr, vlreshet, domix32, vadimturkov, myxo за идеи и правки статьи.

Жду ваших пожеланий и идей, да и прибудет с вами сила!

© Habrahabr.ru