Первым делом пришла идея, я тогда пробовал делать игры только в 2d и пиксель арте. Порисовав в изометрии, я подумал, что графика слишком слабая и я не наберу восторженных отзывов об игре, да и к тому же некоторые симуляторы славятся тем, что после определённого цикла развития, развиваться становится уже некуда и игру приходится заканчивать просто без конечных титров.

В один прекрасный день, как это бывает у меня по цикличности, я вновь загорелся учить электронику, когда увидел книгу «Основы Электричества». Я её купил, а когда начал читать, то в первой теме я всё понимал, но дальше потребовался векторный анализ, который я не знал и тогда я принялся учить линейную алгебру и аналитическую геометрию. Так я прошел и векторный анализ и дошел до того, что захотелось реализовывать каждое знание, которое я подчерпнул из книги.

Таким образом я стал рассуждать, вот 3d, надо пустить луч, я помню, что был способ через матрицы, но не помню какой. Спросив у нейросети и выполнив то, что нужно, я заметил, что алгоритм пересечения сферы с лучом не правильный. Тогда я решил выработать свой способ, ну просто порасчитывать так как мне этого захотелось. Тогда я потратил 5 дней на это и выработал формулу, которая помогает манипулировать фронтом камеры без вычисления обратных матриц. Я обрадовался. Теперь моя функция бросания луча выглядит так.

make_ray_from_cursor (game, cam[CAM_MAIN], ray, x, y, 1000.f);

Также после это я сделал код для показа AABB контура и шейдер к нему.

неправильный контур объекта
неправильный контур объекта

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

После того как я сделал AABB и бросание луча я вспомнил о своей игре про кофейню. В первую очередь я просто разместил пол из плиток, а потом и стены. Стены час не мог правильно разместить по контурам. Для пола применяю инстансинг, чтобы одна моделька рисовалась за один рендер в нескольких местах.

Следующая задача была расставлять стену на том месте, где указываю курсор. Какое‑то время мне не удавалось это реализовать, а потом я понял как это делается. Я смог найти формулу пересечение луча с плоскостью. Поставил плоскость по Y координате и Указал нормаль смотреть вверх, то‑есть [0.0, 1.0, 0.0]. Бросал луч из центра экрана. Бросание луча из центра экрана ещё проще и эту идею я взял у пользователя Хабра, который до этого догадался. Она тоже связана с фронтом камеры, которая вытекала из моей формулы, но там ещё проще и работает только, если бросать луч из центра. Так что я стал бросать луч из центра. Находим пересечение, точку. И по этой точке я сделал небольшой алгоритм, который размечает стену кратно его размеру.

Далее надо было сделать поворот камеры. Потом у нашей камеры Y координату обращаем в ноль и получаем, что наша камера находится как будто на той же плоскости, что и точка. После этого получаем дистанцию и после этого можно уже крутить камеру вокруг используя радиану угла, cosf, sinf. Например для поворота камеру по кругу по часовой стрелке мы поворачиваем по -2 градуса, а камеру наоборот по 2 градуса. После того, как это получилось сделать, я добавил его в свой движок уже с более качественным кодом. Поворот сначала был по 90 градусов. Я решил проверить как выглядело это в sims, так как мне как раз надо было посмотреть где исчезает стена, если установить режим, чтобы ближайшие стены исчезали. Увидел, что у них камера поворачивается на 45 градусов, так что поменял параметр в своей игре и тоже теперь повороты получают по 45 градусов.

Дальше нужно было раскрашивать комнаты. Это оказалась сложной задачей. Я всё никак не мог придумать как её реализовать. Единственное, что придумал, это, что я буду в realtime вставлять текстуру части стены в текстуру стены по смещению. Раскрашиваться должна была замкнутая стена.

В итоге для этой задачи я придумал вот что. У меня уже есть алгоритм пускания луча и сравнение с AABB. Я разрисую стороны стены в разные цвета. У каждой стороны по два треугольника. Таким образом я узнаю какие треугольники отвечают за каждую сторону. И напишу алгоритм, который будет идти например всегда вправо и поворачивать, если преграда и дальше идти вправо, пока не встретит свой первоначальный след. Этим я и занялся.

