XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Желтая пресса: обучающие статьи
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

 
adic3x

offline
Опыт: 108,439
Активность:
Осваиваем JASS - исправления и дополнения
Пару дней назад из чувства ностальгии я перечитал статью "осваиваем JASS", статью, по которой я и сам когда-то осваивал его. Ну то, что она писалась давно, и содержала некоторые неточности я знал, но что их будет так много... а после этого я удивляюсь почему многие кодеры допускают совершенно нелепые ошибки.

Я понимаю, что сия статья писалась в далеком 2005 году (ну по крайней мере была опубликована) и конечно тогда информации было намного меньше, и в общем данная статья дала большой толчок к развитию все моддинга warcraft’a, но при всем том думаю стоит описать основные ошибки, которые в ней есть (а их там на метр квадратный хоть отбавляй). Приступим...

Я предполагаю, что вы прочли эту стать и поняли как минимум 90% из описанного там материала и теперь готовы к принятию более сложных вещей, в противном случае идите и перечитайте ее снова.

Для удобства я буду приводить цитаты из оригинальной статьи, а потом объяснять, где ошибка и как ее избежать.

1. Что есть jass и для чего он нужен



Цитата:
Более того, я не рекомендую писать все триггеры сценария исключительно на jass. Для многих задач редактор триггеров подойдет лучше – ведь это действительно очень удобная штука.


В этом разделе это единственное с чем я не согласен. Удобство редактора триггеров как и "сложность" написания JASS кода - вещи весьма сомнительные. На самом деле если вы хорошо освоите JASS (практика, практика и снова практика) то написание кода станет куда удобнее выбора GUI подменюшек. Да и еще, я до сих пор не понимаю как на триггерах пишутся сложные арифметические выражения, к примеру (a*b+c*2)/x*x+(y-8)/r. а на JASS это просто набирается и будет выглядеть точно также как и тут ;)

2. Локальные переменные



Начну с того, что Sergey дает в принципе неправильное объяснение области применения локальных переменных как таковых.

Локальные переменные созданы вовсе не для использования вместе с wait'ами (более того эта конструкция весьма порочна, в идеале я вообще не рекомендую использовать wait(и конструкции, основанные на нем), а использовать именно таймер). Реально локальные переменные - это временные переменные в прямом смысле этого слова. Память под них выделяется в момент входа в функцию, и освобождается при выходе из нее.

Локальные переменные самые быстрые (обращение к ним как правило незначительно быстрее чем к глобальным переменным, и намного быстрее чем к кешу). Так же важное свойство локальных переменных - что их область видимости ограничена одной функцией, т.е. мы не должны беспокоиться о том, что имя двух переменных из разных функций совпадут, и что при пересечении потоков (если вы не поняли о чем я то ничего страшного) может быть получены/записаны не те данные и т.д.

Где применять локальные переменные? Если в рамках одной функции мы либо должны производить какие либо вычисления а потом обращаться к их результату достаточно часто - результат стоит поместить в переменную. Если мы должны обращаться к какому либо объекту - его стоит занести в переменную.

Цитата:
Есть способ исправить эту проблему: для каждого запуска файербола помещать значения не в переменные, а в ячейку массива. Для каждого запуска сохранять значения в свои ячейки, каким-то образом отслеживать, что пришел момент создать спецэффект для такого-то юнита из массива или удалить такой-то спецэффект из другого массива. Это не очень удобный и достаточно громоздкий способ. В итоге, простая по сути задача – становится очень тяжелой.


именно так реализованы struct в vJass. реально этот метод более чем удобен, не имеет побочных эффектов (какие имеет wait) и оправдывает себя на 100%. Я ниже подробно опишу его.

Цитата:
Ты умеешь создавать их при помощи редактора переменных.


Конечно когда писалась эта статья JassHelper'а еще не было, или он был еще в зачаточном состоянии. В любом случае я советую установить его и объявлять переменные в нем непосредственно в коде - это намного удобнее, тем более что можно отказать от уродливого префикса udg_). Делается это так:

Код:
globals
 //<тип переменной> <имя> = <значение>  // просто переменная
 //<тип переменной> array <имя>         // массив

 integer MyInt=12   // целочисленная
 unit array Heroes  // массив юнитов
endglobals


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

Более того, для большей понятности читателю Sergey переносит потом данные из локальной переменной в глобальную. Понятно что так делать не стоит)

