Форум программистов, компьютерный форум, киберфорум
SFML
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.89/9: Рейтинг темы: голосов - 9, средняя оценка - 4.89
14 / 14 / 13
Регистрация: 14.02.2013
Сообщений: 787
1

Зрение NPC с препятствиями

21.02.2021, 12:53. Показов 1817. Ответов 7

Author24 — интернет-сервис помощи студентам
Здравствуйте. подскажите как сделать зрение для NPC с препятствиями.
Пример на изображении.
Сейчас левый NPC видит правого, но правый за стеной, то-есть левый не должен видеть правого.
Для зрения я делаю так: есть спрайт viewArea (на изображении он красного цвета), я проверяю колизию viewArea с спрайтом NPC, если значение true, значит NPC видит другого NPC, но как бить если между ними стена ?
Миниатюры
Зрение NPC с препятствиями  
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
21.02.2021, 12:53
Ответы с готовыми решениями:

Игра лабиринт с препятствиями
Здраствуйте.Создаю игру лабиринт,но не простой лабиринт,а с препятствиями.Вот не знаю как добавить...

Прохождение под препятствиями
Здравствуйте. Подскажите пожалуйста, как персонажу проползти под препятствием. Как изменить его...

Количество маршрутов с препятствиями
Здравствуйте, вот познаю основы динамического программирование и столкнулся с проблемой во время...

Остановка перед препятствиями
игрок передвигается по карте, нужно что бы натыкаясь на стену игрок не мог идти на нее...как это...

7
6107 / 3461 / 1406
Регистрация: 07.02.2019
Сообщений: 8,794
21.02.2021, 14:56 2
Лучший ответ Сообщение было отмечено tdo22 как решение

Решение

tdo22, кастить лучи
Миниатюры
Зрение NPC с препятствиями  
1
14 / 14 / 13
Регистрация: 14.02.2013
Сообщений: 787
21.02.2021, 16:01  [ТС] 3
zayats80888, нужно пустить луч из начальной точки, и остановить его когда будет прикосновение со стеной или NPC ?
0
Просто Икс
685 / 227 / 46
Регистрация: 15.12.2009
Сообщений: 674
21.02.2021, 16:57 4
луч и есть твое "зрение". ты либо видишь стенку, либо NPC
кидаешь несколько лучей из одной точки под разным углом - получаешь углы обзора
0
6107 / 3461 / 1406
Регистрация: 07.02.2019
Сообщений: 8,794
21.02.2021, 17:06 5
Цитата Сообщение от tdo22 Посмотреть сообщение
нужно пустить луч из начальной точки, и остановить его когда будет прикосновение со стеной или NPC ?
Посмотрите пример№2 тут
0
14 / 14 / 13
Регистрация: 14.02.2013
Сообщений: 787
21.02.2021, 20:50  [ТС] 6
посмотрел, выглядит интересно.
Но если скажем будет 200npc как это скажется на производительности?
у меня будет более 500 npc
0
6107 / 3461 / 1406
Регистрация: 07.02.2019
Сообщений: 8,794
21.02.2021, 21:44 7
Цитата Сообщение от tdo22 Посмотреть сообщение
Но если скажем будет 200npc как это скажется на производительности?
Для начала там просто пример. Для эффективного каста использую специальные структуры данных (типа BSP tree)/
Если все 200 npc находятся на расстоянии видимости от персонажа, то придётся для каждого тестить 4 луча.
Если ещё там 100500 препятствий, то придётся каждый луч пускать через все 100500.
Но это уже вопрос структурирования данных.

Добавлено через 22 минуты
Пример под конкретно вашу задачу на базе того кода.
Напрвление обзора задаётся курсором мыши.
Положение наблюдателя - левой кнопкой мыши.
math.hpp
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
#pragma once
 
#include <SFML/System/Vector2.hpp>
#include <cmath>
#include <utility>
#include <limits>
 
namespace sf
{
    /// скалярное произведение
    inline float dot(Vector2f lh, Vector2f rh) noexcept
    {
        return lh.x * rh.x + lh.y * rh.y;
    }
    
