• Почему удаляются запросы во вкладке Network?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега Веб-разработка
    Это редиректы. Поставьте на вкладке галку «сохранять лог» и они останутся, но без payload и response.
    Ответ написан
    2 комментария
  • Class "Amp\Loop" not found при установленой библиотеке, как так?

    AMPHP no longer ships its own event loop. It’s now based on Revolt.
    https://amphp.org/upgrade#event-loop

    Либо ставьте старую версию, либо ищите туториал посвежее.
    Ответ написан
    2 комментария
  • Как искать значения не по номеру дочернего обьекта, а по значению title?

    Невозможно сказать не увидев html. Вероятнее всего не правильный селектор. Могу предположить что ваши title у td, а не у span. Соответственно должно быть так: td[title="${title}"] span.

    Но тут дело в другом - в том что вам в принципе приходится так делать. Похоже у вас проблемы с архитектурой самого приложения, так как задача строить объект js из данных вытаскиваемых из нод DOM страницы - один из индикаторов таких проблем. Вероятно не очень правильный выбор инструмента.
    Вам стоит посмотреть в сторону реактивных фреймворков - таких как React или Vue.
    Ответ написан
    Комментировать
  • Как вытянуть значение за одну асинхронную функцию?

    @zkrvndm
    Софт для автоматизации
    Ну так это же промисы, используй метод Promise.all
    https://developer.mozilla.org/ru/docs/Web/JavaScri...
    Ответ написан
    2 комментария
  • Как принудительно закрыть страницу через время?

    Stalker_RED
    @Stalker_RED
    Ну допустим, что этот ваш "timeout для других функций" это setTimeout()

    setTimeout(myFunction, 1000) // вызовет функцию myFunction через 1000 миллисекунд (1 сек)
    setTimeout(myFunction, 60000) // вызовет через 10 минут


    Осталось придумать что написать внутри myFunction.
    Если вы рассчитываете на window.close(), то в современных браузерах это сработает только в тех окнах, которые и открыты были скриптом.
    Ну и никто не помешает нажать потом Ctrl+Shift+T и восстановить закрытую вкладку.
    Ответ написан
    3 комментария
  • Как сделать mysql запрос?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    1. У вас синтаксическая ошибка в запросе, нет закрывающей скобки.
    2. Надо использовать подготовленные выражения и не пихать в строку запроса данные, пришедшие от клиента.
    Ответ написан
    3 комментария
  • Как законектить драйвер PDO mysql?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    mysql !== mysgl
    Ответ написан
    Комментировать
  • Как законектить драйвер PDO mysql?

    @rPman
    скорее всего php устанавливался вручную готовым пакетом, все .so идут в поставке по умолчанию
    настраивается в php.ini

    вот пример (пути могут другие быть)
    Ответ написан
    Комментировать
  • Почему цикл-тег html не работает, а выдает свою часть?

    vfreelancer
    @vfreelancer
    php
    <? - а что это? вроде так надо <?php
    Ответ написан
    Комментировать
  • Почему VS code не может установить виртуальное окружение?

    delphinpro
    @delphinpro
    frontend developer
    Ну так укажите полный путь к pip.exe
    Или добавьте этот каталог в системный PATH

    6368e4980be83295929053.png
    Ответ написан
    5 комментариев
  • Как сделать так, чтобы анимация подстраивалась под точку?

    @Ezekiel4
    Охотник на пиратов и сборщик монолитов
    Способ 1 (геморный):
    Объединить все анимации в группы, в зависимости от того, сколько и какие детали персонажа будут задействованы для её исполнения. На аниматоре персонажа выделить по одному слою для каждого фрагмента тела с дублирующими схожее действие анимациями через стейт машину, затем, имея простое число как ключ от стейт машины, вы просто считаете высоту препятствия. Или заранее проставляете точки для её просчёта.
    Например:
    6340b815c74c7939656683.png
    Это имеет смысл, если у вас реально большая игра. Примерно так и была реализована адаптивность анимаций в серии Assassin's Creed (точно Unity+, до неё хз), там более 800 фрагментов анимаций движения персонажа.

    Способ 2 (простой):
    Если у вас маленький или средний проект, не требующий идеального исполнения (хотя это тоже решаемо), воспользуйтесь возможностями дополнения Animation Rigging. Гайды по нему есть на всех языках в гугле.
    Ответ написан
    2 комментария
  • Как получить такой результат (вопрос по векторам)?

    freeExec
    @freeExec
    Участник OpenStreetMap
    Сформируй новый вектор, в котором одни компоненты остаются с такими же значениями, а по нужной оси смени знак.
    Ответ написан
    6 комментариев
  • Соответсвует ли код принципам солид?

    mayton2019
    @mayton2019
    Bigdata Engineer
    Есть такой анекдот что на 100 строк разработки Java приходится 10 строк Clojure с точно таким-же
    алгоримическим смыслом. Так вот мне кажется что в данном исходнике этот коэффицент еще худе.

    Код - по большей части ничего не делает. Он настолько формален и общ, что мне кажется что 50% callbacks можно заинлайнить и кода станет меньше а читаемость пострадает не сильно. Вобщем - редкий случай когда SOLID вместо помощи разработчику - создаёт ненужные абстракции.

    У кода - очень неравномерная плотность информации. Например в Weapons/Bow.cs есть метод BallisticVel который резко контрастирует с другим кодом. Тут - как будто клавиатуру взял другой человек и написал в Haskell-style формулу. У меня возникает вопрос. Почему автор так старался декомпозировать всякий формализм а сложную функцию не декомпозировал? Вобщем такая резкая смена плотности информации на квадратный метр исходников - очень настораживает.

    В качестве метрики "полезности" - я-бы спросил автора

    - Ты бы сам себе заплатил-бы за такой код?
    Ответ написан
    2 комментария
  • Соответсвует ли код принципам солид?

    K0TlK
    @K0TlK
    Буллю людей.
    Разделять код на миллион монобехов != ооп. Единственный способ достичь правильного ооп в юнити - отделять всю логику от движка, а монобехи использовать как вьюшку, чтобы отображать всякое. Но реализуемый для тебя способ - использовать интерфейсы, под каждый паблик метод отдельный интерфейс, взаимодействовать через эти интерфейсы, учиться нормально прокидывать зависимости: всегда должна быть какая-то точка входа, где будут инициализироваться компоненты и передаваться все зависимости.
    Virtual методы = плохо, класс в идеале должен быть либо abstract либо sealed.
    Protected поля = public поля = нарушение инкапсуляции/иммутабельности.
    PlayerHealth, PlayerEnergy, Health - это все одно и то же с разными реализациями, есть интерфейс IHealth все под него и просто этот интерфейс реализуешь.
    AudioPlayer вообще какой-то ужас. Во-первых, почему GameObject takeDamageSourseObject и т.д Почему GameObject, если ты потом получаешь у него компонент AudioSource, ты можешь конкретные компоненты в инспекторе передавать, т.е. не GameObject, a AudioSource и GetComponent потом делать не нужно будет. Во-вторых, из всего этого можно было сделать один компонент, в котором будет один метод Play, который будет принимать AudioSource и проигрывать его. Либо напрямую пропихивать AudioSource и воспроизводить звук.
    Нет какого-то единого кодстайла, где-то есть нижнее подчеркивание, где-то нет, где-то есть _cs где-то нет, для чего эта _cs я так и не понял. Где-то сериализуемое приватное поле, где-то тупо паблик.
    Про солид вообще смысла говорить нет. Интерфейсов пара штук на весь проект. Зависимости нормально не прокидываются, всё через поля.
    Это так, навскидку. Думаю, если зайти в папку bot, то можно будет диссертацию написать по содержимому этой папки.
    Ответ написан
    4 комментария
  • Почему бот идет к цели не всегда?

    K0TlK
    @K0TlK
    Буллю людей.
    Любой искусственный интеллект, возможности которого выходят за рамки "принеси подай иди подальше не мешай", проще будет сделать через Behaviour Tree. Без него, чем сложнее у тебя будет бот, тем больше кода придется писать и тем больше будет каких-то непонятных условий, что у тебя, собственно, и проявляется в апдейте, полностью состоящем из if else if else if else. С Behaviour Tree все куда проще, есть много разных классов, отвечающих за что-то одно, все они совмещаются в разные деревья.
    Пример я покажу с использованием ассета Behaviour Designer, т.к., если я буду делать свою реализацию BT, то, боюсь, места для текста в ответе не хватит. Стоит Designer 90$, к счастью, пиратить софт очень плохо и так делают только плохие люди.

    Для того, чтобы бот ходил, нужно направление движения, соответственно, это направление нужно откуда-то брать:
    using UnityEngine;
    
    namespace BehaviourTree
    {
        public interface IMovementInput
        {
            Vector3 Direction { get; }
        }
    }


    Ну и, собственно, сам бот:
    using UnityEngine;
    
    namespace BehaviourTree
    {
        [RequireComponent(typeof(Rigidbody))]
        public class Bot : MonoBehaviour
        {
            [SerializeField] private MonoBehaviour _botInput = null;
            [SerializeField] private float _speed = 10f;
    
            private Rigidbody _rb;
    
            private IMovementInput BotInput => (IMovementInput) _botInput;
    
            private void OnValidate()
            {
                if (_botInput is IMovementInput) return;
    
                Debug.LogError($"{nameof(_botInput)} should implement {nameof(IMovementInput)}");
                _botInput = null;
            }
    
            private void Awake()
            {
                _rb = GetComponent<Rigidbody>();
            }
    
            private void FixedUpdate()
            {
                var velocity = new Vector3(BotInput.Direction.x, 0, BotInput.Direction.z) * _speed;
                _rb.velocity = velocity;
            }
    
            public void LookAt(Vector3 direction)
            {
                var rotation = Quaternion.LookRotation(direction);
    
                transform.rotation = rotation;
            }
        }
    }


    Инжектим интерфейс ввода через инспектор и в FixedUpdate задаем скорость. Чтобы посмотреть в каком-то направлении, нужно вызвать метод LookAt. Чтобы прокинуть интерфейс через инспектор нужен какой-то монобех, который будет реализовывать интерфейс. Вот он:
    using UnityEngine;
    
    namespace BehaviourTree
    {
        public class BotInput : MonoBehaviour, IMovementInput
        {
            public Vector3 Direction { get; set; }
        }
    }

    В дальнейшем через этот BotInput я буду перемещать бота, просто задавая нужное направление. С ботом все, теперь можно накинуть Bot на какой-нибудь капсюль на сцене, на него же повесить BotInput и в поле BotInput компонента Bot поместить тот самый BotInput.

    62c017ffaf2a1016283427.png

    Теперь нужна какая-то цель, чтобы бот за ней шел. Интерфейс:
    using UnityEngine;
    
    namespace BehaviourTree
    {
        public interface ITarget
        {
            Vector3 Position { get; }
        }
    }

    Реализация:
    using UnityEngine;
    
    namespace BehaviourTree
    {
        public class SomeTarget : MonoBehaviour, ITarget
        {
            public Vector3 Position => transform.position;
        }
    }

    Все то же самое, что и с ботом. Поместить на какой-нибудь капсюль, отличный от капсюля бота.

    Ну а теперь BT. Добавляешь своему боту компонент Behaviour Tree: в инспекторе бота -> Add Component -> Behaviour Designer -> Behaviour Tree.
    Далее нужно как-то туда добавить инпут бота, для этого создаем скрипт:
    using BehaviorDesigner.Runtime;
    
    namespace BehaviourTree.Brain
    {
        public class SharedBotInput : SharedVariable<BotInput>
        {
            public static implicit operator SharedBotInput(BotInput input) => new SharedBotInput {Value = input};
        }
    }

    Теперь BotInput можно прокидывать в переменные Behaviour Designer. Кстати о них. Открываем Tools -> Behaviour Designer -> Editor, далее тыкаем на бота, к которому прикреплен компонент Behaviour Tree, в дизайнере открываем Variables в поле Name вводим botInput, в поле Type выбираем BotInput, нажимает Add. Далее в инспекторе бота перетаскиваем компонент BotInput в поле botInput компонента Behaviour Tree.

    62c018177e9a0543052458.png

    Теперь в Behavoiur Designer можно устанавливать значение Direction компонента BotInput.
    Сначала я покажу весь код, что я написал для управления ботом, а потом то, как это все выглядит в дизайнере.
    Бот будет очень простой - он будет просто ходить в рандомном направлении, а если увидит свой таргет, то будет тупо бежать в него.

    Собственно, хождение в рандомном направлении:
    using BehaviorDesigner.Runtime;
    using BehaviorDesigner.Runtime.Tasks;
    using UnityEngine;
    
    namespace BehaviourTree.Brain
    {
        public class SetRandomDirection : Action
        {
            public SharedVector3 Direction;
    
            public override TaskStatus OnUpdate()
            {
                Direction.Value = Vector3.Scale(Random.insideUnitSphere.normalized, new Vector3(1, 0, 1));
                return TaskStatus.Success;
            }
        }
    }

    Наследуясь от Action, SetRandomDirection появляется в нодах(Tasks) дизайнера в выпадающем меню Actions
    Теперь нужно как-то установить направление движения бота.
    using BehaviorDesigner.Runtime;
    using BehaviorDesigner.Runtime.Tasks;
    
    namespace BehaviourTree.Brain
    {
        public class SetInput : Action
        {
            public SharedBotInput SharedInput;
            public SharedVector3 Input;
    
            public override TaskStatus OnUpdate()
            {
                SharedInput.Value.Direction = Input.Value;
                return TaskStatus.Success;
            }
        }
    }

    Собственно, здесь все понятно. Берем значение из Input и присваиваем его в SharedInput.
    Поворот в определенном направлении:
    using BehaviorDesigner.Runtime.Tasks;
    
    namespace BehaviourTree.Brain
    {
        public class LookAtDirection : Action
        {
            public SharedBotInput Direction;
            public Bot Bot;
    
            public override TaskStatus OnUpdate()
            {
                Bot.LookAt(Direction.Value.Direction);
                return TaskStatus.Success;
            }
        }
    }

    Далее идет нахождение цели и следование за ней, соответственно цель эту надо тоже добавить в дизайнер.
    using BehaviorDesigner.Runtime;
    
    namespace BehaviourTree.Brain
    {
        public class SharedTarget : SharedVariable<SomeTarget>
        {
            public static implicit operator SharedTarget(SomeTarget target) => new SharedTarget {Value = target};
        }
    }

    Установка направления к цели:
    using BehaviorDesigner.Runtime;
    using BehaviorDesigner.Runtime.Tasks;
    using UnityEngine;
    
    namespace BehaviourTree.Brain
    {
        public class SetDirectionToTarget : Action
        {
            public SomeTarget Target;
            public Bot Origin;
            public SharedVector3 Direction;
    
            public override TaskStatus OnUpdate()
            {
                var direction = (Target.Position - Origin.transform.position).normalized;
                Direction.Value = new Vector3(direction.x, 0, direction.z);
                return TaskStatus.Success;
            }
        }
    }


    Теперь нужно как-то дать возможность боту увидеть цель. Интерфейс:
    namespace BehaviourTree.Brain
    {
        public interface IEye
        {
            bool InSight(ITarget target);
        }
    }

    Реализация:
    using UnityEngine;
    
    namespace BehaviourTree.Brain
    {
        public class Eye : IEye
        {
            private readonly float _fov;
            private readonly Transform _origin;
    
            public Eye(float fov, Transform origin)
            {
                _fov = fov;
                _origin = origin;
            }
    
            public bool InSight(ITarget target)
            {
                var direction = target.Position - _origin.position;
    
                var angle = Mathf.Acos(Vector3.Dot(_origin.forward.normalized, direction.normalized)) * Mathf.Rad2Deg;
    
                return angle <= _fov;
            }
        }
    }

    Здесь все просто. Глаз, у которого есть Field of View в методе InSight проверяем, входит ли позиция таргета в этот фов. Проверяется все очень просто. Находим направление от бота к таргету, вычисляем угол между этим вектором направления и направлением взгляда бота, сравниваем. Чтобы вычислить угол нужно знать всего лишь формулу скалярного произведения векторов из нее выводим то, что у меня в коде умножение на Mathf.Rad2Deg потому что Mathf.Acos возвращает угол в радианах, а я его перевожу в градусы.
    Ну и последнее - нахождение цели:

    Все таки нужно было писать свою реализацию BT, потому что места все равно не хватило, продолжение в комментах.
    Ответ написан
    5 комментариев
  • Что я не так делаю с интерфейсами?

    K0TlK
    @K0TlK
    Буллю людей.
    Что я не так делаю с интерфейсами?

    Всё.
    У тебя у одного интерфейса слишком много ответственностей у него и Warside какой-то есть и дамаг может принимать и умереть может.

    Разделяй этот интерфейс на несколько.

    namespace Health
    {
        public interface IHealth
        {
            void Lose(int amount);
            void Restore(int amount);
        }
        
        public interface IMutable<out T>
        {
            T Current { get; }
        }
        
        public interface IFinal
        {
            event Action Over;
        }
    }


    Получается такой Health:

    using System;
    using UnityEngine;
    
    namespace Health
    {
        public class Health : IHealth, IFinal, IMutable<int>
        {
            public event Action Over;
            private readonly int _max;
            private const int Min = 0;
    
            public Health(int max)
            {
                _max = max;
                Current = _max;
            }
            
            public int Current { get; private set; }
            
            public void Lose(int amount)
            {
                SetCurrent(Current - amount);
            }
    
            public void Restore(int amount)
            {
                SetCurrent(Current + amount);
            }
    
            private void SetCurrent(int amount)
            {
                Current = Mathf.Clamp(amount, Min, _max);
                
                if (Current == Min) Over?.Invoke();
            }
            
        }
    }


    Health не должен быть отдельным компонентом, который будет висеть на условном рыцаре. Рыцарь будет содержать в себе этот Health, но напрямую хп ему изменять никто не будет, поэтому нужен еще один интерфейс IDamageable:
    namespace Health
    {
        public interface IDamageable
        {
            void ApplyDamage(int amount);
        }
    }


    И сам рыцарь:
    using UnityEngine;
    
    namespace Health
    {
        public class Knight : MonoBehaviour, IDamageable
        {
            [SerializeField] private int _maxHealth = 100;
            private Health _health;
    
            private void Awake()
            {
                _health = new Health(_maxHealth);
            }
    
            private void OnEnable()
            {
                _health.Over += Die;
            }
    
            private void OnDisable()
            {
                _health.Over -= Die;
            }
    
            public void ApplyDamage(int amount)
            {
                _health.Lose(amount);
                Debug.Log($"Damaged, hp left - {_health.Current}");
            }
    
            private void Die()
            {
                Debug.Log("Died");
                Destroy(gameObject);
            }
        }
    }


    Теперь, чтобы нанести урон рыцарю, нужно получить компонент IDamageable и вызвать его метод ApplyDamage:
    using UnityEngine;
    
    namespace Health
    {
        public class Enemy : MonoBehaviour
        {
            [SerializeField] private int _damage = 50;
            
            private void OnTriggerEnter2D(Collider2D other)
            {
                if (other.TryGetComponent(out IDamageable damageable))
                {
                    damageable.ApplyDamage(_damage);
                }
            }
        }
    }


    Всё. Используй TryGetComponent и тогда не нужно будет делать миллион проверок является ли что-то null.
    Warside твой должен висеть на рыцаре, а не на хп, поэтому делай отдельный интерфейс под этот Warside.
    Ответ написан
    5 комментариев
  • Можете ли вы дать идею для реализации AI?

    EveningEugene
    @EveningEugene
    Unity-разраб
    Есть функция Vector2.SignedAngle, которая возвращает угол между двумя векторами. Причём эта версия Возвращает значения от -180 до 180. В зависимости от того значение меньше или больше нуля вам и нужно поворачивать направо или на лево, если вообще нужно поворачивать. И вот это "нужно ли поворачивать" - это вовсе не "угол равен 0", а какие-то небольшие отклонения от нуля.
    Уделяйте больше времени чтению документации, задача очень простая. Вам уже сказали, что тут простая векторная математика, а значит идёте и исследуете документацию по Vector2
    Ответ написан
    6 комментариев
  • Не могу понять что не так, почему не работает?

    vabka
    @vabka Куратор тега C#
    Токсичный шарпист
    rightMove = !false;
    Интересный код :)

    В целом если у тебя !rightMove (истина, когда rightMove = false) у тебя в итоге код попадает только в ту ветку, где rightMove=false

    И наоборот
    Ответ написан
  • Почему код не записывает значения в таблицу excel?

    kshnkvn
    @kshnkvn
    yay ✌️ t.me/kshnkvn
    def writeRow(sheet, letter, arr):
        print(arr)
        i = 0
        u = 0
        while i < len(arr):
            nextrow = sheet.max_row + 1
            sheet[letter + str(nextrow)].value = arr[i]
            i = i + 1
            u = u + 1
    Ответ написан
    Комментировать
  • Как сложить все массивы класса?

    @o5a
    Я так понял что-то типа такого, для неизвестного количества элементов
    from dataclasses import fields
    
    def combine(elements):
        data = {field.name: sum([getattr(elem,field.name) for elem in elements],[]) for field in fields(elements[0])}
        return Element(**data)
    
    a = Element(...)
    b = Element(...)
    ...
    
    c = combine([a, b])
    Ответ написан
    Комментировать