3. Применение локальных переменных



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


Нет, нет и еще раз нет. Если использовать таймеры - вы напишете примерно столько же кода, но обезопасите себя от тупых багов, более того с точки зрения быстродействия это также ужасно... Понятно что в статье рассматриваются примеры "для новичков".

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

Цитата:
Локальные переменные выступают как хранилища на время пауз в триггере, глобальные переменные нужны для каких-то мгновенных действий


И опять же с точностью до наоборот - глобальные переменные нужны в т.ч. для отсроченных действий (к примеру какие либо счетчики, или главные герой, который будет использоваться на протяжении всей игры, или позиции точек появления мобов) а локальные переменные для действий в одном потоке (ну в одной функции без прерываний потоков (wait)).

Цитата:
Итак, Читатель, ты уже достаточно узнал, чтобы создать свой собственный jass код. Правда, есть определенные тонкости который тебе нужно узнать.


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

4. Условия, циклы в jass



Сразу скажу что автор упустил то, что конструкция if куда более гибкая на JASS:

Код:
if i==0then
 // если равно 1
elseif i==10 and (b==2 of c>3)then
 //
 // если i равно 10 и при этом
 // или b равно 2 или c больше 3
 //
elseif i==1then
 // если равно 1
elseif i==2then
 // если равно 2
elseif i==3then
 //
 // --->
 //
else
 //
 // если не удовлетворено ни одно условие,
 // ставить необязательно
endif


Итак, эта конструкция может быть почти любой, это стоит учитывать.

О циклах добавлю, что в директиве exitwhen boolean, значение может быть получено из любого выражения, к примеру

Код:
local integer i=0
loop
 // --->
 set i=i+1
 exitwhen i==12 or GetRandomInt(0, 99)==0 or (u!=null and y>2 and x<=2.12)
endloop


В этом цикле при исполнении директивы exitwhen сначала будет проверенно значение переменной i, если она равна 12 произойдет выход из цикла, если же она не равна 12 будет вызвана функция GerRandomInt (получить любое целочисленное число от 0 до 99), если это число - 0 произойдет выход из цикла, если же это число не будет равно 0 то будет проверенна переменная u, если ее значение не равно null, и значение y больше двух и значение x меньше или равно 2.12 то произойдет выход из цикла, иначе произойдет прыжок к метке loop.

Вообще это очень важно правило при вычислении значений переменных типа bollean, к примеру if A() and B()then, если A вернет false то B вызвана не будет, и алогично if A() or B()then, тут если A вернет true то B вызвана не будет.

Теоретически также цикл может содержать несколько exitwhen, но на практике это применяется редко.

5. Функции в jass



Цитата:
AdjustPlayerStateBJ - это встроенная функция с тремя параметрами. Список всех таких встроенных функций имеется в MPQ архивах. Так что получается у нас, что все триггеры устроены так, что одни функции ссылаются на другие, те на третьи и т.д.


Остается уточнить что в конечно случае все функции, которые так или иначе воздействуют на игровой процесс "вшиты" в движок, определяются как native и их список находится в MPQ архиве в файле scripts\common.j

Также существуют паразитические BJ фунции, о них советую почитать в стать Jon'a "оптимизация".

6. Устройство триггера с точки зрения jass



Цитата:
Исключение Map Initialization, но это отдельный разговор.


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

Рекомендую извлечь с помощью MPQ архиватора из любой карты war3map.j и поизучать его. Это что то вроде домашнего задания ;)

7. Динамическое создание триггера



Вообще пример с массивами не так уж плох. Но на что я хочу тут обратить внимание... Негоже искать юнита в массиве перебором всех его элементов. Для этого используются разные damage detection системы, в общем принцип работы у них такой же, но на триггер "аттачиться" ссылка на юнита, но это уже другая тема)

8. События с малым периодом



Ну первое что я хочу заметить - удобнее использовать место триггеров таймеры, хотя каких либо серьезных отличий этот вариант не имеет.

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

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

9. Полярные координаты (ликбез)



Цитата:
Если у вас есть две точки A и B, координаты которых нам известны. Как вычислить координаты третьей точки C, находящейся на заданном расстоянии R от точки A в направлении к точке B?


Если у нас заранее не посчитан угол между точками - то вектором.