    /// псевдовекторное произведение
    inline float cross(Vector2f lh, Vector2f rh) noexcept
    {
        return lh.x * rh.y - lh.y * rh.x;
    }
} // namespace sf
 
namespace math
{
    /// epsilon
    constexpr float eps() noexcept { return 1.0e-6f; }
    
    /// два пи
    constexpr float twoPi() { return 6.283185307f; }
    
    /// тест пересечения луча ab и отрезка cd
    /// если пересекаются, возвращает параметр t
    /// точка пересечения P(t) = A + t * AB
    inline bool testRaySegment(::sf::Vector2f a, ::sf::Vector2f ab,
                               ::sf::Vector2f c, ::sf::Vector2f d,
                               float & t) noexcept
    {
        bool result = false;
        auto const cd = d - c;
        auto const denom = cross(cd, ab);
        if (::std::abs(denom) > eps()) // прямые не коллинеарны
        {
            auto const ac = c - a;
            auto const t2 = cross(ab, ac) / denom;
            if (0 - eps() < t2 && t2 < 1 + eps()) // точка на отрезке
            {
                auto const t1 = ::std::abs(ab.x) > eps()
                                ? (ac.x + cd.x * t2) / ab.x
                                : (ac.y + cd.y * t2) / ab.y; // если луч вертикальный
                if (t1 > 0) // точка впереди луча
                {
                    result = true;
                    t = t1;
                }
            }
        }
        return result;
    }
    
    /// отрезок
    typedef ::std::pair<::sf::Vector2f, ::sf::Vector2f> Segment2f;
    
    /// тест пересечения луча ray {начало, направление} с группой отрезков [first, last)
    /// возвращает итератор ближайшего отрезка и параметр t точки пересечения
    /// если пересечений не найдено, то result.first == last
    /// точка пересечения P(t) = ray.first + t * ray.second
    template <class It>
    ::std::pair<It, float> findNearestRaySegments(Segment2f ray, It first, It last)
    {
        auto result = ::std::make_pair(last, ::std::numeric_limits<float>::max());
        for (; first != last; ++first)
        {
            float t;
            if (testRaySegment(ray.first, ray.second, first->first, first->second, t) &&
                t < result.second)
            {
                result.first = first;
                result.second = t;
            }
        }
        return result;
    }
} // namespace math

main.cpp
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#include <SFML/Graphics.hpp>
#include "math.hpp"
#include <vector>
#include <array>
#include <iterator>
 
// стена
class Wall : public sf::Drawable
{
public:
    Wall(sf::FloatRect bounds)
        : m_segs(sf::Lines, 4)
        , m_shape{{bounds.width, bounds.height}}
    {
        m_shape.setPosition(bounds.left, bounds.top);
        m_segs[0] = sf::Vertex({bounds.left, bounds.top}, sf::Color::Blue);
        m_segs[1] = sf::Vertex({bounds.left + bounds.width, bounds.top + bounds.height}, sf::Color::Blue);
        m_segs[2] = sf::Vertex({bounds.left, bounds.top + bounds.height}, sf::Color::Blue);
        m_segs[3] = sf::Vertex({bounds.left + bounds.width, bounds.top}, sf::Color::Blue);
    }
    
    // я просто стену в качестве цели использую
    sf::FloatRect getGlobalBounds() const
    {
        return m_shape.getGlobalBounds();
    }
    
    void setColor(sf::Color color)
    {
        m_shape.setFillColor(color);
    }
    
    std::array<math::Segment2f, 2> getSegments() const
    {
        return
        {
            math::Segment2f{m_segs[0].position, m_segs[1].position},
            math::Segment2f{m_segs[2].position, m_segs[3].position}
        };
    }
    
private:
    
    void draw(sf::RenderTarget & target, sf::RenderStates states) const override
    {
        target.draw(m_shape, states);
        target.draw(m_segs, states);
    }
    
    sf::VertexArray m_segs; // отрезки, для наглядности
    sf::RectangleShape m_shape;
};
 
