Dreaman Dreaman

TheDarkestRed,

Отлично! Всё прям как в проектах AAA-класса ;)

Dreaman Dreaman

В игре "Mental State" на космической станции появилось новое просторное и довольно уютное помещение :)

...
win20082007 win20082007

Наконец то закончил работу над первым боссом, осталось сделать еще 7 :) Если кого заинтересовал проект со странными существами - милости прошу к нашему шалашу https://vk.com/another_arts

TheDarkestRed TheDarkestRed

Concept art нового врага для The Darkest Red 🎮 🧟 👨‍🎨

...
kirsakshlil kirsakshlil

Ох, давненько я не заходил сюда. С Новым Годом всех! Надеюсь в новом году работа попрёт!

Dreaman Dreaman

Jusper,

Спасибо! Приятно слышать :)

id230694559 id230694559

Хорошая подборка книг по разработке игр https://iunity3d.ru/top-luchshih-knig-po-razrabotke-igr/

Jusper Jusper

Dreaman, с наступившим Новым годом! Приятно следить за твоим прогрессом :)

Dreaman Dreaman

Присоединяюсь к поздравлениям! Отдельное спасибо за упоминание :)
Всех поздравляю с уже наступившим Новым Годом!

Jusper Jusper

Taske,

LOD-ы нет вроде, не делали.

Taske Taske

Возможно глупый вопрос, но я пока только вникаю в юнити.

Лоды при этом методе у мешей сохраняются? Какие объекты выгоднее всего таким образом объединять?

Jusper Jusper

И вас с наступающим, друзья.
Успехов!

TheDarkestRed TheDarkestRed

Новый лесной монстр для The Darkest Red 🎮 🧟 👹
https://vk.com/the_darkestred

TheDarkestRed TheDarkestRed

Тестируем новую механику захвата врагов 🎮🧟🤺

Jusper Jusper

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

Ato_Ome Ato_Ome

Скетч-арт от художницы для нашего проекта, упырь.

alexprey alexprey

ehnaton, никто его не обижает, у него просто стиль похожий

Логотип проекта Unity

Сшиватель мешей для Unity

Сшиватель мешей для Unity

Предисловие

В недавнем скриншотнике мы говорили, что работали над оптимизацией. Slash Polygon: Tournament предназначен прежде всего для мобильных платформ, поэтому вопрос производительности для нас почти номер 1.

Mesh Composer

Мы сделали небольшой скрипт, который сшивает меши в один, экономя отрисовку на устройстве. Инструкция использования простая:

Пример структуры объектов — Сшиватель мешей для Unity — Unity — DevTribe: инди-игры, разработка, сообщество (mesh composer, tool, Скрипт, скрипт)
Пример структуры объектов
  • Создайте пустой Game Object
  • В качестве Child к созданному объекту добавьте те модели, которые хотите сшить (для них должен использоваться 1 материал, иначе от сшивание нет смысла)
  • Добавьте компонент на Game Object - Mesh Composer
  • (опционально) Для подчиненных объектов можно включить Mesh Collider
Можно было еще включить нашу отрисовку теней, но она кастомная и вам не понадобится.
Компонент на объекте — Сшиватель мешей для Unity — Unity — DevTribe: инди-игры, разработка, сообщество (mesh composer, tool, Скрипт, скрипт)
Компонент на объекте

Вcпомогательная информация:

  • Total Childs (сколько подчиненных объектов в группе)
  • Total Verts (сколько в итоге вертексов получается в сшитом объекте)
  • Ругань, если подчиненные сшитые объекты имеют разный материал
  • Ругань, если максимальное количество вершин в одном сшитом объекте > 65536

Код

using UnityEngine;

namespace Visual
{
    [RequireComponent(typeof(MeshFilter))]
    [RequireComponent(typeof(MeshRenderer))]
    public class MeshComposer : MonoBehaviour
    {
        public MeshFilter MyFilter;
        public MeshRenderer MyRenderer;
        public MeshFilter[] InternalFilters;
        public Material SharedMaterial;
        [SerializeField] private bool _useMeshCollider;

        private void Awake()
        {
            Compose();
        }

        private void Compose()
        {
            var combineMeshes = new CombineInstance[InternalFilters.Length];
            for (int i = 0; i < combineMeshes.Length; i++)
            {
                combineMeshes[i].mesh = InternalFilters[i].mesh;
                combineMeshes[i].transform = InternalFilters[i].transform.localToWorldMatrix;
                InternalFilters[i].gameObject.SetActive(false);
            }

            MyFilter.mesh = new Mesh();
            MyFilter.mesh.CombineMeshes(combineMeshes, true, true, false);
            MyRenderer.material = SharedMaterial;

            if (_useMeshCollider)
            {
                var collider = gameObject.AddComponent<MeshCollider>();
                collider.sharedMesh = MyFilter.mesh;
            }
        }
    }
}
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace Visual.Editor
{
    [CustomEditor(typeof(MeshComposer))]
    public class MeshComposerInspector : UnityEditor.Editor
    {
        private MeshComposer _instance;

        private void OnEnable()
        {
            _instance = (MeshComposer) target;
        }

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            _instance.MyFilter = _instance.GetComponent<MeshFilter>();
            _instance.MyRenderer = _instance.GetComponent<MeshRenderer>();

            // fetch childs
            _instance.InternalFilters = _instance.GetComponentsInChildren<MeshFilter>()
                .Where(filter => filter != _instance.MyFilter).ToArray();

            EditorUtility.SetDirty(_instance);

            EditorGUILayout.HelpBox($"total childs:{_instance.InternalFilters.Length}", MessageType.Info);

            if (_instance.InternalFilters == null || _instance.InternalFilters.Length == 0)
                return;

            var totalVerts = _instance.InternalFilters.Select(filter => filter.mesh.vertices.Length)
                .Aggregate((total, next) => total += next);

            var messageType = MessageType.Info;
            if (totalVerts > uint.MaxValue)
                messageType = MessageType.Error;

            EditorGUILayout.HelpBox($"total verts: {totalVerts}", messageType);

            var useSameMaterial = true;

            var material = _instance.InternalFilters[0].GetComponent<MeshRenderer>().sharedMaterial;
            foreach (var meshFilter in _instance.InternalFilters)
            {
                var rend = meshFilter.GetComponent<MeshRenderer>();
                if (rend.sharedMaterial != material)
                {
                    useSameMaterial = false;
                    break;
                }
            }

            if (!useSameMaterial)
            {
                EditorGUILayout.HelpBox($"Internal meshses uses different materials, must be the same!",
                    MessageType.Error);
            }
            else
            {
                _instance.SharedMaterial = material;
            }

            EditorUtility.SetDirty(_instance);
        }
    }
}

Скачать Mesh Composer

Два готовых скрипта для использования в Unity.

Результат

В видео показан игровой процесс с устройства. В видео стоит ограничитель на максимальный FPS - 30 кадров. Количество Batches и Tris уменшилось на порядок, игра из конкретно нестабильного куска гов билда превратилась в нормальный рабочий прототип.

Смотрите также:


Комментарии



Возможно глупый вопрос, но я пока только вникаю в юнити.

Лоды при этом методе у мешей сохраняются? Какие объекты выгоднее всего таким образом объединять?

Taske,

Возможно глупый вопрос, но я пока только вникаю в юнити.

Лоды при этом методе у мешей сохраняются? Какие объекты выгоднее всего таким образом объединять?

LOD-ы нет вроде, не делали.
Все существующие меши отключаются на старте, остается только инстансы внутри группировки.

Лучше всего объединять мелкие объекты (мы сшивали лесной участок).

Справка