Перейти до вмісту
sp1le.tech
EN
Ігровий білд 2026 Прототип кооп-пограбування · Photon Fusion + Voice

Getaway

Це починалося як полігон: я хотів побачити, як Photon Fusion 2 і Photon Voice 2 поводяться в реальній сцені з дверима, охороною і втечею, а не у вендорському демо. Виросло в ігровий соло-режим: зайти в ломбард, розкрити сейф, винести сумки повз охорону й поліцію, поїхати. Рівень, реквізит, персонажі та інтерфейс виробляє код редактора - лише завдяки цьому одна людина тримає проєкт такого розміру в купі. Мультиплеєр, заради якого все й будувалося, ще не запускався: немає Photon App Id, тож немає й другого гравця.

Unity 6 · C# · Photon Fusion 2 Публічного білда поки немає. Запускається на моїй машині - рівно стільки я і заявляю.

Обмеження

Ні художників, ні левел-дизайнера, ні бюджету. Усе, чим ходить гравець, має складатися з безкоштовних паків плюс коду, а весь квартал має перезбиратися з нуля після будь-якої зміни.

Рішення

  • 4 записи
  • кожен - із ціною

Журнал рішень

Рівень, префаби, персонажі та інтерфейс генеруються кодом редактора

Замість
Збирати сцени руками в редакторі Unity
Тому що
Зібрана руками сцена - це бінарний блоб, який може правити одна людина і не може переглянути ніхто. Згенерований контент перетворює весь квартал на читабельний diff, а повна перезбірка - це один пункт меню або одна команда в batchmode.
Ціна рішення
3 918 із 10 723 рядків - це інструментарій, який не побачить жоден гравець. Посунути одну стіну означає правити код, а не тягнути маркер.

Сходи намальовані сходинками поверх одного прихованого пандуса-колайдера

Замість
Сходинки з власними колайдерами
Тому що
Гравець іде плавним схилом, NavMesh запікає чисту поверхню, а око все одно бачить сходи. Справжні колайдери сходинок чіпляють капсули персонажів і дають агентам шлях, з яким вони борються.
Ціна рішення
Візуальна й фізична геометрія навмисно не збігаються, і це має лишатися записаним, інакше майбутня правка тихо поверне зачіпку.

Photon Fusion 2 і Photon Voice 2 живуть за директивами компіляції

Замість
Залежати від мережевого SDK всюди
Тому що
Проєкт має відкриватися, компілюватися й лишатися ігровим без Photon App Id. Мережа - це функція гри, а не умова роботи над нею.
Ціна рішення
Дві конфігурації збірки, які треба тримати компільними, і мережеві гілки коду, які легко лишити без тестів саме тому, що дефолтна збірка їх не виконує.

C# перевіряється запуском Unity у batchmode без графіки

Замість
Відкривати редактор заради кожної зміни
Тому що
Компіляція без графіки займає частку часу й запускається з термінала - єдиний практичний спосіб для однієї людини тримати десятитисячний Unity-проєкт чесним між ігровими сесіями.
Ціна рішення
Batchmode доводить, що воно компілюється і що контент перезбирається. Про відчуття від гри він не доводить нічого, тож плейтест лишається повністю ручним.

Код

  • Assets/_Game/Scripts/Editor/GetawayLevelKit.cs

З вихідного коду

Рішення про сходи в коді: кожна сходинка - куб зі знятим колайдером, а під ними одна прихована повернута плита, на якій насправді стоять гравець і NavMesh. Два з трьох коментарів дописані для цієї сторінки, решта - дослівно.

public static void Stairs(Transform parent, string name, Vector3 start, Vector3 direction, float run,
    float rise, float width, int steps, Material material)
{
    var root = Empty(name, parent, Vector3.zero).transform;
    Vector3 dir = new Vector3(direction.x, 0f, direction.z).normalized;
    Vector3 side = Vector3.Cross(Vector3.up, dir);

    float stepRun = run / steps;
    float stepRise = rise / steps;

    for (int i = 0; i < steps; i++)
    {
        float top = start.y + stepRise * (i + 1);
        Vector3 centre = start + dir * (stepRun * (i + 0.5f));
        centre.y = top - stepRise * 0.5f;

        var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
        go.name = "Step";
        go.transform.SetParent(root, false);
        go.transform.localPosition = centre;
        go.transform.localRotation = Quaternion.LookRotation(dir, Vector3.up);
        go.transform.localScale = new Vector3(width, stepRise, stepRun);
        go.GetComponent<MeshRenderer>().sharedMaterial = material;
        Object.DestroyImmediate(go.GetComponent<Collider>());   // the eye gets steps
    }

    BuildRamp(root, start, dir, side, run, rise, width);        // the feet get a slope
}

private static void BuildRamp(Transform parent, Vector3 start, Vector3 dir, Vector3 side,
    float run, float rise, float width)
{
    const float thickness = 0.4f;
    float slope = Mathf.Sqrt(run * run + rise * rise);

    Vector3 alongSlope = (dir * run + Vector3.up * rise).normalized;
    Quaternion rotation = Quaternion.LookRotation(alongSlope, Vector3.Cross(alongSlope, side).normalized);

    // Sit the ramp so its top face passes through the step noses.
    Vector3 topMid = start + dir * (run * 0.5f) + Vector3.up * (rise * 0.5f);
    Vector3 centre = topMid - rotation * Vector3.up * (thickness * 0.5f);

    var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
    go.name = "RampCollider";
    go.transform.SetParent(parent, false);
    go.transform.localPosition = centre;
    go.transform.localRotation = rotation;
    go.transform.localScale = new Vector3(width, thickness, slope);
    go.GetComponent<MeshRenderer>().enabled = false;            // never drawn, always walked on
}

Цифри

  • заміряно, не округлено

Масштаб

10 723
рядків C#
3 918
з них - інструментарій редактора
83
ігрових скриптів
91
матеріалів переведено в URP скриптом

Стек

  • Unity 6000.3.11f1
  • C#
  • Universal RP 17.3
  • Photon Fusion 2.1.2
  • Photon Voice 2.63
  • Input System
  • AI Navigation
  • Editor tooling

Ретро

  • написано після, не до

Що змінив би

Мережа інтегрована й компілюється зі справжніми SDK, але жодного разу не тримала чотирьох гравців, бо в мене поки немає Photon App Id. Я не називатиму мультиплеєр готовим, доки не побачу, як він ламається на реальних затримках і втратах пакетів - усе інше було б заявою, яку я не можу підкріпити. Генерувати все кодом було правильно для соло-проєкту, і це перше, що я переглянув би тієї миті, коли зʼявиться левел-дизайнер: інструмент, який звільняє одного, стає стіною для другого.

Далі

Контакти

  • Інженер-програміст повного циклу

Почнімо розмову

Напишіть, що ви будуєте і що заважає. Відповідаю особисто, зазвичай того ж дня.

stasyukdmitriy2602@gmail.com Telegram @decimal128 LinkedIn github.com/sp1le