Сейчас поясню - вектор из точки a в точку b равен (b.x-a.x; b.y-a.y). Длина вектора равна корню квадратному из (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y). Теперь сделав просто деление можно определить координаты искомой точки:

Код:
//local real ax
//local real ay

//local real bx
//local real by

//local real vx
//local real vy

//local real r

set vx=bx-ax
set vy=by-ay

set r=(vx*vx+vy*vy)/range // range = расстояние, причем в данной формуле оно не должно быть возведено в квадрат
                      // поскольку мы ищем именно соотношение данных велечин

set vx=vx/r
set vy=vy/r


Будет ли это работать быстрее? Не знаю, в принципе если считать это единожды оно так на так и выйдет, но! Во первых вектор посчитан изначально (хотя можно и с помощью Sin(real)/Cos(real) посчитать его единожды), а при работе в 3D векторы более чем оправдывают свое использование.

Да и еще, помните, что функции Cos и Sin принимают угол в радианах, это значит то что развернутый угол равен 3.1415... (пи), а прямой 1.57...

10. Оптимизация: утечки памяти



Цитата:
Умершие юниты должны быть удалены действием Remove Unit, чтобы не занимать место в памяти.


Это не так - умерший юнит после разложения благополучно удаляется. Более того его handle будет удален если на него нету никаких ссылок (он не находится в переменных).

Кстати это и есть объяснение почему надо обнулять локальные переменные - после помещения юнита в локальную переменную и выхода из функции его handle становится "висячим" и не может быть использован повторно. Более того насколько я догадываюсь это таки небольшая утечка (но не сравнима с неудаленным объектом).

В большинстве случаев можно считать координаты без точек вообще (в принципе иногда можно использовать точки для оптимизации, к примеру если нам параллельно надо вычислить высоту поверхности но опять же это не намного быстрее) - поэтому мой совет - не используйте точки! все аналоги функций есть и на координатах (кроме GetLocationZ(location) & GetSpellTargetLoc()).

Во первых вам не надо выделять время на создание/перемещение/удаление/обнуление. Во вторых точки двумерны и делать 3d движение с ними неудобно. В третьих работая с координатами вы получаете быстродействие. Что бы сместить точку нам надо вызывать функцию, а смещение координат делается арифметическим действием, что явно быстрее.

Код:
function AngleBetweenPoints takes location locA, location locB returns real
    return bj_RADTODEG * Atan2(GetLocationY(locB) - GetLocationY(locA), GetLocationX(locB) - GetLocationX(locA))
endfunction

// --->
set p = GetUnitLoc(u)
set a = AngleBetweenPoints(p, p2)
call MoveLocation(p, GetLocationX(p) + 50 * CosBJ(a), GetLocationY(p) + 50 * SinBJ(a))
// <---


Тут используется совершенно ненужная функция, которая вычисляет угол, а реально же она сначала делает вектор, потом вызывает Atan2, и потом с помощью Sin & Cos мы получаем искомые координаты.

Как бы это делал я

Код:
local real x=GetUnitX(u)
local real y=GetUnitY(u)
local real r

local real x2=1234. // это координаты второй точки
local real y2=5678. // 

set x2=x2-x
set y2=y2-y

set r=(x2*x2+y2*y2)/50.

set x=x+x2/r
set y=y+y2/r


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

Но если мы знаем угол (и он у нас храниться в радианах) то можно поступить так:

Код:
set x=x+50.*Cos(a)
set y=y+50.*Sin(a)


Цитата:
call RemoveLocation(p)
set p=null // !!!


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

Цитата:
Читатель, если удалить все основные утечки, то для 99,99% сценариев больше ничего не нужно оптимизировать.


Что же, это тоже неправда. Лаги могут быть не только от засорения памяти, ну и от слишком "долгого кода". К примеру попробуйте каждые .01 секунды создавать полсотни точек и удалять их и вы поймете о чем я. Дело в том что каждая инструкция в коде требует время на исполнение, поэтому когда вы пишете код, особенно в случае с функциями, вызываемыми на малых периодах вы также должны использовать максимально быстрые инструкции и не совершать лишних действий.

Цитата:
Но, как оказалось, существуют и другие виды утечек. Например, куда девается локальная переменная после того, как триггер кончил исполнение? На самом деле они продолжают сидеть в памяти.


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

Код:
set i = 0
set r = 0
set s = ""


