forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Болтовня (http://forum.boolean.name/forumdisplay.php?f=25)
-   -   Альтернатива скриптовику. (http://forum.boolean.name/showthread.php?t=17441)

radiobutton 30.10.2012 07:39

Ответ: Альтернатива скриптовику.
 
Вообщем. Пример для XNA.

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

тут 3 файла.
hero.dll
script1.dll
game.exe

hero.dll это вспомогательная библиотека скомпилированная из кода ниже и содержащая класс персонажа с информацией о нем. Персонаж у нас это 2д квадрат 20 на 20. Информация это его положение и цвет.

Код:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace hero_namespace
{
    public class Hero
    {
        public int x, y, h, w;
        public Color clr;
        Texture2D hz_za4em;
        GraphicsDevice gd;
        SpriteBatch sb;

        public Hero(int x, int y, int h, int w, Color clr, GraphicsDevice gd, SpriteBatch sb)
        {
            this.x = x; this.y = y; this.h = h; this.w = w;
            this.clr = clr;
            this.gd = gd;
            this.sb = sb;
            hz_za4em = new Texture2D(gd, 1, 1);
            hz_za4em.SetData(new Color[] { Color.White });
        }

        public void draw()
        {
            sb.Begin();
            sb.Draw(hz_za4em, new Rectangle(x, y, w, h), clr);
            sb.End();
        }
    }
}



script1.dll это наш скрипт. Он создает одного персонажа и двигает его по окружности с радиусом 80.

Код:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using hero_namespace;

namespace script_namespace
{
    public class script1
    {
        Hero gamer1;
        int gamer1_x, gamer1_y;
        double gamer1_dx, gamer1_dy;
        GraphicsDevice gd;
        SpriteBatch sb;

        public script1(GraphicsDevice gd, SpriteBatch sb)
        {
            gamer1_x = 200; gamer1_y = 200;
            gamer1_dx = 80; gamer1_dy = 80;
            this.gd = gd; this.sb = sb;
            gamer1 = new Hero(gamer1_x, gamer1_y, 20, 20, Color.Blue, gd, sb);
        }

        public void update(GameTime t)
        {
            gamer1.x = gamer1_x + (int)(Math.Cos((t.TotalGameTime.TotalSeconds)) * gamer1_dx);
            gamer1.y = gamer1_y + (int)(Math.Sin((t.TotalGameTime.TotalSeconds)) * gamer1_dy);
        }

        public void draw()
        {
            gamer1.draw();
        }
    }
}



game.exe это наш скриптовый движок, который читает script1.dll с помощью рефлексии и выполняет соответственно.

Код:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Reflection;

namespace test_script
{
    public class Game_test : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        public static SpriteBatch spriteBatch;

        MethodInfo method1, method2;
        object test_obj;

        public Game_test()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {

            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
         
            Assembly assembly = Assembly.LoadFrom(@"script1.dll");
            Type type = assembly.GetType("script_namespace.script1");
            ConstructorInfo[] ci = type.GetConstructors();
            object[] param = { GraphicsDevice, spriteBatch };
            test_obj = ci[0].Invoke(param);
            method1 = type.GetMethod("update");
            method2 = type.GetMethod("draw");
        }

        protected override void UnloadContent()
        {

        }

        protected override void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Escape) == true) this.Exit();

            method1.Invoke(test_obj,new object[]{gameTime});
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            method2.Invoke(test_obj, null);
            base.Draw(gameTime);
        }

    }
}



Компиляция первых двух библиотек производилась с помощью следующего кода:

