Автор Тема: Скрипты для NewDark  (Прочитано 10287 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн HellRaiser

  • Тень
  • Сообщений: 5017
Скрипты для NewDark
« Ответ #15 : 27 Марта 2017 14:33:51 »
Ну вот "где-то там" как раз и описана вся суть (будь то сторонняя библиотека или конкретные апи), а в простенькой программе нужно конкретизировать определённое поведение через все эти полиморфизмы и наследования. Да и как я уже говорил тут на форуме давно: программизм это умение складывать пазл, запчасти которого и есть сторонние библиотеки и интерфейсы. Нужно только грамотно их сложить так, чтобы это в итоге заработало, а ошибки и исключения выкидывались не в твоей программе, а в тех самых кусках, в которые ты не можешь залезть по копирастным или физическим причинам.

Вот есть у тебя некий интерфейс, ну назовём его "сценой" (я в этих ваших мрак-моторах ещё не ковырялся достаточно подробно ). Вот есть у сцены свойство "дождь". Есть сообщение (метод) по которому генерится начало и конец дождя (а может и не генерится). А ты решил взять и сделать сцену со снегом. И тут у тебя 2 варианта: или переписать огроменнейший класс только ради того, чтобы изменить одно свойство, или унаследоваться от имеющегося и добавить ему параметр, отвечающий за выпадение снега. В итоге это выглядит как глобальный условный оператор, только не на уровне кода, а на уровне компилятора, который без нас знает, когда и чью конкретную функцию вызвать.

Умные люди (энто я конечно же совсем не про нас с вами  :bigsmirk:) проектируют  интерфесы так, чтобы кодер мог с лёгкостью модифицировать поведение имеющегося класса для своих нужд, не трогая при этом сотни тысяч строк уже готового и отлаженного кода.

ну вот как примитивнейший пример (как это б выглядело):
class scene
{
...
virtual void OnWeatherChange()
{
  ...
   StartRain(); //
  ...
}
...
};

class snow_scene : scene
{
void StartSnow()
{
...
}
virtual void OnWeatherChange()
{
  StartSnow();
}
};

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




Оффлайн Zontik

  • Фантом
  • Сообщений: 17087
    • Тёмное место
Скрипты для NewDark
« Ответ #16 : 27 Марта 2017 14:49:22 »
Собственно, я бы так и поступал со снегом, будь он у меня. И именно так поступаю с дождем. А то, что предлагает Чужой (переписать дождь на Белке) - это и есть "переписать огромнейший класс", когда можно просто взять и поменять параметр в графе "вид осадков". Да?
Правда, переписывать класс в этом случае все равно придется, поскольку галки "стартовать дождь по триггеру" в существующем классе нет, и просто взять и добавить ее нельзя. Только лезть в "туда, где всё".
Цитировать
Т.е. ты модифицировал одну функцию, не ломая при этом всю остальную программу.
Вот в этом-то и была моя основная проблема: я не мог модифицировать функцию, не зная, где она, какая она, что делает и чего требует. Модифицировать заочно, что ли?
Цитировать
Главное, чтобы вызывающий код понимал, что твой класс нуна загрузить в определённом месте в определённое время.
Но сначала ты сам должен разобраться, что это за место и время. И уже потом пытаться вдолбить свое понимание программе через какое-то (на этот раз известное) место.
Дайте глазам отдохнуть! Тёмное место

Оффлайн HellRaiser

  • Тень
  • Сообщений: 5017
Скрипты для NewDark
« Ответ #17 : 27 Марта 2017 15:28:28 »
А то, что предлагает Чужой (переписать дождь на Белке) - это и есть "переписать огромнейший класс", когда можно просто взять и поменять параметр в графе "вид осадков". Да?
Это уже по-другому называется. Есть ли смысл в данном переезде - я не могу сказать. Будет ли удобнее или легче с точки зрения программирования модифицировать и расширять твой код - тем более не могу.

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

Вот в этом-то и была моя основная проблема: я не мог модифицировать функцию, не зная, где она, какая она, что делает и чего требует. Модифицировать заочно, что ли?
Ну ты же по коду мог определить, что делает именно эта программа? Или не очень?

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

Оффлайн Zontik

  • Фантом
  • Сообщений: 17087
    • Тёмное место
Скрипты для NewDark
« Ответ #18 : 27 Марта 2017 16:02:01 »
Вообще я этот дождь привел просто как пример, мне вовсе не нужно включать его по триггеру. И никто код дождя не переписывал (кроме kdau).
Цитировать
Ну ты же по коду мог определить, что делает именно эта программа? Или не очень?
Что делает - я знал и без кода. А вот код заставил меня в этом сомневаться. Нигде не было явных указаний на то, что именно она делает.
Скажу больше: после С у меня вообще было ощущение, будто это не код, а что-то совсем другое. Какая-то декларация о намерениях, а не список конкретных действий.
Смешно звучит, да?
Я лучше оставлю ООП в покое. Подход в решении прикладных задач в редакторе в принципе сходен: ты знаешь время и место, может, приблизительные, а может даже, точнее некуда. Осталось только перевести то, что ты знаешь, в язык понятных редактору действий. Вот тут большинство начинающих авторов и ломается, потому что, во-первых, фиг поймешь, какие действия он умеет выполнять, а во-вторых, образ, которым он действует - это тоже своего рода иностранный язык.
Например, мне нужно, чтобы действие С выполнялось при условии, что выполнены действия А и В, причем не просто выполнены, а не были отменены после выполнения (уже нюанс, который с легкостью игнорируется начинающими). Я знаю, как перевести это условие на язык редактора, мне известны средства, которыми можно этого достичь. Я более-менее представляю, как сделать это на языке высокого уровня, проблема может быть разве что в синтаксисе. Как сделать то же самое на языке ООП - не имею ни малейшего представления, и если даже на самом деле это проще, то для меня - сложнее.
Дайте глазам отдохнуть! Тёмное место

Оффлайн nemyax

  • Тень
  • Сообщений: 5267
  • Нёмыч
Скрипты для NewDark
« Ответ #19 : 27 Марта 2017 16:21:47 »
Как сделать то же самое на языке ООП - не имею ни малейшего представления
Хранишь в своём экземпляре объекта некое состояние. А для удобства перещёлкивания состояния предусматриваешь объекту соответствующие методы. Не?
Желаю тебе из тысячи рулеток одну — самую русскую!
Желаю тебе из тысячи надежд одну — самую крупскую!

Оффлайн HellRaiser

  • Тень
  • Сообщений: 5017
Скрипты для NewDark
« Ответ #20 : 27 Марта 2017 16:22:36 »
Пробовал как-то разобраться в тексте простенькой программы на С++.
Дай поглядеть. Малобукафф вовсе не говорит о простоте (ровно как и наоброт).

Цитировать
Смешно звучит, да?
Вовсе нет. Это могла быть совсем и не ООП-программа (в том смысле в котором мы сейчас обсуждаем), а просто с событийно-ориентированной архитектурой (типа QT или MFC). Я тоже считаю, что ООП не нужно для решения простых задач. Более того, в скриптах вообще (кроме расширения функционала) не вижу в этом смысла. Да и в больших самостоятельных программах оно не всегда нужно в общем-то. Но ведь могут нарисоваться такие задачи, где без него ну совсем никак не обойтись. Поэтому, лучше наверное с ним, чем без него.

Оффлайн Zontik

  • Фантом
  • Сообщений: 17087
    • Тёмное место
Скрипты для NewDark
« Ответ #21 : 27 Марта 2017 16:49:01 »
Цитировать
Хранишь в своём экземпляре объекта некое состояние. А для удобства перещёлкивания состояния предусматриваешь объекту соответствующие методы. Не?
Верю, что именно так это выглядит, когда разбираешься в вопросе. Но не очень-то похоже на набор четких инструкций, правда?
HellRaiser
Тот поезд давно ушел, я даже не помню с какого вокзала. Помню только свое разочарование при виде последнего вагона.
Программа была тестовой из пакета С++ for Windows. И она не была бы простой, если бы ее писали просто на С, уж настолько-то я теперь просветился.
Дайте глазам отдохнуть! Тёмное место

Оффлайн HellRaiser

  • Тень
  • Сообщений: 5017
Скрипты для NewDark
« Ответ #22 : 27 Марта 2017 17:10:05 »
Цитировать
Хранишь в своём экземпляре объекта некое состояние. А для удобства перещёлкивания состояния предусматриваешь объекту соответствующие методы. Не?
Но не очень-то похоже на набор четких инструкций, правда?
Очень даже похоже. Представь себе конечный автомат с состояниями, может такая аналогия покажет тебе эту медаль с немного другой стороны?

И она не была бы простой, если бы ее писали просто на С, уж настолько-то я теперь просветился.
Первое правило во всех книжках по программизму: не усложнять без определённой надобности. Если бы она была просто на С, то вполне вероятно, что объём кода был бы чуть больше, а быстродействие чуть выше. Но вовсе не обязательно, что она стала бы сложнее. Всё-таки очень хотелось бы узнать, о чём собсна речь.

Если конкретно про винду, то там есть 2 подхода (и в обоих можно комбинировать и С и С++ в любых пропорциях).
1) Приложение строится по-старинке: есть точка входа WinMain и куча оконных клоассов со своими обработчиками а-ля wndProc, в каждом таком обработчике необходимо на каждый чих писать много кода (рисовалку, реакцию на пункт меню, мыши, клавы, юзер, системные сообщения и т.д.) Особенно канонiчно в таком коде использовать нативное виндовое АПИ, где не всегда понятные названия функций сочетаются с просто ниэпически большим количеством параметров, каждый второй из которых - некая структура, которая ещё и инициализируется другой вспомогательной функцией, принимающей в качестве параметров прочую неведомую херню.

2) событийно-ориентированный код (как я говорил выше - QT или MFC), где весь вышеописанный ужос спрятан в красивые классы (много классов, ОЧЕНЬ МНОГО КЛАССОВ) с удобными обработчиками и позволяющий довольно быстро и относительно удобно разрабатывать оконные приложения. Имеются обёрточки для контейнеров данных, файлов, тредов и прочие полезности. Но точку входа найти намного труднее, т.к. весь код представляет собой некий набор непонятных классов (самый простой пример - от 2 до 6), написанных заранее умными дядями,  производных от базовых классов, отвечающих за вид, диалог, документ...  У MFC и QT тоже есть свои подводные ками, и не мало, но я думаю, что об этом никто не захочет тут поговорить :)

ЗЫ: QT - кроссплатформенный фреймворк, поэтому под линукс и программу портировать не придётся (почти).
ЗЗЫ: есть и другие фреймворки, можно их комбинировать, отчего на плечи ложится более тяжкий груз ответственности, больше прыжков с бубном, чтобы подружить ужа с ежом, но как итог - меньший объём (дааааалеко не всегда) конкретно твоего проекта.
« Последнее редактирование: 27 Марта 2017 17:12:03 от HellRaiser »

Оффлайн nemyax

  • Тень
  • Сообщений: 5267
  • Нёмыч