В этих трех строках сделано 4 ошибки)

1) Тип integer - скалярный - это значит что он не является ссылкой на какой либо обьект. обнулять его не надо, его реальное значение соответствует тем байтам, что хранятся по адресу переменной в стеке, и он будет перезаписан при выходе из функции и повторном использовании локальных данных
2) Тоже касается и real
3) Строки обнулять не надо, хоть они и являются ссылками. строки не удаляются, и не имеет "счетчика ссылок"
4) Если мне нужна пустая строка я напишу set s=null , в этом примере же переменной присваивается ссылку на полноценную строку, у которой есть как либо обозначенное окончание.

Еще хочу сказать - обнулять не надо boolean, а также handle, которые не требуют этого (player обнулять смешно просто).

11. RETURN BUG (RB)



Цитата:
Вообще говоря разработчики war3 вряд ли хотели, чтобы картостроители могли работать с памятью, но они допустили при разработке один важный баг, которым научились пользоваться спецы по jass. Баг не позволяет менять содержимое ячейки памяти - но зато он дает возможность для любого игрового объекта найти номер ячейки памяти, в которую он записан, а также по номеру ячейки найти ссылку на объект.


Это не так, доступа к области памяти, в которой хранятся объекты доступа с помощью RB добиться нельзя. на факте RB - это обход типобезопасности в JASS.

Все переменные в JASS - dword, поэтому такая фичя не приводит ни к каким осложнениям. Хорошо, сейчас объясню)

Переменная - это место в памяти компьютера, куда можно что либо записать. Переменные можно условно разделить на скалярные типы (real, integer, boolean в JASS) и ссылочные - это все остальные. В первом случае информация о непосредственно самом объекте храниться в самой переменной (к примеру если мо помещаем в переменную i единицу, то в памяти, там где числиться наша переменная биты принимают значение 1). Во втором же случае если мы присваиваем переменной типа unit значение какого либо юнита, то в переменную, точнее по ее адресу запишется некоторое id, и по этому id движок найдет юнита. Точнее сказать записывается "ссылка на ссылку", т.к. объекты не могут быть расположены так близко.

RB дает возможности обойти это. т.е. благодаря ему мы можем узнать это id и записать его именно как число, и обратно же мы можем любое число использовать как id. Вообще это бред ;) (ну к примеру в нормальном языке программирования это просто нонсенс использовать отдельную функцию для возможности обращаться к переменной так, как ты считаешь нужным, во много благодаря тому что понятие переменной, как и понятие функции там всего лишь макросредства, надеюсь все поняли что я о своем горячо любимом ASM).

Код:
function H2I takes handle h returns integer
 return h
 return 0
endfunction


Что делает эта функция? Мы сообщаем в нее переменную типа handle которая к примеру имеет значение 0x12345678 (я записываю числа в 16ричной нотации, если вы не знаете что это прочитайте мою статью "тип integer"). Теперь она нам возвращает это же значение, но уже типа integer, результат кода set i=H2I(h) будет таким, что в переменной i окажется... 0x12345678

Мы можем хешировать (сжать) нашу переменную типа integer с помощью элементарной арифметики и использовать как индекс к массиву. Идем дальше, хотя лично я не всегда рекомендую так поступать, почему - объясню потом.

Код:
function H2I takes integer i returns handle
 return i
 return null
endfunction

//

set h=I2H(h)


теперь мы передаем число 0x12345678 и получаем переменную h типа handle со значением 0x12345678. и теперь мы можем передать это в любую native функцию для совершения реальных действий. вот и вся суть.

Цитата:
Скажем, на твоей карте несколько сотен регионов, в которых должны появиться одни и те же монстры и отправиться в патруль в следующую точку. Проще всего делать это через массивы с циклами.


Стоп! Это - измена! Юниты создаются либо по координатам (что самое лучшее, т.е. два параметра типа real, либо надо передать объект типа location, что не так удобно). Как многие создавали юнитов раньше - это было сделано за счет создания точки посреди региона и в ней создание юнита. но это бессмысленно. это не нужно ни вам ни компьютеру! Не делайте так!

Цитата:
А я сделал предположение, что регионы, которые мы создаем в редакторе, занимают соседние ячейки памяти. Т.е. если создали мы первый регион - он попал в ячейку X, тогда следующий регион попадет в ячейку X+1 и т.д. Т.е. если мы знаем номер первого региона и остальные регионы создавали в определенном порядке, мы можем при помощи RB найти все остальные регионы и занести их в массив на автомате.