Код:

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using Microsoft.CSharp;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {

            string s = @"
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace hero_namespace
{
    public class Hero
    {
        public int x, y, h, w;
        public Color clr;
        Texture2D hz_za4em;
        GraphicsDevice gd;
        SpriteBatch sb;

        public Hero(int x, int y, int h, int w, Color clr, GraphicsDevice gd, SpriteBatch sb)
        {
            this.x = x; this.y = y; this.h = h; this.w = w;
            this.clr = clr;
            this.gd = gd;
            this.sb = sb;
            hz_za4em = new Texture2D(gd, 1, 1);
            hz_za4em.SetData(new Color[] { Color.White });
        }

        public void draw()
        {
            sb.Begin();
            sb.Draw(hz_za4em, new Rectangle(x, y, w, h), clr);
            sb.End();
        }
    }
}
";
            Dictionary<string, string> providerOptions = new Dictionary<string, string>
        {
          {"CompilerVersion", "v4.0"}
        };

           
            CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);

            CompilerParameters compilerParams = new CompilerParameters { OutputAssembly = "D:\\hero.dll", GenerateExecutable = false };           
            compilerParams.ReferencedAssemblies.Add(@"C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.dll");
            compilerParams.ReferencedAssemblies.Add(@"C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Graphics.dll");
           
            // Компиляция
            CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, s);

            // Выводим информацию об ошибках
            Console.WriteLine("Number of Errors: {0}", results.Errors.Count);
            foreach (CompilerError err in results.Errors)
            {
                Console.WriteLine("ERROR {0}", err.ErrorText);
            }

            Console.Read();

            s = @"

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using hero_namespace;

namespace script_namespace
{
    public class script1
    {
        Hero gamer1;
        int gamer1_x, gamer1_y;
        double gamer1_dx, gamer1_dy;
        GraphicsDevice gd;
        SpriteBatch sb;

        public script1(GraphicsDevice gd, SpriteBatch sb)
        {
            gamer1_x = 200; gamer1_y = 200;
            gamer1_dx = 80; gamer1_dy = 80;
            this.gd = gd; this.sb = sb;
            gamer1 = new Hero(gamer1_x, gamer1_y, 20, 20, Color.Blue, gd, sb);
        }

        public void update(GameTime t)
        {
            gamer1.x = gamer1_x + (int)(Math.Cos((t.TotalGameTime.TotalSeconds)) * gamer1_dx);
            gamer1.y = gamer1_y + (int)(Math.Sin((t.TotalGameTime.TotalSeconds)) * gamer1_dy);
        }

        public void draw()
        {
            gamer1.draw();
        }
    }
}";                     
            compilerParams = new CompilerParameters { OutputAssembly = "D:\\script1.dll", GenerateExecutable = false };
            compilerParams.ReferencedAssemblies.Add(@"C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Dll");
            compilerParams.ReferencedAssemblies.Add(@"C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Game.dll");
            compilerParams.ReferencedAssemblies.Add(@"C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Graphics.dll");
            compilerParams.ReferencedAssemblies.Add(@"D:\hero.dll");
           
            results = provider.CompileAssemblyFromSource(compilerParams, s);

            Console.WriteLine("Number of Errors: {0}", results.Errors.Count);
            foreach (CompilerError err in results.Errors)
            {
                Console.WriteLine("ERROR {0}", err.ErrorText);
            }

            Console.Read();
            Console.Read();
        }
    }
}



Смысл в том, что скриптовый движок знает только то, что в скрипте есть две функции - draw и update. Соотвественно он их и исполняет. А в них прописано создание объекта персонажа и его движение. Тоесть скрипт.

Возможно из-за криво указанных XNA сборок при компиляции у вас ниче не запустица если их адресс не будит совпадать с моим. =) Без указания полного пути у меня так и не получилось.

Можно компилировать код на c# из текстовых файлов в сборки IL в оперативную память. При запуске игры например. Автоматически.

Randomize 30.10.2012 10:51

Ответ: Альтернатива скриптовику.
 
Раз не подходит LUA, AngleScript, TCL, REBOL то надо писать свой скриптовик под свои нужды.

Gector 30.10.2012 18:39

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от Randomize (Сообщение 241687)
Раз не подходит LUA, AngleScript, TCL, REBOL то надо писать свой скриптовик под свои нужды.

Ты это мне или рабиобаттону?) Если мне то... эм... я на блице работаю пока что... :-D Если на С++ работал вопроса бы небыло).

den 30.10.2012 18:39

Ответ: Альтернатива скриптовику.
 
Цитата:

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

radiobutton 30.10.2012 19:03

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от Den (Сообщение 241736)
збс скрипт
radiobutton, ты с какого раза поймёшь, что суть скрипта в том, чтобы он выполнлся на лету (интерпретировался), а не компилировался.
если ты не понимаешь таких очевидных вещей - мне тебя жаль нет мне похуй

что значит на лету?

у меня как выполняются?

Gector 30.10.2012 19:07

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от radiobutton (Сообщение 241740)
что значит на лету?

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

Скрипт в DLL - это хардкор.

radiobutton 30.10.2012 19:39

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от Gector (Сообщение 241741)
Имеется ввиду возможность быстро изменять их. Есть txt-файл и команды из него выполняются без компиляции и прочих заморочек. Хотя можно переводить его сразу в файл кода виртуальной машины, чтобы лишний раз не парсил и был нарочно неудобен для редактирования третьими лицами. Но это зачастую напрасный геморрой единственная выгода от которого - более быстрая загрузка.

А у меня чем отличается процесс? :)

Вместо блокнота только нужно свое приложение небольшое написать. В котором будит таже кнопка сохранить. И тоже текстовое поле в котором можно будит вводить код. Когда будишь нажимать на сохранить код будит автоматически компилироваться в dll сборку. А можно вообще не компилировать. А хранить в зашифрованном виде c# код. И компилировать уже при запуске игры расшифровывая файлы.

При этом скорость работы кода на IL в разы быстрее чем скорость работы ваших интерпритаторов. :)


Код:

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using Microsoft.CSharp;

namespace ConsoleApplication2
{
    class Program
    {
        static long Millisecs()
        {
            long[] p = new long[12];

            p[0] = 31; p[1] = 28; p[2] = 31; p[3] = 30; p[4] = 31; p[5] = 30;
            p[6] = 31; p[7] = 31; p[8] = 30; p[9] = 31; p[10] = 30; p[11] = 31;
            if ((DateTime.Now.Year - 2012) % 4 == 0)
            {
                p[1] = 29;
            }
            long n = 0;
            for (int i = 0; i < DateTime.Now.Month - 1; i++)
            {
                n += p[i];
            }
            return Convert.ToInt64(((((n + Convert.ToInt64(DateTime.Now.Day)) * 24 + Convert.ToInt64(DateTime.Now.Hour)) * 60 + Convert.ToInt64(DateTime.Now.Minute)) * 60 + Convert.ToInt64(DateTime.Now.Second)) * 1000 + Convert.ToInt64(DateTime.Now.Millisecond));
        }

        static void Main(string[] args)
        {

            long l1 = Millisecs();

            for (int i = 0; i < 1000; i++)
            {
                string s = @"

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using hero_namespace;

namespace script_namespace
{
    public class script1
    {
        Hero gamer1;
        int gamer1_x, gamer1_y;
        double gamer1_dx, gamer1_dy;
        GraphicsDevice gd;
        SpriteBatch sb;

        public script1(GraphicsDevice gd, SpriteBatch sb)
        {
            gamer1_x = 200; gamer1_y = 200;
            gamer1_dx = 80; gamer1_dy = 80;
            this.gd = gd; this.sb = sb;
            gamer1 = new Hero(gamer1_x, gamer1_y, 20, 20, Color.Blue, gd, sb);
        }

        public void update(GameTime t)
        {
            gamer1.x = gamer1_x + (int)(Math.Cos((t.TotalGameTime.TotalSeconds)) * gamer1_dx);
            gamer1.y = gamer1_y + (int)(Math.Sin((t.TotalGameTime.TotalSeconds)) * gamer1_dy);
        }

        public void draw()
        {
            gamer1.draw();
        }
    }
}";


          Dictionary<string, string> providerOptions = new Dictionary<string, string>
        {
          {"CompilerVersion", "v4.0"}
        };

                CompilerParameters compilerParameters = new CompilerParameters();
                compilerParameters.GenerateInMemory = true;
                CompilerResults compileResults = new CSharpCodeProvider(providerOptions)
                    .CompileAssemblyFromSource(compilerParameters, s);
            }


            l1 = Millisecs() - l1;
            Console.WriteLine(l1);
            Console.Read();
        }
    }
}



компиляция 1000 раз моего скрипта из прошлого примера занимает 23456 милисекунд. тоесть 23 милисекунды на скрипт. Имхо большая часть времени уходит на составление метаданных т.к. скрипт маленький.

Теперь подумай сколько времени тебе нужно чтобы дотянуться мышкой в блокноте до кнопки сохранить?

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

Gector 30.10.2012 20:08

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от radiobutton (Сообщение 241745)
А у меня чем отличается процесс? :)

Компиляцией.

IgorOK 30.10.2012 21:59

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от radiobutton (Сообщение 241745)
А у меня чем отличается процесс? :)

Вместо блокнота только нужно свое приложение небольшое написать. В котором будит таже кнопка сохранить. И тоже текстовое поле в котором можно будит вводить код. Когда будишь нажимать на сохранить код будит автоматически компилироваться в dll сборку. А можно вообще не компилировать. А хранить в зашифрованном виде c# код. И компилировать уже при запуске игры расшифровывая файлы.

При этом скорость работы кода на IL в разы быстрее чем скорость работы ваших интерпритаторов. :)


Код:

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using Microsoft.CSharp;

namespace ConsoleApplication2
{
    class Program
    {
        static long Millisecs()
        {
            long[] p = new long[12];

            p[0] = 31; p[1] = 28; p[2] = 31; p[3] = 30; p[4] = 31; p[5] = 30;
            p[6] = 31; p[7] = 31; p[8] = 30; p[9] = 31; p[10] = 30; p[11] = 31;
            if ((DateTime.Now.Year - 2012) % 4 == 0)
            {
                p[1] = 29;
            }
            long n = 0;
            for (int i = 0; i < DateTime.Now.Month - 1; i++)
            {
                n += p[i];
            }
            return Convert.ToInt64(((((n + Convert.ToInt64(DateTime.Now.Day)) * 24 + Convert.ToInt64(DateTime.Now.Hour)) * 60 + Convert.ToInt64(DateTime.Now.Minute)) * 60 + Convert.ToInt64(DateTime.Now.Second)) * 1000 + Convert.ToInt64(DateTime.Now.Millisecond));
        }

        static void Main(string[] args)
        {

            long l1 = Millisecs();

            for (int i = 0; i < 1000; i++)
            {
                string s = @"

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using hero_namespace;

namespace script_namespace
{
    public class script1
    {
        Hero gamer1;
        int gamer1_x, gamer1_y;
        double gamer1_dx, gamer1_dy;
        GraphicsDevice gd;
        SpriteBatch sb;

        public script1(GraphicsDevice gd, SpriteBatch sb)
        {
            gamer1_x = 200; gamer1_y = 200;
            gamer1_dx = 80; gamer1_dy = 80;
            this.gd = gd; this.sb = sb;
            gamer1 = new Hero(gamer1_x, gamer1_y, 20, 20, Color.Blue, gd, sb);
        }

        public void update(GameTime t)
        {
            gamer1.x = gamer1_x + (int)(Math.Cos((t.TotalGameTime.TotalSeconds)) * gamer1_dx);
            gamer1.y = gamer1_y + (int)(Math.Sin((t.TotalGameTime.TotalSeconds)) * gamer1_dy);
        }

        public void draw()
        {
            gamer1.draw();
        }
    }
}";


          Dictionary<string, string> providerOptions = new Dictionary<string, string>
        {
          {"CompilerVersion", "v4.0"}
        };

                CompilerParameters compilerParameters = new CompilerParameters();
                compilerParameters.GenerateInMemory = true;
                CompilerResults compileResults = new CSharpCodeProvider(providerOptions)
                    .CompileAssemblyFromSource(compilerParameters, s);
            }


            l1 = Millisecs() - l1;
            Console.WriteLine(l1);
            Console.Read();
        }
    }
}



компиляция 1000 раз моего скрипта из прошлого примера занимает 23456 милисекунд. тоесть 23 милисекунды на скрипт. Имхо большая часть времени уходит на составление метаданных т.к. скрипт маленький.

Теперь подумай сколько времени тебе нужно чтобы дотянуться мышкой в блокноте до кнопки сохранить?

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

Возьми пожалуйста это:


По существу: Иди проспись.

radiobutton 31.10.2012 01:00

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от IgorOK (Сообщение 241759)
Возьми пожалуйста это:


По существу: Иди проспись.

кончились аргументы - кинь тупую картинку. :)

IgorOK 31.10.2012 12:10

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от radiobutton (Сообщение 241794)
кончились аргументы - кинь тупую картинку. :)

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

Gector 31.10.2012 12:31

Ответ: Альтернатива скриптовику.
 
"Был не прав - притворись, что троллил."

radiobutton. То что ты предложил - по сути лишний геморрой. Проще сразу зашить сценарий в экзешник.

falcon 31.10.2012 16:26

Ответ: Альтернатива скриптовику.
 
Gector, IgorOK
зря вы на radiobutton накинулись.
да, к кому угодно можно испытывать расовую неприязнь и да, есть некоторые тонкости в использовании рефлексии вместо полноценной скрипт системы, но ваши аргументы сами по себе унылы.
какой нафиг блокнот?
нет, серьёзно, скриптовать логику в блокноте?
Это конфиг параметры в блокноте удобно править. А писать какой-никакой код - совсем неудобно.
Иметь какую-либо тривиальную IDE в любом случае надо.
Желательно что бы она была вшита в какой-нибудь редактор.
А раз оно всё равно нужно, то чем плохо написать код и собрать его в длл средствами этой самопальной IDE?
Компиляция? Ну это, господа, совсем не аргумент.
Чем оно вас не устроило? Я понимаю, ничего хорошего в ребилде всего проекта нет (у меня например проекты билдятся от получаса до нескольких часов, правда это C++, Boost и куча куча других штук).
Но уж скомпилять за полсекунды длл-ку со скриптовой логикой вполне себе допустимо.
Аргументы не те.
Придумайте что-нибудь по существенней. :)

Taugeshtu 31.10.2012 17:14

Ответ: Альтернатива скриптовику.
 
Цитата:

Придумайте что-нибудь по существенней.
Скриптовый язык должен быть прост, чтобы левел-дизайнер без особых программерских скиллов мог его использовать (говорим про общий случай, окда?).
Насчёт "писать в блокноте" - согласен, а вот насчёт "чтобы что-то подправить нужна IDE" - нет. Желательно, чтобы скрипты можно было корёжить без IDE, на локальной машине тестера/друга/демонстрационном компьютере в комнате собраний совета директоров. Таскать с собой всегда и везде флешечку с тулчейном - не айс.
Pre-runtime компиляция - ок, но тогда надо изворачиваться, если потребуется runtime-модификация скриптов (а это айс-какая фича, и вообще ускоряет разработку).

Отсюда вывод: в общем случае интерпретация для скриптинга предпочтительнее.

Gector 31.10.2012 17:31

Ответ: Альтернатива скриптовику.
 
Цитата:

Сообщение от falcon (Сообщение 241875)
Gector, IgorOK
зря вы на radiobutton накинулись.
да, к кому угодно можно испытывать расовую неприязнь и да, есть некоторые тонкости в использовании рефлексии вместо полноценной скрипт системы, но ваши аргументы сами по себе унылы.
какой нафиг блокнот?
нет, серьёзно, скриптовать логику в блокноте?
Это конфиг параметры в блокноте удобно править. А писать какой-никакой код - совсем неудобно.
Иметь какую-либо тривиальную IDE в любом случае надо.
Желательно что бы она была вшита в какой-нибудь редактор.
А раз оно всё равно нужно, то чем плохо написать код и собрать его в длл средствами этой самопальной IDE?
Компиляция? Ну это, господа, совсем не аргумент.
Чем оно вас не устроило? Я понимаю, ничего хорошего в ребилде всего проекта нет (у меня например проекты билдятся от получаса до нескольких часов, правда это C++, Boost и куча куча других штук).
Но уж скомпилять за полсекунды длл-ку со скриптовой логикой вполне себе допустимо.
Аргументы не те.
Придумайте что-нибудь по существенней. :)

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


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

vBulletin® Version 3.6.5.
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Перевод: zCarot