Скрипты для NewDark
« Ответ #23 : 27 Марта 2017 17:48:40 »
не очень-то похоже на набор четких инструкций, правда?
Ну вот псевдоговнокод, если хочешь:
class MyStateTracker
{
  bool stage_a_complete = false;
  bool stage_b_complete = false;
public:
  bool to_thy_work()
  {
    if (stage_a_complete && stage_b_complete)
    {
      // do something clever
      return true;
    }
    return false;
  }
  void check_a(MyState st_a)
  {
    int some_a_metric;
    // analyse state and assign to some_a_metric
    if (some_a_metric >= NICE_A) { stage_a_complete = true; }
  }
  void check_b(MyState st_b)
  {
    int some_b_metric;
    // analyse state and assign to some_b_metric
    if (some_b_metric >= NICE_B) { stage_b_complete = true; }
  }
};
Почти не отличается от подобного же на си, если присмотреться. Тут как раз настоящее ООП и не требуется, как подметил HellRaiser.
Желаю тебе из тысячи рулеток одну — самую русскую!
Желаю тебе из тысячи надежд одну — самую крупскую!

Оффлайн Chuzhoi

  • Призрак
  • Сообщений: 10546
Скрипты для NewDark
« Ответ #24 : 27 Марта 2017 17:57:57 »
Я когда-то давно изучал С, но это было еще до появления С++.
Ну вот, ты, оказывается, уже почти все знаешь! А я-то думал, что ты вообще ни бум-бум... А еще говорил про околонулевой уровень.  А больше ты ничего не изучал? Может, Паскаль или Бейсик? Или вот Фортран в свое время в технических вузах преподавали...

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