Это - тоже не правильно, сейчас объясню. к примеру мы создаем 10 регионов. Пусть их handle будут равны x+0; x+1; x+2; ... ; x+9. теперь мы удаляем первые 5, тогда движок считает что первые 5 handle свободны и в них можно помещать снова ссылки. При повторном создание объектов их handle будут равны x+0; x+1; ... ; x+4; x+10; x+11; ... ; x+14, и при попытке обратится к 10 регионом, от x+0 до x+9... Надеюсь ошибка понятна.

Цитата:
Дело в том, что все ячейки памяти имеют сквозную нумерацию. Т.е. благодаря RB мы можем сопоставить всем объектам их номера (точнее номера ячеек, которые они занимают в памяти). И все эти номера будут уникальны. Совпадений не будет.


И это не так) Если вы будете удалять триггер в его же потоке, потом использовать TriggerSleepAction(real) и создавать новые обьекты - это более чем возможно.

12. Тип Handle



Насчет типа handle я советую всего навсего открыть common.j и посмотреть какие типы от чего наследуются. Хотя по сути это все иллюзия, на факте же все типы это dword (т.е. просто 32 бита данных), значение может иметь что помещено в этот dword и как мы к нему обращаемся.

13. Система Super Custom Value (SCV) или RB+cache



Sergey в дополнении к статье описывает порочность этой системы и предлагает крайне уродливое решение основанное на локальной переменной и wait. Очень надеюсь что всерьез это решение никто не рассматривает.

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

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

Есть очень удобное макросредство в JassHelper, имя ему - struct. Хотя лично я пишу все сам ручками - это копирование примерно 15 строк кода и изменение нескольких символов, с другой стороны я извлекаю из этого некоторую выгоду, но к нашему рассказу это отношения не имеет.

Код:
struct spell
 unit caster
 unit target
 integer lvl
endstruct

// --->
function Spl takes nothing returns nothing
 local timer t=GetExpiredTimer()
 local spell s=*attach get*(t)
 //
 // action with s.caster and s.target
 //
 call s.destroy()
 call DestroyTimer(t)
 set t=null
endfunction

function SpellAction takes nothing returns nothing
 local spell s=spell.create()
 local timer t=CreateTimer()

 set s.caster=GetSpellAbilityUnit()
 set s.target=GetSpellTargetUnit()
 set s.level=GetUnitAbilityLevel(s.caster, 'A000')

 call *attach*(t, integer(s))
 call TimerStart(t, 2., false, function Spl)
 set t=null
endfunction
// <---


Первое, строка integer(s) это чисто для видимости, иначе JASSHelper выдаст ошибку, реально же это и есть integer.

Теперь, как работает этот код? Очень просто, вас стоит сохранить карту с ним и посмотреть содержание war3map.j

У нас есть три массива (реально) и есть функции, которые выдают свободный индекс или помечают использованный как свободный. s.create() дает нам такой индекс. Далее мы использую его заполняем массивы. потом мы аттачим этот индекс к таймеру (можно и через кеш, можно через аналогичные системы, к примеру XAT). Запускаем таймер, когда таймер истечет будет вызвана функция, которую мы указали в параметрах его запуска. В ней мы снова извлекаем (с помощью RB конечно) из таймера индекс к нашим массивам, и делаем действия с данными занесенными в них ранее.

Я скажу больше, иногда достаточно одного таймера. Этот пример есть в карте, внизу статьи, постарайтесь разобраться в нем сами.

14. Да здравствует SCV!



Цитата:
Рассмотрим один из наших старых примеров – полет юнита снаряда. Можно ли улучшить его при помощи SCV? Раньше нам приходилось использовать массивы, чтобы сохранить информацию, что такой-то юнит-снаряд летит к такой-то цели и имеет такой-то уровень заклинания.


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

15. Послесловие



отлов вреда, получаемого юнитом (или damage detection systems) - тема отдельная, и требующая долгого описания, там есть много своих нюансов, поэтому описывать ее я не буду. Пришло время и мне заканчивать.
Прикрепленные файлы
Тип файла: w3x source.w3x (12.6 Кбайт, 88 просмотров )

