Форум программистов, компьютерный форум, киберфорум
Unity, Unity3D
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.90/29: Рейтинг темы: голосов - 29, средняя оценка - 4.90
0 / 0 / 0
Регистрация: 15.10.2020
Сообщений: 42

Атака юнитами юнитов

26.05.2021, 21:34. Показов 5890. Ответов 12

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

Суть вопроса: Юниты разных команд должны сражаться друг с другом, при этом один юнит должен фиксированно атаковать другого, пока он не умрёт, при их встрече друг с другом. Т.е. мне надо, чтобы атака распространялась на одного врага, а не на нескольких, как в случае, если сделать это триггером(могут несколько попасть в триггер юнита и получать урон). При этом всё происходит автоматически, т.е. игрок не указывает какому юниту какого врага атаковать. Как всё-таки мне сделать определение определённого врага среди множества, который будет ближе всех к союзному юниту и заставить атаковать его до тех пор, пока тот не умрёт, а после переключиться по такому же принципу на другого?

Добавлено через 1 минуту
Примечание: смотрел видеоролики на ютубе, в т.ч. англоязычные, не совсем "бум-бум", а иногда и весь принцип не подходит.
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
26.05.2021, 21:34
Ответы с готовыми решениями:

Неразберуха с разными Юнитами
Доброго времени суток. Вот в чем проблема. Есть у меня 2 юнита. С первого во второй передаю переменные. Когда это вроде как наладил,...

Открытие проекта со всеми юнитами
Здравствуйте! Есть проект на delphi 7, состоящий из основной программы и служебного unit'a. Не нашел в настройках, возможно ли сделать...

надо разработать процедуры с юнитами
Исходные данные вводите из текстового файла. Результаты работы программы также поместите в текстовый файл. Дана матрица A порядка...

12
гуглю вместо тебя
 Аватар для raygman
240 / 157 / 88
Регистрация: 20.05.2021
Сообщений: 436
26.05.2021, 22:27
как раз вышел видос похожий, от одного юнитщика
на 23 минуте уже ищет ближайшего, явно не мегу крутой код, но там и не пытались сделать мега крутым, ну вдруг какую функцию не поймешь или гугли, или пиши



Добавлено через 3 минуты
ну вдруг какую функцию не поймешь или гугли, или пиши
0
0 / 0 / 0
Регистрация: 15.10.2020
Сообщений: 42
26.05.2021, 22:48  [ТС]
Цитата Сообщение от raygman Посмотреть сообщение
как раз вышел видос похожий, от одного юнитщика
на 23 минуте уже ищет ближайшего, явно не мегу крутой код, но там и не пытались сделать мега крутым, ну вдруг какую функцию не поймешь или гугли, или пиши
Выручаешь, спасибо). Завтра посмотрю видеоролик.
0
0 / 0 / 0
Регистрация: 15.10.2020
Сообщений: 42
28.05.2021, 16:12  [ТС]
Цитата Сообщение от raygman Посмотреть сообщение
ну вдруг какую функцию не поймешь или гугли, или пиши
В общем, я посмотрел видео и, насколько я понял, там идёт изначальное определение врагов, а у меня они должны определяться по ходу. Вот увидел враг другого врага, определил его как свою цель и долбит, пока хп не закончатся и так далее. А у него там что-то изначально по парам разбивка идёт.
0
гуглю вместо тебя
 Аватар для raygman
240 / 157 / 88
Регистрация: 20.05.2021
Сообщений: 436
28.05.2021, 17:03
SStorm, ну и, видео почти о том же, систему можно просто интерпретировать под себя

Цитата Сообщение от SStorm Посмотреть сообщение
там идёт изначальное определение врагов
- там затрагивалось о листе и в Awake определялись все юниты,
поскольку используется List, кто тебе запрещает сделать добавление в List новых таргетов

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

P.S. ну и сама реализация агра зависит от типа игры, если например как tabs, то юнитов со всей карты собирать и там сортировать, если как варкрафт где на базе стоят юниты спокойно и ждут, то у них есть радиус агра, а потом уже включается поиск ближайшего, можно например реализовать с помощью Physics.OverlapSphere

https://docs.unity3d.com/Scrip... phere.html

на видео где-то на 11мин уже пишет оверлапсферу
1
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
28.05.2021, 19:15
Лучший ответ Сообщение было отмечено SStorm как решение

Решение

Цитата Сообщение от SStorm Посмотреть сообщение
Юниты разных команд должны сражаться друг с другом, при этом один юнит должен фиксированно атаковать другого, пока он не умрёт, при их встрече друг с другом. Т.е. мне надо, чтобы атака распространялась на одного врага, а не на нескольких, как в случае, если сделать это триггером(могут несколько попасть в триггер юнита и получать урон). При этом всё происходит автоматически, т.е. игрок не указывает какому юниту какого врага атаковать. Как всё-таки мне сделать определение определённого врага среди множества, который будет ближе всех к союзному юниту и заставить атаковать его до тех пор, пока тот не умрёт, а после переключиться по такому же принципу на другого?
Ох, ничего то вы не можете, ничего не умеете...
Коментарии специально писал на русском:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
 