(А если серьезно, то я не совсем понимаю, зачем все так усложнять, давать примеры на C++, что-то писать про Qt и проч., и проч. Мы же не собираемся из Зонтика сделать заправского программиста, а просто пытаемся объяснить ему, что такое Squirrel, который на порядок проще, чем C++ - например, там даже приватности членов нет).

А класс - это всего лишь описание объекта. И все. Это примерно как архетип в Thief - вот есть архетип (который еще не объект, а просто его схема), а есть объект, который на основе этого архетипа создан. Или несколько однотипных объектов.

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

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

Еще немного терминологии:
- объект, созданный на основе класса, называется экземпляром класса;
- свойства и методы класса называются членами класса.

Собственно, я думаю, это вся теория, которую тебе нужно для начала знать.

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

-- Это скрипт на Lua

local frobCount = 0

function FrobWorldBegin(msg)
frobCount = frobCount + 1
lgs.DarkUISrv.TextMessage("Object id=" .. msg.to .. " frobbed " .. frobCount .. " times")
return true
end

// Это скрипт на Squirrel

class FrobCount extends SqRootScript
{
frobCount = 0;

function OnFrobWorldBegin()
{
frobCount++;
DarkUI.TextMessage("Object id=" + message().to + " frobbed " + frobCount + " times");
return true;
}
}

Как видишь, не все так страшно. Добавлена строчка спереди и фигурные скобки. Все остальное аналогично.
« Последнее редактирование: 27 Марта 2017 18:01:52 от Chuzhoi »

Оффлайн HellRaiser

  • Тень
  • Сообщений: 5017
Скрипты для NewDark
« Ответ #25 : 27 Марта 2017 17:58:54 »
Обожаю nemyax'а: всё по делу, разжёвано до текста, понятного даже школьнику, без лишних простыней, коротко и сжато (снимаю шляпу пойду спилю рога).

А если серьезно, то я не совсем понимаю, зачем все так усложнять, давать примеры на C++, что-то писать про Qt и проч., и проч.
Да, чёт поток сознания понёс во все тяжкие :embarassed:
Много лишнего текста у меня там, но зато Зонтику нечего бояться, ведь мы почти все скримеры ему уже показали  ;D

Мы же не собираемся из Зонтика сделать заправского программиста, а просто пытаемся объяснить ему, что такое Squirrel
.... А вдруг ВНЕЗАПНО <...> ?  :eek: 
« Последнее редактирование: 27 Марта 2017 18:06:21 от HellRaiser »

Оффлайн nemyax

  • Тень
  • Сообщений: 5267
  • Нёмыч
Скрипты для NewDark
« Ответ #26 : 27 Марта 2017 23:38:04 »
Zontik
А вот ровно те же яйца вообще без плюсов:
#include <stdio.h>
#include <stdbool.h>
#define Call(s_ptr, method, ...) s_ptr->method(s_ptr, ##__VA_ARGS__)
#define Nice_a 42
#define Nice_b 0

struct state
{
  int a;
  int b;
};

struct state_tracker
{
  struct state *my_state;
  _Bool stage_a_complete;
  _Bool stage_b_complete;
  void (*check_a)(struct state_tracker *st);
  void (*check_b)(struct state_tracker *st);
  _Bool (*go)(struct state_tracker *st);
};

struct state g_state = {.a=10, .b=20};

void a(struct state_tracker *st)
{
  if(st->my_state->a >= Nice_a) {st->stage_a_complete = true;}
}

void b(struct state_tracker *st)
{
  if(st->my_state->b <= Nice_b) {st->stage_b_complete = true;}
}

_Bool go(struct state_tracker *st)
{
  if(st->stage_a_complete && st->stage_b_complete)
  {
    printf("zngrmmm!\n");
    return 1;
  }
  printf("scht...\n");
  return 0;
}

int main()
{
  struct state_tracker st =
    {.my_state=&g_state
    ,.stage_a_complete=0
    ,.stage_b_complete=0
    ,.check_a=a
    ,.check_b=b
    ,.go=go};
  struct state_tracker *pst = &st;
  printf("a is %i, b is %i\n", g_state.a, g_state.b);
  Call(pst, check_a);
  Call(pst, check_b);
  Call(pst, go);
  g_state.a = 50;
  g_state.b = 0;
  printf("a is %i, b is %i\n", g_state.a, g_state.b);
  Call(pst, check_a);
  Call(pst, check_b);
  Call(pst, go);
  return 0;
}


