Форум программистов, компьютерный форум, киберфорум
Unity, Unity3D
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/21: Рейтинг темы: голосов - 21, средняя оценка - 5.00
135 / 130 / 60
Регистрация: 16.06.2013
Сообщений: 527

Программно генерируемый пол (Unity 2D)

22.07.2019, 04:57. Показов 4225. Ответов 2
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем добра. Я только начинаю освоение Unity, поэтому, скорее всего вопрос будет до жути тупым. Но меня нужно ткнуть носом в нужное направление, т.к. я не смог найти (нагуглить/придумать) подходящего решения.

Суть задачи вот в чём - для тренировки, я решил перенести на Unity реализацию одной игрушки, которую делал на чистом C#, лет 5 назад. Игрушка примитивная, по функционалу - аналог гоночек на тетрисе. И вся загвоздка у меня с реализацией пола. По сути, у меня фиксированная камера, по пространству которой ездит машинка, а движутся именно пол и препятствия (это перешло со старой реализации).

Собственно, как я реализовал пол:
1. Создал Prefab, из спрайта с текстурой пола.
2. Создал скрипт, который:
- При инициализации генерирует объекты "плиток" пола так, чтобы замостить весь объём экрана (с небольшими вылетами).
- При работе (метод Update), постоянно сдвигает плитки пола.
- Как только ряд плиток пола выходит за верхнюю часть экрана, он переносит её под самый нижний ряд.
Таким образом, у нас получается "бесконечная дорожка" из плиток пола, создающая иллюзию движения.

Но, с такой реализацией есть проблема - хотя fps держится в районе 70, пол периодически подлагивает при движении, с периодичностью в 1-2 секунды. Возможно это связано с переносом плиток вниз.

На всякий случай, вот код скрипта работы с полом:
Кликните здесь для просмотра всего текста

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
public class spawnFloor : MonoBehaviour
{
 
    /// <summary>
    /// Префаб блока пола
    /// </summary>
    public GameObject floorBlock;
 
    /// <summary>
    /// Ширина блока пола
    /// </summary>
    private float width = 2.5f;
    /// <summary>
    /// Высота блока пола
    /// </summary>
    private float height = 2.5f;
    /// <summary>
    /// Ширина экрана
    /// </summary>
    private float widthScr = 10f;
    /// <summary>
    /// Высота экрана
    /// </summary>
    private float heightScr = 5f;
    /// <summary>
    /// Количество блоков по горизонтали
    /// </summary>
    private int ctWidth = 5;
    /// <summary>
    /// Количество блоков по вертикали
    /// </summary>
    private int ctHeight = 5;
    /// <summary>
    /// Линия, за которой блоки пола переносятся вниз
    /// </summary>
    private float destoyLine = -7.5f;
 
    /// <summary>
    /// Список созданных блоков пола
    /// </summary>
    private Rigidbody2D[,] floorBlocks;
 
    /// <summary>
    /// Инициализатор скрипта
    /// </summary>
    void Start()
    {
        //Инициализируем размеры блока пола актуальным значением
        initSizes();
        //Генерируем стартовй пол
        spawn();
    }
 
 
    /// <summary>
    /// Событие обновления скрипта
    /// </summary>
    void Update()
    {
        //Перемещаем пол
        moveFloor();
    }
 
    /// <summary>
    /// Инициализируем размеры блока пола актуальным значением
    /// </summary>
    private void initSizes()
    {
        //Получаем класс рендеринга нашего спрайта
        var renderer = floorBlock.GetComponent<SpriteRenderer>();
        //Получчаем прямоугольник спрайта
        Rect rectangle = renderer.sprite.textureRect;
        //Получаем ширину и высоту спрайта в юнитах
        width = rectangle.width / renderer.sprite.pixelsPerUnit;
        height = rectangle.height / renderer.sprite.pixelsPerUnit;
 
        //Получаем ширину экрана в юнитах
        var vector = Camera.main.ViewportToWorldPoint(Camera.main.rect.size);
        //Запоминаем высоту и ширину экрана
        widthScr = vector.x;
        heightScr = vector.y;
 
        //Получаем количество блоков по горизонтали и вертикали
        ctWidth = (int)((widthScr * 2) / width) + 1;
        ctHeight = (int)((heightScr * 2) / height) + 3;
 
        //Считаем линию переноса пола
        destoyLine = heightScr + height;
    }
 
 
    /// <summary>
    /// Спавним плитки пола
    /// </summary>
    private void spawn()
    {
        //Координаты блоков
        float x, y;
        //Созданный новый блок
        GameObject buff;
        //Инициализируем массив созданных блоков пола
        floorBlocks = new Rigidbody2D[ctHeight, ctWidth];
 
        //Проставляем координаты первого блока
        //(верхний левый угол экрана)
        x = -widthScr;
        y = heightScr;
 
        //Проходимся по строкам
        for (int i = 0; i < ctHeight; i++)
        {
            //Проходимся по блокам строки
            for(int j = 0; j < ctWidth; j++)
            {
                //Спавним блок, возвращая результирующий объект
                buff = Instantiate(
                        floorBlock, 
                        new Vector3(x, y, 10), 
                        Quaternion.identity
                    );
                //Получаем физический объект созданного блока
                floorBlocks[i, j] = buff.GetComponent<Rigidbody2D>();
                //Переносим координату к следующему блоку ряда
                x += width;
            }
 
            //Обновляем координаты
            x = -widthScr;
            y -= height;
        }
    }
 
    /// <summary>
    /// Перемещаем пол
    /// </summary>
    private void moveFloor()
    {
        //Значения сдвига вверх каждого из блоков линии
        Vector2 moveLine;
 
        //Если у нас есть строка блоков
        if (ctWidth > 0)
        {
            //Проходимся по строкам
            for (int i = 0; i < ctHeight; i++)
            {
                //Считаем, насколько нужно сдвинуть каждый из блоков линии
                moveLine = floorBlocks[i, 0].position + Vector2.up * Time.deltaTime * publicVariables.floorSpeed;
 
                //Если пол на данный момент выше критической точки
                if (moveLine.y > destoyLine)
                {
                    //Получаем координату нижнего ряда блоков
                    moveLine.y = getMinPosition() - height;
                    //Сдвигаем данынй ряд блоков
                    moveLine += Vector2.up * Time.deltaTime * publicVariables.floorSpeed;
                }
 
                //Проходимся по блокам строки
                for (int j = 0; j < ctWidth; j++)
                {
                    //Координату x подставляем от данного блока
                    moveLine.x = floorBlocks[i, j].position.x;
                    //Сдвигаем пол вверх
                    floorBlocks[i, j].MovePosition(moveLine);
                }
            }
        }
    }
 
    /// <summary>
    /// Получаем минимальную координату строки
    /// </summary>
    /// <returns>Значение координаты</returns>
    private float getMinPosition()
    {
        float ex = 0;
        
        //Проходимся по строкам
        for (int i = 0; i < ctHeight; i++)
            //Если этот ряд ниже
            if (floorBlocks[i, 0].position.y < ex)
                //ЗАменяем его значением минимум
                ex = floorBlocks[i, 0].position.y;
 
        return ex;
    }
}


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

Добавлено через 2 часа 35 минут
Небольшой апдейт. Ещё 3 часа гугления привели к такому решению:
1. На заднем плане разместил Plane.
2. Натянул на него текстуру.
3. Программно изменяю offset и scale для текстуры.
Конечно, в данном решении есть некоторые сложности с синхронизацией скорости перемещения объектов, которые должны находиться "на полу", но в целом - мне такое решение нравится.

