Задать вопрос
  • Как при word wrap узнать количество строк на которое разбит текст?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Для рендеринга исходная строка не меняется. У текста есть cachedTextGenerator, из которого можно достать lineCount.

    UPD: Для GUI.Label количество строк можно вычислисть с помощью GUIStyle.CalcHeight и GUIStyle.lineHeight.
    Ответ написан
  • Почему не работает триггер?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    На этой странице есть матрица взаимодействий коллайдеров. В частности триггеры взаимодействуют только с Rigidbody.
    Ответ написан
    Комментировать
  • Как генерировать нормальные реки при помощи Perlin noise?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Популярный способ - это построить карту высот, выбрать одну из точек в горах в качестве источника, а потом с помощью поиска пути спуститься по карте высот вниз одновременно рисуя путь реки.
    www-cs-students.stanford.edu/~amitp/game-programmi...
    Ответ написан
    3 комментария
  • Как навсегда добавить плагины в unity3d?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Никак. Есть кнопка Add Asset Package при создании проекта, но никаких шаблонов для проектов нет.
    Gsn7t
    Ответ написан
    Комментировать
  • Отслеживание текущей анимации?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Есть три варианта:
    1. Создать в нужном кадре анимации событие и подцепить туда метод. Корявый и неудобный метод, но как вариант, если анимация одна и меняться не будет. В последних версиях юнити их можно даже добавлять из кода, но я бы всё равно не советовал запускать методы через строки.

    2. Внутри OnAnimatorMove дёргать Animator.GetCurrentAnimatorStateInfo. Не супер надёжный метод при низком фпс, короткие переходы можно проскочить, но в остальном вполне нормальный и подходит для синхронизации чего-либо со временем анимации.

    3. Использовать StateMachineBehaviour. Самый универсальный и надёжный способ, при желании классы можно проинициализировать с помощью Animator.GetBehaviour. Требует ручного навешивания скриптов на узлы графа, но это можно легко автоматизировать при желании.

    Не стоит также забывать про компонент Animation. В последних версиях его плохо поддерживают, но, по крайней мере, текущая анимация определяется тривиально.
    Ответ написан
    Комментировать
  • Как организовать движение персонажа опираясь на входящий Vector3?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Для таких целей подойдёт Transform.TransformDirection. Джойстик даёт вам направление в локальных координатах персонажа, а менять вы хотите мировые координаты. Берёте вектор из джойстика, преобразовываете с помощью TransformDirection, а потом используете новый вектор как раньше.
    Ответ написан
    Комментировать
  • Kак решить проблему со спавном в unity3d?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Вы же при вызове Instantiate указываете положение и вращение, конечно у вас вращение из префаба игнорируется. Либо вынесите в инспектор ObjectSpawner параметр доворота и используйте его при инстанциировании, либо перепишите генератор так, чтобы он сразу спавнил домики по обеим сторонам улицы.
    Ответ написан
    Комментировать
  • Где можно посмотреть правильный код(C#) Unity3d от опытных разработчиков?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    В новых официальных туториалах код хороший. На Catlike Coding код тоже неплохой, многие полезные практики обстоятельно расписаны. Кроме туториалов можете поковырять большие популярные плагины, например UniRx, в том же Asset Store полно бесплатных плагинов от маститых разработчиков, качайте да читайте.
    Ответ написан
    Комментировать
  • Можно ли в Unity выполнить скрипт, записанный в строке?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Вариантов несколько:

    Первый способ доступен далеко не на всех платформах, второй потенциально круче, но требует интеграции, третий, пожалуй, проще всех, но требует знания и поддержки ещё одного языка.
    Ответ написан
    Комментировать
  • Как реализовать в инспекторе подобный регулятор?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    В API такого слайдера нету, но до него можно доковыряться декомпилятором:
    UnityEditor.ShadowCascadeSplitGUI
    namespace UnityEditor
    {
        using System;
        using System.Linq;
        using UnityEngine;
    
        internal static class ShadowCascadeSplitGUI
        {
            private static readonly Color[] kCascadeColors = new Color[] { new Color(0.5f, 0.5f, 0.6f, 1f), new Color(0.5f, 0.6f, 0.5f, 1f), new Color(0.6f, 0.6f, 0.5f, 1f), new Color(0.6f, 0.5f, 0.5f, 1f) };
            private const int kPartitionHandleExtraHitAreaWidth = 2;
            private const int kPartitionHandleWidth = 2;
            private const int kSliderbarBottomMargin = 2;
            private const int kSliderbarHeight = 0x18;
            private const int kSliderbarTopMargin = 2;
            private static readonly GUIStyle s_CascadeSliderBG = "LODSliderRange";
            private static readonly int s_CascadeSliderId;
            private static DragCache s_DragCache;
            private static DrawCameraMode s_OldSceneDrawMode;
            private static bool s_OldSceneLightingMode;
            private static SceneView s_RestoreSceneView;
            private static readonly GUIStyle s_TextCenteredStyle;
    
            static ShadowCascadeSplitGUI()
            {
                GUIStyle style = new GUIStyle(EditorStyles.whiteMiniLabel) {
                    alignment = TextAnchor.MiddleCenter
                };
                s_TextCenteredStyle = style;
                s_CascadeSliderId = "s_CascadeSliderId".GetHashCode();
                s_OldSceneDrawMode = DrawCameraMode.Textured;
            }
    
            public static void HandleCascadeSliderGUI(ref float[] normalizedCascadePartitions)
            {
                GUILayout.Label("Cascade splits", new GUILayoutOption[0]);
                GUILayoutOption[] options = new GUILayoutOption[] { GUILayout.Height(28f), GUILayout.ExpandWidth(true) };
                Rect position = GUILayoutUtility.GetRect(GUIContent.none, s_CascadeSliderBG, options);
                GUI.Box(position, GUIContent.none);
                float x = position.x;
                float y = position.y + 2f;
                float num3 = position.width - (normalizedCascadePartitions.Length * 2);
                Color color = GUI.color;
                Color backgroundColor = GUI.backgroundColor;
                int index = -1;
                float[] destinationArray = new float[normalizedCascadePartitions.Length + 1];
                Array.Copy(normalizedCascadePartitions, destinationArray, normalizedCascadePartitions.Length);
                destinationArray[destinationArray.Length - 1] = 1f - normalizedCascadePartitions.Sum();
                int controlID = GUIUtility.GetControlID(s_CascadeSliderId, FocusType.Passive);
                Event current = Event.current;
                int activePartition = -1;
                for (int i = 0; i < destinationArray.Length; i++)
                {
                    float num8 = destinationArray[i];
                    index = (index + 1) % kCascadeColors.Length;
                    GUI.backgroundColor = kCascadeColors[index];
                    float width = num3 * num8;
                    Rect rect2 = new Rect(x, y, width, 24f);
                    GUI.Box(rect2, GUIContent.none, s_CascadeSliderBG);
                    x += width;
                    GUI.color = Color.white;
                    Rect rect3 = rect2;
                    string t = string.Format("{0}\n{1:F1}%", i, num8 * 100f);
                    GUI.Label(rect3, GUIContent.Temp(t, t), s_TextCenteredStyle);
                    if (i == (destinationArray.Length - 1))
                    {
                        break;
                    }
                    GUI.backgroundColor = Color.black;
                    Rect rect4 = rect2;
                    rect4.x = x;
                    rect4.width = 2f;
                    GUI.Box(rect4, GUIContent.none, s_CascadeSliderBG);
                    Rect rect5 = rect4;
                    rect5.xMin -= 2f;
                    rect5.xMax += 2f;
                    if (rect5.Contains(current.mousePosition))
                    {
                        activePartition = i;
                    }
                    if (s_DragCache == null)
                    {
                        EditorGUIUtility.AddCursorRect(rect5, MouseCursor.ResizeHorizontal, controlID);
                    }
                    x += 2f;
                }
                GUI.color = color;
                GUI.backgroundColor = backgroundColor;
                EventType typeForControl = current.GetTypeForControl(controlID);
                if (typeForControl == EventType.MouseDown)
                {
                    if (activePartition >= 0)
                    {
                        s_DragCache = new DragCache(activePartition, normalizedCascadePartitions[activePartition], current.mousePosition);
                        if (GUIUtility.hotControl == 0)
                        {
                            GUIUtility.hotControl = controlID;
                        }
                        current.Use();
                        if (s_RestoreSceneView == null)
                        {
                            s_RestoreSceneView = SceneView.lastActiveSceneView;
                            if (s_RestoreSceneView != null)
                            {
                                s_OldSceneDrawMode = s_RestoreSceneView.renderMode;
                                s_OldSceneLightingMode = s_RestoreSceneView.m_SceneLighting;
                                s_RestoreSceneView.renderMode = DrawCameraMode.ShadowCascades;
                            }
                        }
                    }
                }
                else if (typeForControl == EventType.MouseUp)
                {
                    if (GUIUtility.hotControl == controlID)
                    {
                        GUIUtility.hotControl = 0;
                        current.Use();
                    }
                    s_DragCache = null;
                    if (s_RestoreSceneView != null)
                    {
                        s_RestoreSceneView.renderMode = s_OldSceneDrawMode;
                        s_RestoreSceneView.m_SceneLighting = s_OldSceneLightingMode;
                        s_RestoreSceneView = null;
                    }
                }
                else if ((typeForControl == EventType.MouseDrag) && (GUIUtility.hotControl == controlID))
                {
                    Vector2 vector = current.mousePosition - s_DragCache.m_LastCachedMousePosition;
                    float num10 = vector.x / num3;
                    bool flag = (destinationArray[s_DragCache.m_ActivePartition] + num10) > 0f;
                    bool flag2 = (destinationArray[s_DragCache.m_ActivePartition + 1] - num10) > 0f;
                    if (flag && flag2)
                    {
                        s_DragCache.m_NormalizedPartitionSize += num10;
                        normalizedCascadePartitions[s_DragCache.m_ActivePartition] = s_DragCache.m_NormalizedPartitionSize;
                        if (s_DragCache.m_ActivePartition < (normalizedCascadePartitions.Length - 1))
                        {
                            normalizedCascadePartitions[s_DragCache.m_ActivePartition + 1] -= num10;
                        }
                        GUI.changed = true;
                    }
                    s_DragCache.m_LastCachedMousePosition = current.mousePosition;
                    current.Use();
                }
            }
    
            private class DragCache
            {
                public int m_ActivePartition;
                public Vector2 m_LastCachedMousePosition;
                public float m_NormalizedPartitionSize;
    
                public DragCache(int activePartition, float normalizedPartitionSize, Vector2 currentMousePos)
                {
                    this.m_ActivePartition = activePartition;
                    this.m_NormalizedPartitionSize = normalizedPartitionSize;
                    this.m_LastCachedMousePosition = currentMousePos;
                }
            }
        }
    }


    Как можно видеть из кода выше, используется обычный GUI.Box в комбинации с Event. Чтобы получить нечто похожее, придётся проделать много ручной работы, я бы на вашем месте не стал возиться, пока совсем не припрёт. Можно ещё попробовать через рефлексию попользовать ShadowCascadeSplitGUI, но не факт, что заведётся.

    Вызывается оно из QualitySettingsEditor следующим образом:
    private void DrawCascadeSplitGUI<T>(ref SerializedProperty shadowCascadeSplit)
    {
        float[] normalizedCascadePartitions = null;
        Type type = typeof(T);
        if (type == typeof(float))
        {
            normalizedCascadePartitions = new float[] { shadowCascadeSplit.floatValue };
        }
        else if (type == typeof(Vector3))
        {
            Vector3 vector = shadowCascadeSplit.vector3Value;
            normalizedCascadePartitions = new float[] { Mathf.Clamp(vector[0], 0f, 1f), Mathf.Clamp((float)(vector[1] - vector[0]), (float)0f, (float)1f), Mathf.Clamp((float)(vector[2] - vector[1]), (float)0f, (float)1f) };
        }
        if (normalizedCascadePartitions != null)
        {
            EditorGUI.BeginChangeCheck();
            // ↓↓↓↓
            ShadowCascadeSplitGUI.HandleCascadeSliderGUI(ref normalizedCascadePartitions);
            // ↑↑↑↑
            if (EditorGUI.EndChangeCheck())
            {
                if (type == typeof(float))
                {
                    shadowCascadeSplit.floatValue = normalizedCascadePartitions[0];
                }
                else
                {
                    Vector3 vector2 = new Vector3();
                    vector2[0] = normalizedCascadePartitions[0];
                    vector2[1] = vector2[0] + normalizedCascadePartitions[1];
                    vector2[2] = vector2[1] + normalizedCascadePartitions[2];
                    shadowCascadeSplit.vector3Value = vector2;
                }
            }
        }
    }

    Заодно декомпильнул редактор компонента LODGroup, где есть похожий слайдер, но там код гораздо запутаннее.
    UnityEditor.LODGroupEditor
    UnityEditor.LODGroupGUI
    Ответ написан
  • Как написать условие для Vector3?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    В том же месте, где запускаете изменение скейла, меняйте целевое значение размера камеры.
    public class Eat : MonoBehaviour
    {
        public string Tag;
        public float Increase;
        public Camera camerago;
    
        private Vector3 newScale = Vector3.one;
        private float newZoom = 5;
    
        private void Update()
        {
            transform.localScale = Vector3.Lerp(transform.localScale, newScale, Time.deltaTime*2);
            camerago.orthographicSize = Mathf.Lerp(camerago.orthographicSize, newZoom, Time.deltaTime);
        }
    
        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject.CompareTag(Tag))
            {
                newScale = transform.localScale + new Vector3(Increase, Increase, Increase);
                newZoom = Increase;
    
                Destroy(other.gameObject);
            }
        }
    }

    А вообще да, вам правильно советуют, поделайте туториалы, много вопросов сразу отпадёт.
    С чего начать изучение Unity3D?
    https://docs.unity3d.com/ScriptReference/Mathf.Ler...
    https://docs.unity3d.com/ScriptReference/Component...
    Ответ написан
    Комментировать
  • Как связываются ресурсы с кодом?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    А вот зря отказываетесь от изучения движков, на живом примере проще понять внутреннее устройство.

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

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

    Импорт ресурсов, сериализация настроек, разработка и поддержка редактора - это всё задачи нетривиальные и трудоёмкие, поэтому в наше время никто больше не парится с разработкой своего движка.
    Ответ написан
    2 комментария
  • Можно ли создать мобильное приложение на юнити?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Можно, но это будет не очень удобно, если вы захотите как-то общаться с операционной системой, использовать камеру и т. п. Для таких целей лучше взять Xamarin, благо он теперь бесплатный.
    Ответ написан
    Комментировать
  • Как пиарить свои продукты в Steam Greenlight, что бы их приняли пользователи?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Вопреки популярному мифу, просто интересной игры недостаточно, нужно работать с прессой, писать письма журналистам, стримерам и ютуберам. Для начала можете посмотреть доклады по ссылкам ниже.
    Marketing Indie Games on a $0 Budget
    Making Your First Game: Launching! - How to Market...
    Unite 2013 - Marketing Sucks
    Ответ написан
    Комментировать
  • Что делать, если не запускается Unity3d?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    У вас точно последняя версия? Во всех последних версиях есть кнопка "work offline" рядом с "sign in" как раз для таких случаев. Для работы в редакторе регистрация не обязательна.
    Ответ написан
  • Почему в unity3d при билде текстуры весят 8-10мб?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Реальный размер картинки 2 мегапикселя помножить на 24 бита, либо на 32 бита если есть альфа-канал, то есть шесть-восемь мегабайт. Даже если у вас гифка 2048x2048 с белым квадратом, которая весит четыре килобайта, юнити всё равно возьмёт чистую информацию о цвете в каждом пикселе и внутри себя будет хранить bmp на 12 мегабайт. Но при сборке юнити посмотрит на настройки импорта текстуры и переконвертирует её в указанный формат. Если хотите минимальный размер сборки, то либо жмите картинки, либо качайте их после установки, либо используйте векторную графику с плагином.
    Ответ написан
    Комментировать
  • Как изменить позицию обьекта в unity3d?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Если судить по второму скриншоту, у вас неправильно экспортирована модель. Правильные настройки у каждого тридэшного редактора свои, но они легко гуглятся, частично может помочь мануал. Если моделька не ваша и исходников нет, то можете завернуть её в контейнер, повернуть как нужно, сохранить префаб и пользоваться. Что касаемо "двигаеться криво", без кода сложно что-то посоветовать.
    Ответ написан
    Комментировать
  • Операция, обратная запеканию нормалей?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Напрямую из нормалей геометрию не получить, но это можно сделать из displacement map. Получить displacement map из карты нормалей можно например с помощью этой утилиты. Далее используете Displacement to Polygons в Maya, Displace Mesh в 3ds Max или аналогичную кнопку в своём любимом редакторе.
    Ответ написан
    Комментировать
  • Как сделать реакцию на событие в консоле Unity 3D C#?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    А причём тут консоль? Вам надо не на текст в консоли подписываться, а напрямую в место вызова события. Как конкретно это сделать зависит от используемого фреймворка. Начать можете с чтения MSDN и официального туториала.
    Ответ написан
    Комментировать
  • Кто занимается разработкой анимаций?

    BasmanovDaniil
    @BasmanovDaniil
    Геймдизайнер-телепат
    Анимации делает аниматор, если его нет, то либо берут готовые анимации, либо ими занимается моделер/художник. Программист такими вещами не должен заниматься, он может вставить анимации в игру, подключить логику запуска определённых анимаций, но создавать анимации точно не его работа. Ближе всего к анимированию, наверное, инверсная кинематика, но она в основном используется для решения примитивных задач.
    Ответ написан
    6 комментариев