Цвета текстуры получились вот такими.

цвета светофора и дополнительный цвет
цвета светофора и дополнительный цвет

Я поставил стену на сцене и покликал на каждую сторону стены.

Стена на сцене
Стена на сцене

Сравнение треугольников с лучём я выписал из книги. И какое-то время не получалось понять почему не работает. Только потом дошло спустя минут 10, что надо позицию сложить с позицией треугольника. :)

Получился вот такой код.

if (button == 3) {
        make_ray_from_cursor (game, cam[CAM_MAIN], ray[0], x, y, 1000.f);
        printf ("###########################################\n");
        for (uint32_t i = 0; i < wall_count; i++) {
            float t;
            /*
             * если aabb происходит, то сравниваем треугольники.
             */
            if (ray_aabb_intersect_obj_model_3d (ray[0], wall[i], &t)) {
                float u, v, t;
                int indx = index_of_triangle_by_ray_intersect_obj_model (ray[0], wall[i], &u, &v, &t);
                if (indx >= 0) {
                    printf ("intersect triangle index: %d by wall: %u\n", indx, i);
                }
            }
        }

А сам index_of_triangle_by_ray_intersect_obj_model такой.

int32_t index_of_triangle_by_ray_intersect_obj_model (struct ray *ray, struct obj_model *obj, float *u, float *v, float *t)
{
    uint32_t sz = obj->model->res_data->sz[obj->model->cur_frame];

    float a[3];
    float b[3];
    float c[3];

    int32_t found_indx = -1;
    float distance = 9999.f;
    int32_t cur_indx = 0;
    int i = 0;
    for (; cur_indx < sz;) {
        obj_model_get_triangle_of_vertex (obj, i, a, b, c);
        obj_model_set_triangle_vertex_with_pos (obj, a, b, c);

        if (is_ray_triangle_intersect (ray, a, b, c, u, v, t)) {
            if (distance > *t) {
                distance = *t;
                found_indx = cur_indx;
            }
        }

        cur_indx++;
        i += 3;
    }

    *t = distance;

    return found_indx;
}

Я выписал значения каждого индекса в стене. У каждой стороны по два индекса, вот они.

Зелёный

1

7

Жёлтый

2

8

Синий

11

5

Красный

10

4

Таким образом я узнал стороны стены. Далее нужно было написать алгоритм, который будет заполнять какой-то текстурой комнату, к чему я и приступил и в результате вышло вот это.

В следующей части у меня будет задача сделать закрашенной часть только внутренней комнаты. Это означает, что мне нужно брать текстуру и создавать новую из неё и закрашивать только ту часть стены, которая по алгоритму используется. Но это уже в другой части, так как там надо будет потрудиться думаю.

Комментарии (7)


  1. IvanoDigital
    21.11.2025 22:12

    разработал игру "на своем движке" звучит как "изобрел велосипед"


    1. diakin
      21.11.2025 22:12

      "А чего добился ты?" (с)
      ;)


      1. akardapolov
        21.11.2025 22:12

        "А что ты сделал для хип-хопа в свои годы?" (с)


    1. xverizex Автор
      21.11.2025 22:12

      Я работаю над продуктом мечты. Всё-равно, велосипед ли изобретаю или нет. Все мы поначалу, когда обучаемся чему-то, изобретаем то, что уже изучено. Так я обучаюсь математике, которая изучена, делаю движок, который уже кем-то тоже сделан. Это нормально.


  1. Jijiki
    21.11.2025 22:12

    прикольно, успехов вам. А как ходить по террейну и местности вы определились? у художников есть правило идти от общего к частному на всякий случай )


    1. xverizex Автор
      21.11.2025 22:12

      Спасибо. Как ходить по террейну пока не думал. Пока игру делаю симулятор кофейни и там управляешь камерой с видом сверху. Потом когда потребуется ходить по террейну, буду думать и искать методы.


      1. Jijiki
        21.11.2025 22:12

        вот на всякий случай демка, визуальная, тоже от 3 лица. dealers-dungeon.com/demo/ она указана в гитхабе сокол АПИ (это большой набор апи - уже почти стек) github.com/floooh/sokol.git