Отредактировано ADOLF, 19.11.2008 в 18:17.
Старый 27.08.2008, 21:04
krimatoriy
U.C.
offline
Опыт: 2,654
Активность:
Мда мне бы понять стандартный, а твои исправления буду использовать нескоро, а так - тру
Старый 27.08.2008, 21:37
zer0ne

offline
Опыт: 11,219
Активность:
krimatoriy мне кажется если только начал изучать стандартный, то и это на заметку надо взять, иначе потом многое придется понимать заново
Старый 27.08.2008, 21:39
krimatoriy
U.C.
offline
Опыт: 2,654
Активность:
Точно, ты прав хотя с началом сентября, боюсь времени ваще не будет(начнутся унылые трудовые будни)
Старый 27.08.2008, 22:05
Олежа

offline
Опыт: 3,283
Активность:
Если это проще статьи Сергея, то я с удовольствием прочитаю.
Старый 27.08.2008, 23:33
adic3x

offline
Опыт: 108,439
Активность:
Цитата:
Если это проще статьи Сергея

это не проще, это описание ошибок которые допущенны в онной статье

Отредактировано ADOLF, 28.08.2008 в 00:52.
Старый 27.08.2008, 23:51
exploder
iOS zealot
offline
Опыт: 19,394
Активность:
Цитата:
ссылка на ссылку

Имхо handle это не ссылка (по определению), это id обьекта в системе, так же как и хендлы окошек и контекстов в винде. Для хендл обьектов в jassе создана искуственная типизация и искуственные наследования, а доступ к прямым значениям защищен от пользователя. RB как раз и снимает эту защиту.

Сам движок же оперирует реальными структурами обьектов в памяти, а с подсистемой jass общается на языке хендлов. Либо это способ защиты раздела памяти игры, толи близзы просто не стали заворачиваться и делать полновесное ОО для всего лишь системы сценариев.

Отредактировано exploder, 28.08.2008 в 02:08.
Старый 28.08.2008, 02:02
Gres
Кораэлестраз
offline
Опыт: 18,575
Активность:
ADOLF Лучше напиши свою статью по JASS на основе этой... Исправь имеющиеся ошибки и дополни, новыми статьями. (с разрешения автора статьи конечно).

Я лично полностью за это... В крайнем случае можно создать опрос(нужна новая статья или нет)

p.s. В общем думай!
Старый 28.08.2008, 08:17
adic3x

offline
Опыт: 108,439
Активность:
Цитата:
Имхо handle это не ссылка (по определению), это id обьекта в системе, так же как и хендлы окошек и контекстов в винде. Для хендл обьектов в jassе создана искуственная типизация и искуственные наследования, а доступ к прямым значениям защищен от пользователя. RB как раз и снимает эту защиту.

ну это и так ясно что адрес бы тогда шел не +1 а +4, но всетаки подправил

рб никакой защиты не снимает, он снимает эту "искуственную типизацию"

я вообще хз зачем ее было делать - концепция "програмист дурак, компиль умный, компилю виднее что да как" меня бесит=/

ADOLF добавил:
Цитата:
напиши свою статью по JASS

это займет слишком много времени, да и понять ее мало кто сможет=/
Старый 28.08.2008, 09:10
PlayerDark
Coraline
offline
Опыт: 10,569
Активность:
ADOLF это приверженец абсолютной оптимизации кода. Оптимизируя код главное не забывать зачем ты это делаешь, иначе это может превратиться в самоцель в ущерб карте ...

PlayerDark добавил:
Нет, конечно здравые мысли тут есть но с некоторым я не согласен.
Старый 28.08.2008, 09:40
DFlyer
<3 MJ
offline
Опыт: 42,879
Активность:
Цитата:
но с некоторым я не согласен

Аргументируй, с чем именно?
Старый 28.08.2008, 09:41
PlayerDark
Coraline
offline
Опыт: 10,569
Активность:
Конкретно - использование параллельных массивов - это очень не гибкий способ тк при любых изменениях алгоритма работы приходится изменять большую часть кода.

PlayerDark добавил:
Я делал системы с кешем которые не тормозили даже на моем не очень мощном компьютере.
Старый 28.08.2008, 10:05
ScorpioT1000
Работаем
offline
Опыт: отключен
Цитата:
Конкретно - использование параллельных массивов - это очень не гибкий способ тк при любых изменениях алгоритма работы приходится изменять большую часть кода.