// наблюдатель
class Watcher
    : public sf::Drawable
{
public:
    Watcher(float angle, float distance) // угол сектора в радианах
        : m_sectorRay(std::cos(angle * 0.5f), std::sin(angle * 0.5f))
        , m_cone(sf::Triangles, 3)
        , m_sqrDist(distance * distance)
    {
        const sf::Vector2f r = m_sectorRay;
        const sf::Vector2f l = {m_sectorRay.x, - m_sectorRay.y};
        m_cone[1].position = l * distance;
        m_cone[2].position = r * distance;
    }
    
    // проверка, нужно ли кастить луч в точку to
    // если нужно, результат записывается в ray
    bool getRay(sf::Vector2f to, math::Segment2f & ray) const
    {
        auto const delta = to - m_position;
        auto const sqrDist = dot(delta, delta);
        if (sqrDist <= m_sqrDist) // на расстоянии видимости
        {
            // крайние лучи сектора обзора
            // их лучше хранить в классе и считать при изменении напрявления взгляда,
            // для примера не принципиально
            auto const l = rotate(m_sectorRay);
            auto const r = rotate({m_sectorRay.x, -m_sectorRay.y});
            // в секторе обзора
            bool const inSector = cross(delta, l) > 0 &&
                                  cross(delta, r) < 0;
            if (inSector)
            {
                ray = {m_position, delta};
                return true;
            }
        }
        return false;
    }
    
    void setPosition(sf::Vector2f position)
    {
        m_position = position;
    }
    
    // направление взгляда на target
    void setDirection(sf::Vector2f target)
    {
        auto const dir = target - m_position;
        auto const sqrLen = dot(dir, dir);
        if (sqrLen > math::eps())
            m_rotation = dir / std::sqrt(sqrLen);
    }
    
    void setColor(sf::Color color)
    {
        if (m_cone[0].color != color)
        {
            m_cone[0].color =
            m_cone[1].color =
            m_cone[2].color = color;
        }
    }
    
private:
    
    void draw(sf::RenderTarget & target, sf::RenderStates states) const override
    {
        states.transform *= sf::Transform(m_rotation.x, -m_rotation.y, m_position.x,
                                          m_rotation.y,  m_rotation.x, m_position.y,
                                                  0.0f,          0.0f,         1.0f);
        target.draw(m_cone, states);
    }
    
    // хэлпер для поворота направления
    sf::Vector2f rotate(sf::Vector2f vec) const
    {
        return
        {
            dot(vec, sf::Vector2f(m_rotation.x, -m_rotation.y)),
            dot(vec, sf::Vector2f(m_rotation.y,  m_rotation.x))
        };
    }
    
    sf::Vector2f m_position{0.0f, 0.0f};
    sf::Vector2f m_rotation{1.0f, 0.0f};
    sf::Vector2f m_sectorRay; // "луч" сектора обзора
    sf::VertexArray m_cone;
    float m_sqrDist; // квадрат дистанции обзора
};
 
// находится ли объект aabb в поле обзора наблюдателя watch
// преграды для обзора - [first, last)
// для наглядности, все проверяемые лучи отправляем в dest для последубщей отрисовки
template <class It, /*DEBUG ONLY-->>*/class OutIt>
bool isVisible(const Watcher & watch, It first, It last, sf::FloatRect aabb, /*DEBUG ONLY-->>*/OutIt dest)
{
    sf::Vector2f const points[] =
    {
        {aabb.left, aabb.top},
        {aabb.left + aabb.width, aabb.top + aabb.height},
        {aabb.left, aabb.top + aabb.height},
        {aabb.left + aabb.width, aabb.top}
    };
    for (auto p : points)
    {
        math::Segment2f ray;
        if (watch.getRay(p, ray))
        {
            /*DEBUG ONLY-->>*/*dest++ = ray;
            auto res = math::findNearestRaySegments(ray, first, last);
            if (res.first == last || res.second > 1 + math::eps())
                return true;
        }
    }
    return false;
}
 
int main()
{
    sf::RenderWindow window{ sf::VideoMode{ 800, 600 }, L"RayCast", sf::Style::Close };
    window.setFramerateLimit(120);
    
    std::vector<Wall> walls // стены
    {
        sf::FloatRect(100, 100, 20, 300),
        sf::FloatRect(600, 200, 20, 200),
        sf::FloatRect(200, 200, 200, 20),
        sf::FloatRect(100, 500, 500, 20)
    };
    
    Wall target(sf::FloatRect(400, 300, 50, 50)); // цель
    target.setColor(sf::Color::Yellow);
    
    Watcher watch(30 * math::twoPi() / 360, 500); //наблюдатель
    
    bool needUpdate = true;
    
    std::vector<math::Segment2f> segments; // препятствия
    for (auto const & wall : walls)
    {
        auto const segs = wall.getSegments();
        segments.insert(segments.end(), segs.begin(), segs.end());
    }
    
    std::vector<math::Segment2f> rays; // лучи
    
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
            else if (event.type == sf::Event::MouseMoved)
            {
                auto const & m = event.mouseMove;
                watch.setDirection(window.mapPixelToCoords({m.x, m.y}));
                needUpdate = true;
            }
            else if (event.type == sf::Event::MouseButtonPressed &&
                     event.mouseButton.button == sf::Mouse::Left)
            {
                auto const & m = event.mouseButton;
                watch.setPosition(window.mapPixelToCoords({m.x, m.y}));
                needUpdate = true;
            }
        }
        
        if (needUpdate)
        {
            needUpdate = false;
            rays.clear();
            if (isVisible(watch, segments.begin(), segments.end(), target.getGlobalBounds(), std::back_inserter(rays)))
                watch.setColor(sf::Color(0x7f00007f));
            else
                watch.setColor(sf::Color(0x7f007f));
        }
        
        window.clear();
        
        for (auto const & wall : walls)
            window.draw(wall);
        window.draw(watch);
        window.draw(target);
        for (auto ray : rays)
        {
            sf::Vertex line[2] = {
                {ray.first, sf::Color::Green},
                {ray.first + ray.second, sf::Color::Green}
            };
            window.draw(line, 2, sf::PrimitiveType::Lines);
        }
        window.display();
    }
}


Только ещё один момент, в функции findNearestRaySegments я ищу ближайшее пересечение, перебирая все препятствия. Для вашей задачи можно останавливать поиск, если t < 1.0.
1
1 / 1 / 0
Регистрация: 22.03.2021
Сообщений: 4
22.03.2021, 13:45 8
tdo22 Можно стрелять лучами не каждый кадр, а в зависимости от задачи, которую сейчас выполняет NPC (система состояний у вас для NPC есть?). Также можно пускать луч со случайным отклонением в рамках угла обзора -- если не заметит на одном кадре, то может заметить на другом. Чтобы "случайные" числа генерировались быстро можно заранее посчитать небольшую табличку специально под зрение. "Случайность" тут нужна, чтобы NPC не просто сканировали углы обзора с шагом (как роботы, их тоже можно сделать), а более естественно скакали между разными углами в рамках обзора.
0
22.03.2021, 13:45
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
22.03.2021, 13:45
Помогаю со студенческими работами здесь

NPC pygame
Делаю игру на питоне. Есть класс NPC, в нем есть метод для погони за игроком. Вся проблема в...

Движение NPC
Помогите написать простую функцию движения NPC, сначала NPC появляется в центре экрана, затем будет...

Статичное игровое поле с препятствиями
Друзья, всем привет, сегодня столкнулся с проблемой графики на С++ и очень прошу вас помощи в...

Движение персонажа в консоли с препятствиями
Приветствую! Есть проблема: задали написать игру с главным персонажем, врагами и...

Магический квадрат и бег с препятствиями.
Задали вот такую интересную тему: Сделать программу, которая проверяет, для заданного квадрата,...

Разработка игры гонка с препятствиями
Разработка компьютерной игры &quot;2d Гонка&quot; Жанр шутер, вид сверху. Машина едет и расстреливает...


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

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru