回答編集履歴

1

高速化案を追記

2020/08/16 09:48

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -47,3 +47,415 @@
47
47
 
48
48
 
49
49
  といった風に、変換行列から平行移動成分を取り出している例が載っていました。
50
+
51
+
52
+
53
+ # やたら遅い件について追記
54
+
55
+
56
+
57
+ カメラ範囲内のタイルを抜き出す処理がうまくいってないんじゃないかと思います。`Update`の...
58
+
59
+
60
+
61
+ ```C#
62
+
63
+ foreach (Vector2 item in this.getScreenPos())
64
+
65
+ {
66
+
67
+ this.GetMatrix(item);
68
+
69
+ }
70
+
71
+ ```
72
+
73
+
74
+
75
+ の部分がいまいち意図不明ですね...
76
+
77
+ 不必要そうな部分をばっさりカットして下記のようにしてみました。`rows`と`columns`は`Start`と`Update`の両方で使用する形になったのでローカル変数からフィールドに変更しました。`flatMatrix`を作るときの`x`と`y`の使い方も「横方向が`x`、縦方向が`y`」に統一した方が自然だろうと思って変更しています。
78
+
79
+
80
+
81
+ ```C#
82
+
83
+ using UnityEngine;
84
+
85
+
86
+
87
+ public class Example : MonoBehaviour
88
+
89
+ {
90
+
91
+ public Material material;
92
+
93
+ public int rows = 200;
94
+
95
+ public int columns = 200;
96
+
97
+
98
+
99
+ Matrix4x4[] flatMatrix;
100
+
101
+ readonly Matrix4x4[] array = new Matrix4x4[1023];
102
+
103
+
104
+
105
+ void Start()
106
+
107
+ {
108
+
109
+ int seed = Random.Range(-999999, 999999);
110
+
111
+ float[,] noiseMap = Noise.GenerateNoiseMap(columns, rows, seed, 50, 4, 0.5f, 2, Vector2.zero);
112
+
113
+ flatMatrix = new Matrix4x4[rows * columns];
114
+
115
+ for (int y = 0; y < rows; y++)
116
+
117
+ {
118
+
119
+ for (int x = 0; x < columns; x++)
120
+
121
+ {
122
+
123
+ float height = noiseMap[x, y];
124
+
125
+ int index = (y * columns) + x;
126
+
127
+ Vector2 pos = new Vector2(x, y);
128
+
129
+ Quaternion rotation = Quaternion.identity;
130
+
131
+ Vector3 scale = Vector3.one;
132
+
133
+ if (height < 0.35f)
134
+
135
+ {
136
+
137
+ flatMatrix[index] = Matrix4x4.TRS(pos, rotation, scale);
138
+
139
+ }
140
+
141
+ }
142
+
143
+ }
144
+
145
+ }
146
+
147
+
148
+
149
+ Vector2 GetScreenDownLeft()
150
+
151
+ {
152
+
153
+ Vector2 pos = new Vector2(0, 0);
154
+
155
+ Vector2 downLeft = Camera.main.ScreenToWorldPoint(pos);
156
+
157
+ return downLeft;
158
+
159
+ }
160
+
161
+
162
+
163
+ Vector2 GetScreenUpRight()
164
+
165
+ {
166
+
167
+ Vector2 pos = new Vector2(Screen.width, Screen.height);
168
+
169
+ Vector2 upRight = Camera.main.ScreenToWorldPoint(pos);
170
+
171
+ return upRight;
172
+
173
+ }
174
+
175
+
176
+
177
+ void Update()
178
+
179
+ {
180
+
181
+ // まずスクリーン左下・右上座標を求めて...
182
+
183
+ Vector2 downLeft = GetScreenDownLeft();
184
+
185
+ Vector2 upRight = GetScreenUpRight();
186
+
187
+
188
+
189
+ // 余白分だけ範囲を拡張し...
190
+
191
+ Vector2 margin = new Vector2(5, 5);
192
+
193
+ downLeft -= margin;
194
+
195
+ upRight += margin;
196
+
197
+
198
+
199
+ // 整数化し...
200
+
201
+ Vector2Int downLeftInt = new Vector2Int(Mathf.RoundToInt(downLeft.x), Mathf.RoundToInt(downLeft.y));
202
+
203
+ Vector2Int upRightInt = new Vector2Int(Mathf.RoundToInt(upRight.x), Mathf.RoundToInt(upRight.y));
204
+
205
+
206
+
207
+ // 200x200の範囲をはみ出さないようにクランプする
208
+
209
+ downLeftInt = Vector2Int.Min(
210
+
211
+ Vector2Int.Max(downLeftInt, Vector2Int.zero),
212
+
213
+ new Vector2Int(columns - 1, rows - 1));
214
+
215
+ upRightInt = Vector2Int.Min(
216
+
217
+ Vector2Int.Max(upRightInt, Vector2Int.zero),
218
+
219
+ new Vector2Int(columns - 1, rows - 1));
220
+
221
+
222
+
223
+ // 右上から左下を引き、描画領域のサイズとする
224
+
225
+ Vector2Int areaSize = upRightInt - downLeftInt;
226
+
227
+
228
+
229
+ // 描画するべきタイルの総数は、描画領域の幅×高さとなる
230
+
231
+ int tileCount = areaSize.x * areaSize.y;
232
+
233
+
234
+
235
+ // 描画済みタイル数を記録する変数を用意する
236
+
237
+ int drawnTileCount = 0;
238
+
239
+
240
+
241
+ // タイルを描画していく
242
+
243
+ while (drawnTileCount < tileCount)
244
+
245
+ {
246
+
247
+ // まず、今回のループで描画するべきタイル数を求める
248
+
249
+ // 未描画のタイル数は「タイル総数 - 描画済みタイル数」であり
250
+
251
+ // さらにそれを1023個を超えないようにクランプする
252
+
253
+ int tileCountToDraw = Mathf.Min(tileCount - drawnTileCount, 1023);
254
+
255
+
256
+
257
+ // 描画したい領域をflatMatrixからarrayへコピーしていく
258
+
259
+ // このとき、連続している部分はなるべく丸ごとコピーしてやった方が効率的だと思われる
260
+
261
+ // そこで、まずコピー済みタイル数を記録する変数を用意する
262
+
263
+ int transferredTileCount = 0;
264
+
265
+
266
+
267
+ // そして横方向オフセットを表す変数を用意する
268
+
269
+ // 初期値は描画済みタイル数を領域幅で割った余りとなる
270
+
271
+ int xOffset = drawnTileCount % areaSize.x;
272
+
273
+ while (transferredTileCount < tileCountToDraw)
274
+
275
+ {
276
+
277
+ // 今回のループでコピーするべきタイル数を求める
278
+
279
+ // まず、未コピーのタイル数を超えてはならず...
280
+
281
+ int tileCountToTransfer = tileCountToDraw - transferredTileCount;
282
+
283
+
284
+
285
+ // 現在の横オフセットから描画領域右端までのタイル数を超えてはならない
286
+
287
+ tileCountToTransfer = Mathf.Min(tileCountToTransfer, areaSize.x - xOffset);
288
+
289
+
290
+
291
+ // 描画領域座標系におけるコピー開始点の位置はこうなり...
292
+
293
+ int localTileIndex = drawnTileCount + transferredTileCount;
294
+
295
+ Vector2Int localTilePosition = new Vector2Int(localTileIndex % areaSize.x, localTileIndex / areaSize.x);
296
+
297
+
298
+
299
+ // それに描画領域の左下隅を足せばワールド座標系における位置となり...
300
+
301
+ Vector2Int worldTilePosition = localTilePosition + downLeftInt;
302
+
303
+
304
+
305
+ // flatMatrixのインデックスに直すとこうなる
306
+
307
+ int worldTileIndex = (worldTilePosition.y * columns) + worldTilePosition.x;
308
+
309
+
310
+
311
+ // flatMatrixのworldTileIndex番から数えてtileCountToTransfer個のデータを
312
+
313
+ // arrayのtransferredTileCount番以降にコピーする
314
+
315
+ System.Array.Copy(flatMatrix, worldTileIndex, array, transferredTileCount, tileCountToTransfer);
316
+
317
+
318
+
319
+ // 横オフセットを更新する
320
+
321
+ // とはいえ、2回目以降のループでは常に0となるはず
322
+
323
+ // 末尾では0でない可能性があるが、そもそもループが終了するので考慮不要
324
+
325
+ xOffset = 0;
326
+
327
+
328
+
329
+ // コピー済みタイル数を更新する
330
+
331
+ transferredTileCount += tileCountToTransfer;
332
+
333
+ }
334
+
335
+
336
+
337
+ // タイルをtileCountToDraw個描画する
338
+
339
+ Graphics.DrawMeshInstanced(MeshGenerator.basicMesh, 0, material, array, tileCountToDraw);
340
+
341
+
342
+
343
+ // 描画済みタイル数を更新する
344
+
345
+ drawnTileCount += tileCountToDraw;
346
+
347
+ }
348
+
349
+ }
350
+
351
+ }
352
+
353
+ ```
354
+
355
+
356
+
357
+ また、`MeshGenerator`はどのような作りになっているでしょうか?
358
+
359
+ もし`basicMesh`が「アクセスするたびに新規`Mesh`を生成して返すプロパティ」みたいな作りだったとすると、ここもスピードダウンの原因になりそうです。
360
+
361
+ さしあたり私の場合は下記のように「アクセスされたときに`Mesh`が未生成なら生成し、それ以降は生成済みの`Mesh`オブジェクトを返すプロパティ」としてみました。もしご自身で作成された`MeshGenerator`が速度低下を起こしそうか気になるようでしたら、コードをご提示いただければ何かアドバイスできるかもしれません。
362
+
363
+
364
+
365
+ ```C#
366
+
367
+ using UnityEngine;
368
+
369
+
370
+
371
+ public static class MeshGenerator
372
+
373
+ {
374
+
375
+ private static Mesh quadMesh;
376
+
377
+
378
+
379
+ public static Mesh basicMesh
380
+
381
+ {
382
+
383
+ get
384
+
385
+ {
386
+
387
+ if (quadMesh == null)
388
+
389
+ {
390
+
391
+ quadMesh = new Mesh
392
+
393
+ {
394
+
395
+ name = "Quad",
396
+
397
+ vertices = new[]
398
+
399
+ {
400
+
401
+ new Vector3(-0.5f, -0.5f, 0),
402
+
403
+ new Vector3(-0.5f, 0.5f, 0),
404
+
405
+ new Vector3(0.5f, -0.5f, 0),
406
+
407
+ new Vector3(0.5f, 0.5f, 0),
408
+
409
+ },
410
+
411
+ uv = new[]
412
+
413
+ {
414
+
415
+ new Vector2(0, 0),
416
+
417
+ new Vector2(0, 1),
418
+
419
+ new Vector2(1, 0),
420
+
421
+ new Vector2(1, 1),
422
+
423
+ },
424
+
425
+ triangles = new[]
426
+
427
+ {
428
+
429
+ 0, 1, 2,
430
+
431
+ 3, 2, 1,
432
+
433
+ }
434
+
435
+ };
436
+
437
+
438
+
439
+ quadMesh.RecalculateNormals();
440
+
441
+ quadMesh.RecalculateBounds();
442
+
443
+ quadMesh.UploadMeshData(true);
444
+
445
+ }
446
+
447
+
448
+
449
+ return quadMesh;
450
+
451
+ }
452
+
453
+ }
454
+
455
+ }
456
+
457
+ ```
458
+
459
+
460
+
461
+ ![図](b579d646ef93d31952577b8321dc537c.gif)