PlayerDark добавил:
Я делал системы с кешем которые не тормозили даже на моем не очень мощном компьютере.

точнее совсем наоборот
Старый 28.08.2008, 11:15
adic3x

offline
Опыт: 108,439
Активность:
вообще то паралельные массивы как раз очень удобны для их переработки если надо что то изменить в алгоритме, куда удобнее кеша!

это все с чем несогласен или ты не согласен впринципе?)

Цитата:
ADOLF это приверженец абсолютной оптимизации кода


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

Цитата:
Я делал системы с кешем которые не тормозили даже на моем не очень мощном компьютере.
а я делал 4 года назад систему которая создавал кажды 30 сек 3 крипов на гуи и она не тормозила - и?

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

1) гуи - 10 юнитов вызывают лаги
2) корявый жасс с кеше - 20 юнитов вызывают лаги
3) оптимизированный тру код - 30 юнитов почти не вызывают лагов

вы просто расширяете свои возможности...

ADOLF добавил:
Цитата:
Нет, конечно здравые мысли тут есть

o rly?! o rly?!
Старый 28.08.2008, 15:21
PlayerDark
Coraline
offline
Опыт: 10,569
Активность:
Вобще то это два принципиально разных подхода в скриптописании. Кеш нравится мне в том смысле что он абсолютно не ограничен т.е в него можно записать сколько угодно данных, какой угодно структуры и при этом система не будет связана со всеми остальными.
Старый 28.08.2008, 19:07
krimatoriy
U.C.
offline
Опыт: 2,654
Активность:
Цитата:
Кеш нравится мне в том смысле что он абсолютно не ограничен т.е в него можно записать сколько угодно данных, какой угодно структуры и при этом система не будет связана со всеми остальными.

по мне так карты для сетевой игры имеют более существенное будуйщее , а там вроде бы кэш неуместен

Отредактировано DFlyer, 28.08.2008 в 20:11.
Старый 28.08.2008, 19:35
Лось

offline
Опыт: 7,223
Активность:
В МП просто нельзя сохранить кеш. С кешем просто бывают глюки, когда обнуляеш переменные. Со структами в JNGP удобно работать и глюков небывает, а кеш я для баз данных использую.
Старый 28.08.2008, 19:42
adic3x

offline
Опыт: 108,439
Активность:
Цитата:
а кеш я для баз данных использую

дык на массивах жк удобнее? я не говорю что быстрее, но удобнее то точно!
Старый 28.08.2008, 20:32
Sailar

offline
Опыт: 8,555
Активность:
Понравилась статья только это вряд-ли для новичков они непоймут у сергея была понятние хотя я его статью и нечитал до канца так нужные моменты.
Вобще как сейчас понимаю что действительно глобалки лудше кэша непотомучто удобнее а пото-му что тормаза невызывают.
Просто вот так с кэша коем пользовался всегда сложно перейти на глобалки уже автоматом пишеш функции кэша, а потом вспоминаеш ой юзаем структуру. + Старые коды фанарь править с кэша на глобалки слишком много.
..
А вобще неуверен но как мне кажеться весь вар использует кэш не тот что в jasse.
Ну просто я Хз как в обычном програмирование на том же С++ катором писался вар. Манипулировать с данными Типом того же обекта если допустим юнит бы создавался и все его данные о том что его отрендерил движок игры записавались в Глобалку int то как к нему бы обращалась игра? т.е возможно у близов свои системы с массивами, только понятия неимею их устройства. Но помне было бы целесообразний помещать в кэш юнита. Возможно это бред т.к я очень плохо знаю нормальный язык програмирования, просто любопатно как приблезительно близзы записавали значения юнитов и куда к котороым мы всою очередь обращаемся с помощью переменной unit. Ведь игра создает тагже юнита и тагже перебирает юнита допустим с 1-2 когда выделяем второво мы можем с ним производить действия. Тагже и спервым. Ну это полюбому все данные кэшировалиь.
Это хоть флуд но просто любопытно может кто знает как там все устроенно.
Старый 28.08.2008, 21:18
adic3x

offline
Опыт: 108,439
Активность:
в "нормальных" языках возможности адресации куда более широкие, поэтому сравнивать их с жассом никак низя=/
Старый 28.08.2008, 23:32

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 09:54.