Тоже коробочка с состоянием, которое можно читать и менять по необходимости, и с методами (функциями-членами).
Единственная странный закидон здесь — способ вызова методов. В сях всё равно не получится как в плюсах (obj.method() или obj_ptr->method()), поэтому нечего и пытаться имитировать плюсовый синтаксис. По-любому придётся указывать коробочку в аргументах функции.
Но можно чуть-чуть автоматизировать через самодельный синтаксис, если:
  • Заключить с самим собой соглашение, что коробочка в членах-функциях всегда пойдёт первым аргументом.
  • Склонить препроцессор к развратным действиям (макрос Call; ахтунг, две решётки непереносимы и работают только в GCC).
Убедиться, что оно работает, можно тут: http://www.tutorialspoint.com/compile_c_online.php
Желаю тебе из тысячи рулеток одну — самую русскую!
Желаю тебе из тысячи надежд одну — самую крупскую!

Оффлайн HellRaiser

  • Тень
  • Сообщений: 5017
Скрипты для NewDark
« Ответ #27 : 28 Марта 2017 09:36:30 »
В сях всё равно не получится как в плюсах (obj.method() или obj_ptr->method()), поэтому нечего и пытаться имитировать плюсовый синтаксис. По-любому придётся указывать коробочку в аргументах функции.
Ну это уже крайности, я бы сказал. За последние лет 20, любой разработанный сишный компилер в той или иной степени - немного плюсовый, даже для каких-нить экзотических железок. Поэтому методы вызывать можно. А чисто сишный режим нынче нафих никому не спёрся (кроме случаев, когда нужна поддержка слишком древнего кода на не менее древнем железе). И зачем оно надо так надо над собой издеваться, ведь тот же былинный BC31 уже умел всё красиво? Чистый си был давно и стал легендой, а задачи в те времена были намного проще и не требовали (не так часто как сейчас) таких хитро###сложенных конструкций :)

Оффлайн Zontik

  • Фантом
  • Сообщений: 17087
    • Тёмное место
Скрипты для NewDark
« Ответ #28 : 28 Марта 2017 09:56:50 »
nemyax, действительно, почти все понятно.
Chuzhoi, "изучал" на значит "изучил". А первым действительно был Фортран. Бейсик... это так, факультативно, для себя и совсем чуть-чуть.
Поэтому, раз уж мы докатились до примеров, задам глупые вопросы.
1. Что означает return true? Что все прошло успешно? А если нет (как такое вообще возможно?), тогда return что?
2. В результате получаем ту же переменную, но с измененным значением (FrobCount), которая выводится на экран. Это понятно. А вот сама переменная определяется где? Она уже существует в движке, даже если не писать никакой скрипт, и ее название определено "где-то там"? То есть нужен справочник подобных названий?
3. message().to - вот это вообще непонятно. По идее, эта функция, которая должна хранить внутри номер объекта. Но в Lua она называется msg.to. То есть, получается, у самого движка такой функции нет? Дальше: вот эти скобки в первом варианте - они-то что означают?
4. FrobWorldBegin - хоть что-то знакомое. Это передающийся сигнал. Но в двух версиях он почему-то называется по-разному. С какой стати?
Ну и в заключение. Коней на переправе не меняют, так что до завершения кампании я объявляю мораторий на белку. После с удовольствием возьмусь за освоение (по мере необходимости), но сейчас это будет равнозначно попытке усидеть на двух стульях. Поэтому отложим более детальный разговор (как в примере nemyax) до лучших времен.
Дайте глазам отдохнуть! Тёмное место

Оффлайн nemyax

  • Тень
  • Сообщений: 5267
  • Нёмыч
Скрипты для NewDark
« Ответ #29 : 28 Марта 2017 11:12:56 »
message().to - вот это вообще непонятно. По идее, эта функция, которая должна хранить внутри номер объекта.
Здесь вызов функции и доступ к свойству возвращённого значения.
Функция сама по себе ничего не "хранит". Ну то есть отдельные вызовы функции, конечно, хранятся на стеке до ретурна, каждый вызов со своими независимыми данными. Но на стек обычно лазиет только дебаггер. В программе/скрипте ты туда не заглядываешь.
Желаю тебе из тысячи рулеток одну — самую русскую!
Желаю тебе из тысячи надежд одну — самую крупскую!