public class UnitController : MonoBehaviour
{
    [SerializeField] float AttackPower = 20;//сила удара
    [SerializeField] float AttackInterval = 100;//средний интервал атак
    [SerializeField] float RunSpeed = 3;//скорость бега
    [SerializeField] float HP = 100;//здоровье
 
    //Отряд юнита
    [SerializeField] Team Team;
 
    //список всех живых юнитов на сцене
    static List<UnitController> AllUnits = new List<UnitController>();
 
    //юнит, которго мы атакуем в данный момент
    UnitController Opponent;
 
    //наш rigidbody
    Rigidbody rb;
 
    private void Start()
    {
        rb = GetComponent<Rigidbody>();
 
        //добавляем себя в список живых юнитов
        AllUnits.Add(this);
    }
 
    private void Update()
    {
        //если у нас есть опоонент - бежим к нему
        if (Opponent != null)
            rb.velocity = Vector3.Lerp(rb.velocity, (Opponent.transform.position - transform.position).normalized * RunSpeed, Time.deltaTime * 10);
        else
            //иначе - ищем оппонента
            FindOpponent();
    }
 
    private void FindOpponent()
    {
        //перебираем живых юнитов, находим ближайшего врага, у которго нет оппонента
        var bestDistance = float.MaxValue;
        var bestUnit = default(UnitController);
 
        foreach (var unit in AllUnits)//перебираем юнитов
        if (unit.Team != Team)//это враг?
        if (unit.Opponent == null)//у него нет оппонента?
        {
            var distSqr = (transform.position - unit.transform.position).sqrMagnitude;//получаем квадрат расстояния до врага
            if (distSqr < bestDistance)//ближе чем предыдущий кандидат?
            {
                //запоминаем врага
                bestDistance = distSqr;
                bestUnit = unit;
            }
        }
        
        //нашли блажайшего врага?
        if (bestUnit != null)
        {
            //назначаем его своим оппонентом
            Opponent = bestUnit;
            //а себя - его оппонентом
            bestUnit.Opponent = this;
        }
    }
 
    //мы с кем-то встретились
    private void OnTriggerStay(Collider other)
    {
        if (Opponent == null)
            return;//если у нас нет оппонента - не реагируем
 
        //это наш оппонент?
        var unit = other.GetComponent<UnitController>();
        if (unit == Opponent)
        {
            //атакуем в случайные моменты времени
            if (UnityEngine.Random.Range(0, AttackInterval) < 1)
                AttackOpponent();
        }
    }
 
    //атакуем врага
    private void AttackOpponent()
    {
        //отнимаем HP
        Opponent.HP = Mathf.Max(0, Opponent.HP - AttackPower);
        //с силой откидываем назад
        Opponent.rb.AddForce((Opponent.transform.position - transform.position).normalized * 100  * AttackPower, ForceMode.Acceleration);
        //он умер?
        if (Opponent.HP <= 0)
            Opponent.OnDead();//вызываем его метод для смерти
    }
 
    private async void OnDead()
    {
        //вычеркиваем себя из списка живых юнитов
        AllUnits.Remove(this);
        //падаем
        rb.constraints = RigidbodyConstraints.None;
        //вычеркиваем себя из оппонента (если есть)
        if (Opponent != null)
            Opponent.Opponent = null;
        //обнуляем своего оппонента
        Opponent = null;
        //умираем через 1 сек
        await Task.Delay(1);
        Destroy(gameObject);
    }
}
 
public enum Team
{
    Red, Blue
}
Вложения
Тип файла: zip New Unity Project (5).zip (489.7 Кб, 30 просмотров)
3
0 / 0 / 0
Регистрация: 15.10.2020
Сообщений: 42
31.05.2021, 19:08  [ТС]
Storm23, Слегка(сильно) затормозил с ответом. Тут, получается, идёт строгое разбиение по парам. А есть вариант, когда даже если уже есть юнит_1, который идёт в атаку на юнит_2, чтобы юнит_3 подключился и тоже атаковал юнит_2, вместе с юнит_1? Я дико извиняюсь за подобного рода вопросы, просто я вот действительно немного в такой "темноте" сейчас нахожусь с этим вопросом. Т.е. это как-то массивом обычным(либо листом) реализовать, чтобы быть оппонентом сразу для нескольких врагов?