Единственная проблема в том, что микрофризы никуда не делись =(. Буду дальше ковырять, чтобы найти чем они вызваны...
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
22.07.2019, 04:57
Ответы с готовыми решениями:

Нужно разобраться,пол сайта в индексе, пол запрещенны!
Проблема вот в чём,сайт на вордпрессе,сайту мало меньше месяца, поша индексация страниц. Некоторые попали в выдачу,а большая часть...

Составить программу которая считает пол,отр,дроб.пол,дроб.отр,нули среди задаваемых чисел
помогите пожалуйста!! составить программу которая считает положительные,отрицательные,дробные положительные,дробные отрицательные,нули...

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

2
298 / 260 / 108
Регистрация: 26.10.2012
Сообщений: 810
22.07.2019, 08:21
Лучший ответ Сообщение было отмечено Захарка как решение

Решение

Не надо добавлять полу компонент Rigidbody, это тяжелый компонент. Пол тут не физическое тело, как в Half-life с висящими ящиками. Максимум - коллайдер, и то тут он скорее всего не нужен. Rigidbody лучше двигать в FixedUpdate - в собственном потоке физики.
Rigidbody тут скорее всего и между собою почем зря взаимодействуют как физические объекты.
Как следствие проще двигать через Transform.position, а не через Rigidbody.MovePosition.
1
135 / 130 / 60
Регистрация: 16.06.2013
Сообщений: 527
22.07.2019, 12:59  [ТС]
jetyb, спасибо огромное - действительно, всё так и было. Я ещё поковырялся с разными методами формирования пола, и понял, что первоначальный вариант подходит лучше всего - с ним можно будет пол собирать из разных рандомных кусочков текстур. А тот же Plain лучше использовать как общий задний план.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
22.07.2019, 12:59
Помогаю со студенческими работами здесь

Cлучайно генерируемый массив
как сделать , чтобы массив генерировался рандомно.

Hashtable, генерируемый во время компиляции
Добрый день! Хотелось бы иметь Hashtable, который знает, какие ключи из него будут вызваны программой до первого запуска. Поясню с кодом...

Генерируемый архив делится автоматически
Сайт по приему фотографий на печать. После формирования заказа фотографии зипуются. Но где-то на хостинге видимо есть какие-то ограничения...

Упаковка библиотек в генерируемый jar файл
Вопрос в следующем, можно ли вшивать чужие библиотеки в свой jar файл, вшивать в смысле извлекать файлы из библиотеки и пересаживать к себе...

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


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

Или воспользуйтесь поиском по форуму:
3
Ответ Создать тему
Новые блоги и статьи
Установка Qt Creator для C и C++: ставим среду, CMake и MinGW без фреймворка Qt
8Observer8 05.04.2026
Среду разработки Qt Creator можно установить без фреймворка Qt. Есть отдельный репозиторий для этой среды: https:/ / github. com/ qt-creator/ qt-creator, где можно скачать установщик, на вкладке Releases:. . .
AkelPad-скрипты, структуры, и немного лирики..
testuser2 05.04.2026
Такая программа, как AkelPad существует уже давно, и также давно существуют скрипты под нее. Тем не менее, прога живет, периодически что-то не спеша дополняется, улучшается. Что меня в первую очередь. . .
Отображение реквизитов в документе по условию и контроль их заполнения
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеСпецтехники", разработанного в конфигурации КА2. Данный документ берёт данные из другого нетипового документа. . .
Фото всей Земли с борта корабля Orion миссии Artemis II
kumehtar 04.04.2026
Это первое подобное фото сделанное человеком за 50 лет. Снимок называют новым вариантом легендарной фотографии «The Blue Marble» 1972 года, сделанной с борта корабля «Аполлон-17». Новое фото. . .
Вывод диалогового окна перед закрытием, если документ не проведён
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать программный контроль на предмет проведения документа. . .
Программный контроль заполнения реквизитов табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: 1. Реализовать контроль заполнения реквизита. . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
Программная установка даты и запрет ее изменения
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: при создании документов установить период списания автоматически. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru