update map
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 761 KiB |
BIN
assets/tiles/desert_0.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/tiles/desert_1.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
assets/tiles/desert_2.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
assets/tiles/desert_3.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
assets/tiles/desert_4.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
assets/tiles/desert_5.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
assets/tiles/desert_6.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
assets/tiles/desert_7.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/tiles/desert_8.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 63 KiB |
BIN
assets/tiles/forest_0.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/tiles/forest_1.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
assets/tiles/forest_2.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
assets/tiles/forest_3.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
assets/tiles/forest_4.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
assets/tiles/forest_5.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
assets/tiles/forest_6.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/tiles/forest_7.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
assets/tiles/forest_8.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 48 KiB |
BIN
assets/tiles/glacier_0.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
assets/tiles/glacier_1.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
assets/tiles/glacier_2.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
assets/tiles/glacier_3.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/tiles/glacier_4.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
assets/tiles/glacier_5.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
assets/tiles/glacier_6.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
assets/tiles/glacier_7.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
assets/tiles/glacier_8.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 36 KiB |
BIN
assets/tiles/grassland_0.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/tiles/grassland_1.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/tiles/grassland_2.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/tiles/grassland_3.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/tiles/grassland_4.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/tiles/grassland_5.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/tiles/grassland_6.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/tiles/grassland_7.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/tiles/grassland_8.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 41 KiB |
BIN
assets/tiles/mountain_0.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
assets/tiles/mountain_1.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
assets/tiles/mountain_2.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
assets/tiles/mountain_3.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
assets/tiles/mountain_4.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
assets/tiles/mountain_5.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
assets/tiles/mountain_6.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
assets/tiles/mountain_7.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
assets/tiles/mountain_8.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 45 KiB |
BIN
assets/tiles/snow_mountain_0.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/tiles/snow_mountain_1.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/tiles/snow_mountain_2.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
assets/tiles/snow_mountain_3.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/tiles/snow_mountain_4.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/tiles/snow_mountain_5.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/tiles/snow_mountain_6.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
assets/tiles/snow_mountain_7.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/tiles/snow_mountain_8.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
@@ -7,12 +7,20 @@ import { getClusteredTileVariant } from '@/utils/procedural'
|
||||
TextureStyle.defaultOptions.scaleMode = 'nearest'
|
||||
|
||||
// 地形变体配置
|
||||
const TILE_VARIANTS: Record<string, { prefix: string, count: number }> = {
|
||||
'RAINFOREST': { prefix: 'rainforest', count: 9 },
|
||||
'BAMBOO': { prefix: 'bamboo', count: 9 },
|
||||
'GOBI': { prefix: 'gobi', count: 9 },
|
||||
'ISLAND': { prefix: 'island', count: 9 },
|
||||
'SWAMP': { prefix: 'swamp', count: 9 },
|
||||
// startIndex: 变体索引起始值,默认为 0
|
||||
const TILE_VARIANTS: Record<string, { prefix: string, count: number, startIndex?: number }> = {
|
||||
// 从 0 开始的变体 (0-8)
|
||||
'GLACIER': { prefix: 'glacier', count: 9, startIndex: 0 },
|
||||
'MOUNTAIN': { prefix: 'mountain', count: 9, startIndex: 0 },
|
||||
'DESERT': { prefix: 'desert', count: 9, startIndex: 0 },
|
||||
'SNOW_MOUNTAIN': { prefix: 'snow_mountain', count: 9, startIndex: 0 },
|
||||
'FOREST': { prefix: 'forest', count: 9, startIndex: 0 },
|
||||
'GRASSLAND': { prefix: 'grassland', count: 9, startIndex: 0 },
|
||||
'RAINFOREST': { prefix: 'rainforest', count: 9, startIndex: 0 },
|
||||
'BAMBOO': { prefix: 'bamboo', count: 9, startIndex: 0 },
|
||||
'GOBI': { prefix: 'gobi', count: 9, startIndex: 0 },
|
||||
'ISLAND': { prefix: 'island', count: 9, startIndex: 0 },
|
||||
'SWAMP': { prefix: 'swamp', count: 9, startIndex: 0 },
|
||||
}
|
||||
|
||||
// 全局纹理缓存,避免重复加载
|
||||
@@ -83,8 +91,8 @@ export function useTextures() {
|
||||
|
||||
// Load Tile Variants
|
||||
const variantPromises: Promise<void>[] = []
|
||||
Object.entries(TILE_VARIANTS).forEach(([key, { prefix, count }]) => {
|
||||
for (let i = 1; i <= count; i++) {
|
||||
Object.entries(TILE_VARIANTS).forEach(([key, { prefix, count, startIndex = 0 }]) => {
|
||||
for (let i = startIndex; i < startIndex + count; i++) {
|
||||
const variantKey = `${key}_${i}`
|
||||
const url = `/assets/tiles/${prefix}_${i}.png`
|
||||
variantPromises.push(
|
||||
|
||||
@@ -19,20 +19,21 @@ function random(x: number, y: number): number {
|
||||
* 算法逻辑:
|
||||
* 1. 使用双重正弦波生成低频噪声(Biomes/群落感),让相邻的格子倾向于选择索引相近的变体。
|
||||
* 2. 叠加高频 Hash 扰动(Jitter),避免纹理过于死板或出现明显的人工波纹。
|
||||
* 3. 最终效果:变体会在地图上呈现自然的“斑块状”分布,而非杂乱的噪点。
|
||||
* 3. 最终效果:变体会在地图上呈现自然的"斑块状"分布,而非杂乱的噪点。
|
||||
*
|
||||
* @param x 地图 X 坐标
|
||||
* @param y 地图 Y 坐标
|
||||
* @param count 变体总数 (例如 9)
|
||||
* @returns 1 到 count 之间的整数索引
|
||||
* @param startIndex 变体索引起始值,默认为 0
|
||||
* @returns startIndex 到 startIndex + count - 1 之间的整数索引
|
||||
*/
|
||||
export function getClusteredTileVariant(x: number, y: number, count: number): number {
|
||||
if (count <= 1) return 1;
|
||||
export function getClusteredTileVariant(x: number, y: number, count: number, startIndex: number = 0): number {
|
||||
if (count <= 1) return startIndex;
|
||||
|
||||
// 1. 低频噪声 (Large Scale Noise)
|
||||
// 决定区域的主色调。Scale 越小,斑块越大。
|
||||
// 0.15 意味着大约 2PI / 0.15 ~= 40 格一个完整周期,
|
||||
// 视觉上大约 10-20 格为一个明显的“群落”。
|
||||
// 视觉上大约 10-20 格为一个明显的"群落"。
|
||||
const scale = 0.15;
|
||||
|
||||
// 使用两个不同频率和方向的波叠加,打破完美的对角线感
|
||||
@@ -55,13 +56,13 @@ export function getClusteredTileVariant(x: number, y: number, count: number): nu
|
||||
// 钳制到 [0, 1]
|
||||
finalValue = Math.max(0, Math.min(1, finalValue));
|
||||
|
||||
// 4. 映射到 [1, count]
|
||||
// 使用 Math.floor(val * count) 得到 0..count-1,再 +1
|
||||
let index = Math.floor(finalValue * count) + 1;
|
||||
// 4. 映射到 [startIndex, startIndex + count - 1]
|
||||
let index = Math.floor(finalValue * count) + startIndex;
|
||||
|
||||
// 边界保护 (防止浮点误差导致溢出)
|
||||
if (index > count) index = count;
|
||||
if (index < 1) index = 1;
|
||||
const maxIndex = startIndex + count - 1;
|
||||
if (index > maxIndex) index = maxIndex;
|
||||
if (index < startIndex) index = startIndex;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||