Добавлено через 8 минут
Прочитал сообщение raygman и понял, что он уже ответил на этот вопрос фразой:
Цитата Сообщение от raygman Посмотреть сообщение
если убрать условие что юнит должен долбить только свободного, но оставить условие при этом что ближайшего,
то вот тебе и твой код будет
В принципе, это логично, да).

Правда как быть тогда с удалением себя из оппонента:

Цитата Сообщение от Storm23 Посмотреть сообщение
//вычеркиваем себя из оппонента (если есть)
        if (Opponent != null)
            Opponent.Opponent = null;
, так как оппонентов несколько может быть. В итоге всё равно придётся создавать массив и пихать своих оппонентов "в себя", чтобы потом обратиться к ним и удалить себя же из их оппонента?
0
гуглю вместо тебя
 Аватар для raygman
240 / 157 / 88
Регистрация: 20.05.2021
Сообщений: 436
31.05.2021, 19:24
SStorm, у Storm23 код работает по приоритету:
1) сначала на пары
2) если нет пары то ближайшего атакую
ну по гифке если судить
затести его код

Добавлено через 5 минут
а ошибся, по парам только, вообщем можешь вот здесь добавить else {} и тот же код с ифа, я код детально не читал его, но думаю норм
C#
1
2
3
4
5
6
7
8
9
10
if (unit.Opponent == null)//у него нет оппонента?
        {
            var distSqr = (transform.position - unit.transform.position).sqrMagnitude;//получаем квадрат расстояния до врага
            if (distSqr < bestDistance)//ближе чем предыдущий кандидат?
            {
                //запоминаем врага
                bestDistance = distSqr;
                bestUnit = unit;
            }
        }
Добавлено через 1 минуту
забудь я не читаю то что пишу видимо, такое взбрело что-то

Добавлено через 2 минуты
а да, ты можешь так сделать, только в Update тебе нужно будет закрыть поиск таргета когда он у тебя есть, а точнее вообще вынести его и сделать один раз вызов когда у тебя таргета умирает и на старте понятно что
1
0 / 0 / 0
Регистрация: 15.10.2020
Сообщений: 42
31.05.2021, 19:32  [ТС]
raygman, да, т.е. нет динамического обновления ближайшего врага. А ведь у юнитов может быть разная скорость и тот, кто был дальше, через секунду уже будет ближе. Но я думаю, что я могу это под себя сделать, просто задав определёный Range для юнита, в пределах которого врага можно будет записать в оппоненты, я пока это обмозговываю.

Добавлено через 4 минуты
Я завтра прям плотненько займусь этим делом и отпишу по результатам. Как раз, может до завтра создатель кода сможет ещё накинуть идей или что-то вроде).
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
03.06.2021, 18:46
Цитата Сообщение от SStorm Посмотреть сообщение
да, т.е. нет динамического обновления ближайшего врага.
Юнит просто может постоянно (несколько раз в секунду) искать ближайшего оппонента и перезапоминать его.
В Update он проверяет - если оппонент жив, то бежать к нему.
На близком расстоянии, перед ударом тоже проверяет - жив ли оппонент и бьёт его.

Под эту идею, ниже переписанный код от Storm23
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
 
public class UnitController : MonoBehaviour
{
    [SerializeField] float AttackPower = 20;//сила удара
    [SerializeField] float AttackInterval = 100;//средний интервал атак
    [SerializeField] float RunSpeed = 5;//скорость бега
    [SerializeField] float HP = 100;//здоровье
 
    [SerializeField] bool isAlive; //признаки жизни
    [SerializeField] float FindOpponentIntervalSec = 1f / 3f; // интервал обновления поиска оппонента (три раза в секунду)
    private float _findTimer;
 
    //Отряд юнита
    [SerializeField] Team Team;
 
    //список всех живых юнитов на сцене
    static List<UnitController> AllAliveUnits = new List<UnitController>();
 
    //юнит, которго мы атакуем в данный момент
    UnitController Opponent;
 
    //наш rigidbody
    Rigidbody rb;
 
    private void Start()
    {
        isAlive = true;
        rb = GetComponent<Rigidbody>();
 
        //добавляем себя в список живых юнитов
        AllAliveUnits.Add(this);
    }
 
    private void Update()
    {
        //если у нас есть опоонент - бежим к нему
        if (Opponent != null && Opponent.isAlive)
            rb.velocity = Vector3.Lerp(rb.velocity, (Opponent.transform.position - transform.position).normalized * RunSpeed, Time.deltaTime * 10);
        else
            rb.velocity = Vector3.zero;
        // ищем оппонента
        FindOpponent();
    }
 
