nanoCAD и сбоку бантик, или программируем «Реверси» под CAD-платформу

    Некоторое время назад на Хабре в блоге Нанософта была опубликована статья о программировании под nanoCAD. И в тот же день проскочила статья о «программировании Реверси на Python». Спустя неделю Реверси были написаны уже на Silverlight, чуть позже – на Tcl/Tk. Можно было смело сказать, что игра Реверси пошла в народ. Как раз в это время я занимался изучением возможностей скриптов под nanoCAD. Но изучение требовало практики. И я подумал – а чем я хуже? И решил написать эдакий «нано-боян». Так появились 3D Reversi.

    Title image

    Скриптовые возможности CAD платформ всегда меня привлекали. С помощью скрипта можно прикрутить свой собственный функционал к большой системе. Тем самым, используя CAD уже не только как в нее «зашили» разработчики, но и в моих субъективных целях.

    Для практики изучения симбиоза nanoCAD и JScript я стал писать «несерьезный» скрипт, под САПР систему.

    Начало


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

    Для разработки игрового поля мне необходимо было создавать объекты в пространстве модели nanoCAD. Без документации все попытки писать что-то под что-то наверно обречены. И первое, что я сделал – я стал искать описание объектов и функций. Я спросил у Гугла (почти как в песне поется) и получил большое число ссылок на разные сайты описывающие объектную модель AutoCAD. К примеру этот сайт.

    Создание объектов


    Добавление объектов происходит с помощью коллекции ThisDocument.ModelSpace. Сразу оговорюсь, что я писал на JScript, потому как я программирую на С++ и синтаксис JS мне ближе, чем синтаксис VB Script.

    Поле представляет собой 64 клетки, сетку и фишки. Сетку и фишки я сделал объемными фигурами. А для клеток поля я использовал штриховки.

    //Создаем рамку для штриховки (переменная shag задает сторону клетки).
    var polyline;
    var RefPoly = new Array(0,0,0, shag,0,0, shag,shag,0, 0,shag,0);

    polyline = ms.AddPolyline(ut.CreateTypedArrayFromJSArray(5, RefPoly));
    polyline.Closed = true;

    //После чего заштриховываем рамку:
    hatch = ms.AddHatch(1,"SOLID",false,0);
    hatch.AppendOuterLoop(polyline);


    Потом я размножил клетки и расставил их по местам.

    Дальше мне нужны бордюры и игровые фишки. Совсем копировать игру, написанную на Питоне, было не интересно. Нужна была своя изюминка. Так как nanoCAD имеет возможность отображать объекты в пространстве, этой изюминкой стала объемность доски и игровых фишек. Поэтому бордюры и фишки я сделал объектами 3DMesh.

    Каждое ребро – это просто прямоугольник. А каждая фишка – это сфера.
    Объекмные объекты – это объекты типа 3DMesh. Они создаются методом Add3DMesh(…) коллекции ModelSpace. Это сеть размера M на N клеток. Для ее создания нужно указать в массиве координаты всех вершин сети. По рядам: сперва точки первого ряда, потом второго и так до M.

    //Строим массив координат точек сети:
    var mesh = new Array();
    var n = 0;
    for (i=-1;i<2; ++i)
    	for (j=0;j<9; ++j)
    	{
    		mesh[n]  =0;
    		mesh[++n]=shag*j;
    		mesh[++n]=shag*i/10;
    		n++;
    	}
    // Создаем прямоугольник, заданный координатами, лежащий в плоскости YZ.
    var mesh3d = ms.Add3DMesh(3,9,ut.CreateTypedArrayFromJSArray(5,mesh));

    Потом я все же добавил ребрам толщину, чтобы ребра не исчезали при виде сверху.

    Построить сферу было посложнее, чем ребра. Пришлось вспомнить аналитическую геометрию времен 1-го курса. В библиотеку не пошел. Пошел в википедию, и узнал, как выражаются сферические координаты через декартовы.
    var Vertexs = new Array();
    var idx =-1;
    var r =22;
    for(j=0; j <= 20; ++j)
    	for(i=0; i < 20; ++i)
    	{
    		beta = Math.PI / 19 * j;
    		alfa = 2 * Math.PI / 20 * i;
    		Vertexs[++idx] = 25 + r*(Math.cos(alfa)*Math.sin(beta));
    		Vertexs[++idx] = 25 + r*(Math.sin(alfa)*Math.sin(beta));
    		Vertexs[++idx] = r*Math.cos(beta);
    	}
    fishka_b = ms.Add3DMesh(20,20,ut.CreateTypedArrayFromJSArray(5,Vertexs));


    В результате этих действий получилось такое игровое поле:
    picture 1

    Взаимодействие с пользователем


    Следующим шагом было организация взаимодействия скрипта с пользователем. Нужно спросить, куда игрок хочет поставить свою фишку. Этим занимается функция GetEntity().

    // Смотрим, какие объекты выбраны, до тех пор, пока не выбран 1 объект штриховка
    while (hatch==null)
    {
    	ut.GetEntity(RefPoly,null,"Ваш ход");
    	if (RefPoly.length == 1 && RefPoly [0].EntityName == "AcDbHatch")
    	{
    		// убеждаемся что эта штриховка – это клетка поля. Она хранит координаты клетки.
    		if (RefPoly[0].Hyperlinks.Count > 0)
    			hatch = RefPoly [0];
    	}
    		
    	x = parseInt (hatch.Hyperlinks.Item(0).URLDescription);
    	y = parseInt (hatch.Hyperlinks.Item(0).URLNamedLocation);
    }

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

    // здесь i, j – это координаты клетки
    hatch.Hyperlinks.Add("xy",i.toString(),j.toString());

    //Когда пользователь выбирает клетку для хода, я получаю эти координаты обратно:
    x = parseInt (hatch.Hyperlinks.Item(0).URLDescription);
    y = parseInt (hatch.Hyperlinks.Item(0).URLNamedLocation);


    Останавливаться подробнее на алгоритмах я не буду. Писать супер умный AI я не стал. А функцию принятия решения я скопировал из исходника Реверси на Питоне. Надо сказать, что я до этого дня не видел язык Питон ни разу. Все вполне читается. Адаптация к JScript заняла у меня минут 10.

    Нюансы и хитрости


    Теперь о нюансах скриптостроения под НаноКАД.
    Точно не знаю как в VBS, а в JScript все переменные передаются в функции по значению, а не по ссылке. Что это значит? Это значит, что если вы указываете при вызове метода переменную JScript и ожидаете, что после выполнения метода вам вернется результат, то вы огорчитесь – этого не произойдет. Переменная не изменится. Так случиться, например, с методом GetEntity(Object, PickedPoint [, Prompt]). Первым параметром в нем является переменная, через которую возвращается набор выбранных объектов. Но в JS это не работает. Как обойти это ограничение? Нужно передать вместо переменной массив.

    var RefPoly = new Array();
    ut.GetEntity(RefPoly,null,"Выберите объект");
    var entity = RefPoly[0]; // в массиве лежат выбранные энтити.


    Второй важный момент состоит в том, что методы, связанные с координатами (создание объектов, например), не принимают скриптовые массивы. Так как JS и VBS языки слабо типизированные, то их массивы могут содержать элементы разных типов. Это недопустимо. Для приведения к типизированному массиву служат функции объекта Utility CreateTypedArray(varArr,Type,ParamArray) и CreateTypedArrayFromJSArray(Type As Long, varJSArray). Тут Type – это указание типа элементов массива, а JSArray — сам массив.

    // Создаем js массив с координатами вершин полилинии
    var RefPoly = new Array(0,0,0, shag,0,0, shag,shag,0, 0,shag,0);

    // Создаем полилинию, передавая в нее координаты вершин, преобразованные к TypedArray.
    var polyline = ms.AddPolyline(ut.CreateTypedArrayFromJSArray(5,RefPoly));


    Запуск скрипта


    Для правильного отображения элементов игры нужно задать стиль освещения моделей. Стиль задается в меню Вид -> Стиль. Я задаю стиль «Точное» без показа ребер.
    Чтобы запустить реверси пишем в командной строке nanoCAD команду «JS». А потом указываем полный путь к скрипту nanoReversi.js.

    Результат


    После трех вечеров я достиг поставленной перед собой цели – игра реверси заработала под nanoCAD.
    picture 2

    В изомерии Реверси выглядят гораздо необычней. Я был абсолютно доволен.
    picture 3

    Несмотря на то, что реверси я написал, что называется «Just for fun», скрипт демонстрирует возможности ActiveX Automation nanoCAD. А если вспомнить, что в скриптах можно использовать и другие ActiveX сервера, например ADO для подключения к базам данных, то скриптовые задачи уже не являются столь не серьезными, как Реверси.

    Демонстрационный скрипт можно скачать здесь.

    Нанософт

    65,00

    Компания

    Поделиться публикацией
    Комментарии 14
      +4
      Любят на Хабре реверси…
        0
        Да, кстати, мы по-прежнему набираем вакансии в ЗАО Нанософт — смотрите вакансии тут: habrahabr.ru/company/nanosoft/vacancies/
          +6
          Ждем реверси на 1С =)
            +5
            лучше не надо…
              0
              Уже написана. rghost.ru/1372005
                0
                Эх… а я то загорелся :)
                0
                … с дедлоками)
                0
                3D — современный конёк!
                  0
                  что ж оказываеться реверсы люблю не только я, популярная игра)
                    +1
                    Нанософт, вы таки решили обойти Микрософт, да?

                    Статья хороша
                      +4
                      уж по названию точно обошли :-)
                      0
                      А в нанокаде для скриптования только Jscript?
                      Какой-то он страшненький после Руби с Питоном…
                        0
                        сейчас рассматриваем возможность подключения Питона — теоретически никаких проблем нет. следите за новостями :-)
                        0
                        Раз пошла такая пьянка. Javascript Reversi.
                        davidbau.com/reversi/

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое