?

Log in

No account? Create an account
me

August 2011

S M T W T F S
 123456
78910111213
14151617181920
21222324252627
28293031   
Powered by LiveJournal.com
me

Unity3d + внешние Python скрипты

Порой бывает очень удобно использовать внешние скрипты, такие как Lua или Python в своем проекте.
Почему это удобно? И почему это может облегчить жизнь?

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

Вот тут и приходит на помощь скриптовый язык.

В нашем проекте CASTROL RACE такой необходимости не было, но иногда я всё же задумывался применить тут данную технику. Ну а раз мысли такие меня начали посещать, да и появился свободный часик, я залез в интернет на поиски возможных готовых решений (честно говоря я копал в сторону Lua) и натолкнулся на бесплатный extension для Python.

Кому интересно, прошу под кат!


В Unity3d Store я наткнулся на реализацию Iron Python. Несмотря на то, что искал я Lua (с ней уже приходилось работать),
мне стало интересно и я решил попробовать. Тем более, что это абсолютно бесплатно.

2 клика мышью и проект уже открыт:

unity3d python getting started
рис. 1 Проект изначально содержит все что нужно

Всё что требуется, так это настроить проект на использование .NET 2.0 в свойствах плеера. Об этом подробно написано в небольшой документации к экстеншену, так что не буду грузить мозг. Просто заходим в свойства проекта, далее в свойства плеера, там во вкладке other выбираем .NET 2.0 и сохраняем.

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


рис. 2 Окно в демо проекте

Если запустить проект, то нам явится 2 окна:
- Сверху окно соджержит код на питоне, который можно прям тут же изменить
- Снизу окошко в которое выводится результат выполнения этого самого скрипта

Как можно заметить, скрипт, который уже вбит в верхнее окно, делает следующее - создает 5 сфер в сцене, дает им имена согласно номеру, и выводит строку "Created 5 spheres".
Если нажать на кнопку Run, то это и произойдёт! (:

Вобщем, не долго играясь с верхним окном я полез в код.

Вот он инициализирует сам интерпретатор:

using UnityEngine;
using System.Collections;

/// <summary>
/// A simple python behaviour that demonstrates how code can be ran in the runtime engine
/// </summary>
public class InGamePythonInterpreter : MonoBehaviour 
{
    private string m_pyCode;
    private string m_pyOutput;

    private PythonEnvironment m_pyEnv;

    private const string INITIALIZATION_CODE =
    @"
    import clr
    clr.AddReference('UnityEngine')
    import UnityEngine
    ";
    
    
    
    // Use this for initialization
    void Start () 
    {
        m_pyEnv = new PythonEnvironment();
        m_pyEnv.RunCommand(INITIALIZATION_CODE);
        m_pyOutput = string.Empty;
    }

Данные строки
import clr
clr.AddReference('UnityEngine')
import UnityEngine
осуществляют связку среды Python c классами Unity3d. По сути, далее мы можем обращаться к классам Unity3d непосредственно из скриптов. Стоит отметить 2 момента:
- С одной стороны это очень удобно, поскольку не нужно экспортировать описание каждого класса как в Lua, ведь это запарно
- С другой стороны встает вопрос безопасности Ведь можно подсунуть другой скрипт и ничто не помешает ему внедриться в логику программы... Но это вопрос не к относится к сегодняшней теме (:

Итак, по нажатию на кнопку Run в сущности выполняется всего одна команда:
PythonEnvironment.CommandResult result = m_pyEnv.RunCommand(m_pyCode);

RunCommand - гениально (: Эта команда просто выполняет текст скрипта!

Вроде бы как всё очень понятно и ясно.
Не имея четкой цели, я решил вынести скрипт в отдельный файл, далее скачать его во время работы программы и исполнить. Я добавил кнопку и следующий код:
WWW www = new WWW("http://www.yoursite.ru/_unity/script.py");
yield return www;
PythonEnvironment.CommandResult result = m_pyEnv.RunCommand(www.data);
Это сработало на ура! Теперь можно было запускать почти любую логику из этого скрипта, не прибегаю к перекомпиляции проекта. Цель достигнута.

PS: Должен отметить, что тут не рассматривались вопросы безопасности данного подхода
Также данная статья ни в коем случае не претендует на полноту рассмотрения проблемы использования сторонних интерпретируемых скриптов в Unity3d.

PPS: Был сделан небольшой сравнительный анализ производительности выполнения скрипта на Python и встроенного C#, разница приблизительно в 100 раз (медленнее конечно же). Для анализа скрипт в цикле 1000 раз создавал 2 вектора, отнимал из первого второй, и высчитывал магнитуду полученного.
Tags: , ,

Comments