    private void FindOpponent()
    {
        _findTimer += Time.deltaTime;
        if (_findTimer < FindOpponentIntervalSec) return;
 
        _findTimer = 0;
 
        //перебираем живых юнитов, находим ближайшего врага, у которго нет оппонента
        var bestDistance = float.MaxValue;
        var bestUnit = default(UnitController);
 
        foreach (var unit in AllAliveUnits)//перебираем юнитов
            if (unit.Team != Team)//это враг?
            {
                var distSqr = (transform.position - unit.transform.position).sqrMagnitude;//получаем квадрат расстояния до врага
                if (distSqr < bestDistance)//ближе чем предыдущий кандидат?
                {
                    //запоминаем врага
                    bestDistance = distSqr;
                    bestUnit = unit;
                }
            }
 
        //нашли блажайшего врага?
        if (bestUnit != null)
        {
            //назначаем его своим оппонентом
            Opponent = bestUnit;
        }
    }
 
    //мы с кем-то встретились
    private void OnTriggerStay(Collider other)
    {
        if (Opponent == null)
            return;//если у нас нет оппонента - не реагируем
 
        //это наш оппонент?
        var unit = other.GetComponent<UnitController>();
        if (unit == Opponent && unit.isAlive)
        {
            //атакуем в случайные моменты времени
            if (UnityEngine.Random.Range(0, AttackInterval) < 1)
                AttackOpponent();
        }
    }
 
    //атакуем врага
    private void AttackOpponent()
    {
        //отнимаем HP
        Opponent.HP = Mathf.Max(0, Opponent.HP - AttackPower);
        //с силой откидываем назад
        Opponent.rb.AddForce((Opponent.transform.position - transform.position).normalized * 100 * AttackPower, ForceMode.Acceleration);
        //он умер?
        if (Opponent.HP <= 0)
            Opponent.OnDead();//вызываем его метод для смерти
    }
 
    private async void OnDead()
    {
        isAlive = false;
 
        //вычеркиваем себя из списка живых юнитов
        AllAliveUnits.Remove(this);
        //падаем
        rb.constraints = RigidbodyConstraints.None;
        //обнуляем своего оппонента
        Opponent = null;
        //умираем через 1 сек
        await Task.Delay(1);
        Destroy(gameObject);
    }
}
 
public enum Team
{
    Red, Blue
}
2
0 / 0 / 0
Регистрация: 15.10.2020
Сообщений: 42
03.06.2021, 19:10  [ТС]
samana, Привет, я уже переделал его код под свои нужды, но вот как раз нерешённым остался лишь этот вопрос. Дело в том, что поиск просто по дальности нахождения юнита - это хорошо, но я бы хотел использовать что-то более конкретное в этом плане, например, с какого расстояния между потенциальным оппонентом и нашим юнитом он будет вписан в эти самые оппоненты. Как можно указать какой-то Range в условиях работы c квадратом расстояния?
Цитата Сообщение от samana Посмотреть сообщение
if (distSqr < bestDistance && distSqr <= Range)
{
...
}
Можно ли вписать в Range обычный float или int и как это будет работать с этим самым квадратом?
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
03.06.2021, 19:29
Цитата Сообщение от SStorm Посмотреть сообщение
Можно ли вписать в Range обычный float или int и как это будет работать с этим самым квадратом?
Конечно. Допустим вы вычисляете минимальное расстояние через sqrtMagnitude, но в Range хотите хранить расстояние в реальных единицах, тогда просто перемножайте Range
if (distSqr < bestDistance && distSqr <= Range*Range)
1
0 / 0 / 0
Регистрация: 15.10.2020
Сообщений: 42
03.06.2021, 19:38  [ТС]
Цитата Сообщение от samana Посмотреть сообщение
Конечно. Допустим вы вычисляете минимальное расстояние через sqrtMagnitude, но в Range хотите хранить расстояние в реальных единицах, тогда просто перемножайте Range
if (distSqr < bestDistance && distSqr <= Range*Range)
Ага, понял, большое спасибо.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
03.06.2021, 19:38
Помогаю со студенческими работами здесь

Взаимодействие между юнитами в игре
Имеется подобие очень примитивной игры. Два лучника сражаются друг с другом (объекты одного класса). #include &lt;tuple&gt; ...

Передача record между юнитами
Всем привет, скажите пожалуйста как перекинуть обычный класс с юнита на главный юнит? Этот код на кнопке в главном юните. type ...

Реализация заработка игровыми юнитами
Здравствуйте уважаемые программисты. Я новичек в мире actionscript но суть такова. есть переменная х есть переменная y мне...

Как передавать массив между тремя юнитами
Есть три юнита. Первый-главный в форме с кнопками. Остальные вообще без формы. По отдельности дополнительные юниты работают, и...

Лазарус не складывает больше одной единицы при переносе переменных между юнитами
Есть программа лазарус, тест. Нужно прибавлять переменной S+1 в Unit1 при каждом правильном ответе. Проблема в том, что сколько бы ни было...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11680&amp;d=1772460536 Одним из. . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 19.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru