diff --git a/assets/tiles/bamboo_1.png b/assets/tiles/bamboo_1.png new file mode 100644 index 0000000..1f4957e Binary files /dev/null and b/assets/tiles/bamboo_1.png differ diff --git a/assets/tiles/bamboo_2.png b/assets/tiles/bamboo_2.png new file mode 100644 index 0000000..6127c67 Binary files /dev/null and b/assets/tiles/bamboo_2.png differ diff --git a/assets/tiles/bamboo_3.png b/assets/tiles/bamboo_3.png new file mode 100644 index 0000000..4a8d34e Binary files /dev/null and b/assets/tiles/bamboo_3.png differ diff --git a/assets/tiles/bamboo_4.png b/assets/tiles/bamboo_4.png new file mode 100644 index 0000000..e220f14 Binary files /dev/null and b/assets/tiles/bamboo_4.png differ diff --git a/assets/tiles/bamboo_5.png b/assets/tiles/bamboo_5.png new file mode 100644 index 0000000..d1814cd Binary files /dev/null and b/assets/tiles/bamboo_5.png differ diff --git a/assets/tiles/bamboo_6.png b/assets/tiles/bamboo_6.png new file mode 100644 index 0000000..2202aaa Binary files /dev/null and b/assets/tiles/bamboo_6.png differ diff --git a/assets/tiles/bamboo_7.png b/assets/tiles/bamboo_7.png new file mode 100644 index 0000000..dc5f705 Binary files /dev/null and b/assets/tiles/bamboo_7.png differ diff --git a/assets/tiles/bamboo_8.png b/assets/tiles/bamboo_8.png new file mode 100644 index 0000000..a15da4d Binary files /dev/null and b/assets/tiles/bamboo_8.png differ diff --git a/assets/tiles/bamboo_9.png b/assets/tiles/bamboo_9.png new file mode 100644 index 0000000..0d01711 Binary files /dev/null and b/assets/tiles/bamboo_9.png differ diff --git a/assets/tiles/gobi_1.png b/assets/tiles/gobi_1.png new file mode 100644 index 0000000..f11904d Binary files /dev/null and b/assets/tiles/gobi_1.png differ diff --git a/assets/tiles/gobi_2.png b/assets/tiles/gobi_2.png new file mode 100644 index 0000000..f90f631 Binary files /dev/null and b/assets/tiles/gobi_2.png differ diff --git a/assets/tiles/gobi_3.png b/assets/tiles/gobi_3.png new file mode 100644 index 0000000..803deec Binary files /dev/null and b/assets/tiles/gobi_3.png differ diff --git a/assets/tiles/gobi_4.png b/assets/tiles/gobi_4.png new file mode 100644 index 0000000..149336d Binary files /dev/null and b/assets/tiles/gobi_4.png differ diff --git a/assets/tiles/gobi_5.png b/assets/tiles/gobi_5.png new file mode 100644 index 0000000..c6174da Binary files /dev/null and b/assets/tiles/gobi_5.png differ diff --git a/assets/tiles/gobi_6.png b/assets/tiles/gobi_6.png new file mode 100644 index 0000000..1aa99d5 Binary files /dev/null and b/assets/tiles/gobi_6.png differ diff --git a/assets/tiles/gobi_7.png b/assets/tiles/gobi_7.png new file mode 100644 index 0000000..7f19021 Binary files /dev/null and b/assets/tiles/gobi_7.png differ diff --git a/assets/tiles/gobi_8.png b/assets/tiles/gobi_8.png new file mode 100644 index 0000000..976e8c2 Binary files /dev/null and b/assets/tiles/gobi_8.png differ diff --git a/assets/tiles/gobi_9.png b/assets/tiles/gobi_9.png new file mode 100644 index 0000000..5d75187 Binary files /dev/null and b/assets/tiles/gobi_9.png differ diff --git a/assets/tiles/island_1.png b/assets/tiles/island_1.png new file mode 100644 index 0000000..1c6134e Binary files /dev/null and b/assets/tiles/island_1.png differ diff --git a/assets/tiles/island_2.png b/assets/tiles/island_2.png new file mode 100644 index 0000000..e1041b1 Binary files /dev/null and b/assets/tiles/island_2.png differ diff --git a/assets/tiles/island_3.png b/assets/tiles/island_3.png new file mode 100644 index 0000000..841211c Binary files /dev/null and b/assets/tiles/island_3.png differ diff --git a/assets/tiles/island_4.png b/assets/tiles/island_4.png new file mode 100644 index 0000000..7e2830c Binary files /dev/null and b/assets/tiles/island_4.png differ diff --git a/assets/tiles/island_5.png b/assets/tiles/island_5.png new file mode 100644 index 0000000..49b350b Binary files /dev/null and b/assets/tiles/island_5.png differ diff --git a/assets/tiles/island_6.png b/assets/tiles/island_6.png new file mode 100644 index 0000000..17ee65c Binary files /dev/null and b/assets/tiles/island_6.png differ diff --git a/assets/tiles/island_7.png b/assets/tiles/island_7.png new file mode 100644 index 0000000..feee9dc Binary files /dev/null and b/assets/tiles/island_7.png differ diff --git a/assets/tiles/island_8.png b/assets/tiles/island_8.png new file mode 100644 index 0000000..1e31998 Binary files /dev/null and b/assets/tiles/island_8.png differ diff --git a/assets/tiles/island_9.png b/assets/tiles/island_9.png new file mode 100644 index 0000000..4e1aa8a Binary files /dev/null and b/assets/tiles/island_9.png differ diff --git a/assets/tiles/rainforest_1.png b/assets/tiles/rainforest_1.png new file mode 100644 index 0000000..24637c1 Binary files /dev/null and b/assets/tiles/rainforest_1.png differ diff --git a/assets/tiles/rainforest_2.png b/assets/tiles/rainforest_2.png new file mode 100644 index 0000000..21e8473 Binary files /dev/null and b/assets/tiles/rainforest_2.png differ diff --git a/assets/tiles/rainforest_3.png b/assets/tiles/rainforest_3.png new file mode 100644 index 0000000..8f0cef5 Binary files /dev/null and b/assets/tiles/rainforest_3.png differ diff --git a/assets/tiles/rainforest_4.png b/assets/tiles/rainforest_4.png new file mode 100644 index 0000000..5928686 Binary files /dev/null and b/assets/tiles/rainforest_4.png differ diff --git a/assets/tiles/rainforest_5.png b/assets/tiles/rainforest_5.png new file mode 100644 index 0000000..93121b4 Binary files /dev/null and b/assets/tiles/rainforest_5.png differ diff --git a/assets/tiles/rainforest_6.png b/assets/tiles/rainforest_6.png new file mode 100644 index 0000000..65aa787 Binary files /dev/null and b/assets/tiles/rainforest_6.png differ diff --git a/assets/tiles/rainforest_7.png b/assets/tiles/rainforest_7.png new file mode 100644 index 0000000..a623f40 Binary files /dev/null and b/assets/tiles/rainforest_7.png differ diff --git a/assets/tiles/rainforest_8.png b/assets/tiles/rainforest_8.png new file mode 100644 index 0000000..1d599d4 Binary files /dev/null and b/assets/tiles/rainforest_8.png differ diff --git a/assets/tiles/rainforest_9.png b/assets/tiles/rainforest_9.png new file mode 100644 index 0000000..613bd28 Binary files /dev/null and b/assets/tiles/rainforest_9.png differ diff --git a/assets/tiles/swamp_1.png b/assets/tiles/swamp_1.png new file mode 100644 index 0000000..1514835 Binary files /dev/null and b/assets/tiles/swamp_1.png differ diff --git a/assets/tiles/swamp_2.png b/assets/tiles/swamp_2.png new file mode 100644 index 0000000..1a01894 Binary files /dev/null and b/assets/tiles/swamp_2.png differ diff --git a/assets/tiles/swamp_3.png b/assets/tiles/swamp_3.png new file mode 100644 index 0000000..49cb6ea Binary files /dev/null and b/assets/tiles/swamp_3.png differ diff --git a/assets/tiles/swamp_4.png b/assets/tiles/swamp_4.png new file mode 100644 index 0000000..bd63ea0 Binary files /dev/null and b/assets/tiles/swamp_4.png differ diff --git a/assets/tiles/swamp_5.png b/assets/tiles/swamp_5.png new file mode 100644 index 0000000..7bc3c66 Binary files /dev/null and b/assets/tiles/swamp_5.png differ diff --git a/assets/tiles/swamp_6.png b/assets/tiles/swamp_6.png new file mode 100644 index 0000000..61071e8 Binary files /dev/null and b/assets/tiles/swamp_6.png differ diff --git a/assets/tiles/swamp_7.png b/assets/tiles/swamp_7.png new file mode 100644 index 0000000..13ead9f Binary files /dev/null and b/assets/tiles/swamp_7.png differ diff --git a/assets/tiles/swamp_8.png b/assets/tiles/swamp_8.png new file mode 100644 index 0000000..4fc234e Binary files /dev/null and b/assets/tiles/swamp_8.png differ diff --git a/assets/tiles/swamp_9.png b/assets/tiles/swamp_9.png new file mode 100644 index 0000000..74a7777 Binary files /dev/null and b/assets/tiles/swamp_9.png differ diff --git a/web/src/components/game/MapLayer.vue b/web/src/components/game/MapLayer.vue index dc5b4dc..b930e19 100644 --- a/web/src/components/game/MapLayer.vue +++ b/web/src/components/game/MapLayer.vue @@ -8,7 +8,7 @@ import type { RegionSummary } from '../../types/core' const TILE_SIZE = 64 const mapContainer = ref() -const { textures, isLoaded, loadSectTexture, loadCityTexture } = useTextures() +const { textures, isLoaded, loadSectTexture, loadCityTexture, getTileTexture } = useTextures() const worldStore = useWorldStore() // 动态水面相关变量 @@ -121,7 +121,7 @@ async function renderMap() { } // 处理普通地块 - let tex = textures.value[type] + let tex = getTileTexture(type, x, y) if (type === 'SECT') { // Legacy placeholder diff --git a/web/src/components/game/composables/useTextures.ts b/web/src/components/game/composables/useTextures.ts index e0c6b8a..62847d6 100644 --- a/web/src/components/game/composables/useTextures.ts +++ b/web/src/components/game/composables/useTextures.ts @@ -1,10 +1,20 @@ import { ref } from 'vue' import { Assets, Texture, TextureStyle } from 'pixi.js' import { gameApi } from '@/api/game' +import { getClusteredTileVariant } from '@/utils/procedural' // 设置全局纹理缩放模式为 nearest (像素风) TextureStyle.defaultOptions.scaleMode = 'nearest' +// 地形变体配置 +const TILE_VARIANTS: Record = { + '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 }, +} + // 全局纹理缓存,避免重复加载 const textures = ref>({}) const isLoaded = ref(false) @@ -71,6 +81,20 @@ export function useTextures() { } }) + // Load Tile Variants + const variantPromises: Promise[] = [] + Object.entries(TILE_VARIANTS).forEach(([key, { prefix, count }]) => { + for (let i = 1; i <= count; i++) { + const variantKey = `${key}_${i}` + const url = `/assets/tiles/${prefix}_${i}.png` + variantPromises.push( + Assets.load(url) + .then(tex => { textures.value[variantKey] = tex }) + .catch(e => console.warn(`Failed to load variant ${variantKey}`, e)) + ) + } + }) + // Load Clouds const cloudPromises: Promise[] = [] for (let i = 0; i <= 8; i++) { @@ -100,7 +124,7 @@ export function useTextures() { ) } - await Promise.all([...tilePromises, ...avatarPromises, ...cloudPromises]) + await Promise.all([...tilePromises, ...variantPromises, ...avatarPromises, ...cloudPromises]) isLoaded.value = true console.log('Base textures loaded') @@ -162,13 +186,30 @@ export function useTextures() { await Promise.all(slicePromises) } + // 获取地形纹理(支持随机变体) + const getTileTexture = (type: string, x: number, y: number): Texture | undefined => { + const variantConfig = TILE_VARIANTS[type] + if (variantConfig) { + // 使用噪声聚类算法替代纯随机 Hash + // 让变体在地图上呈现自然的群落分布,减少视觉噪点 + const index = getClusteredTileVariant(x, y, variantConfig.count) + const variantKey = `${type}_${index}` + + if (textures.value[variantKey]) { + return textures.value[variantKey] + } + } + return textures.value[type] + } + return { textures, isLoaded, loadBaseTextures, loadSectTexture, loadCityTexture, - availableAvatars + availableAvatars, + getTileTexture } } diff --git a/web/src/utils/procedural.ts b/web/src/utils/procedural.ts new file mode 100644 index 0000000..3fb98b0 --- /dev/null +++ b/web/src/utils/procedural.ts @@ -0,0 +1,68 @@ +/** + * 地形生成相关的过程生成算法 + * 用于解决纯随机带来的视觉杂乱问题 + */ + +/** + * 伪随机数生成器 (基于坐标) + * 返回 [0, 1] 之间的浮点数 + */ +function random(x: number, y: number): number { + const dot = x * 12.9898 + y * 78.233; + const sin = Math.sin(dot); + return (sin * 43758.5453) % 1; // frac +} + +/** + * 获取基于噪声聚类的地块变体索引 + * + * 算法逻辑: + * 1. 使用双重正弦波生成低频噪声(Biomes/群落感),让相邻的格子倾向于选择索引相近的变体。 + * 2. 叠加高频 Hash 扰动(Jitter),避免纹理过于死板或出现明显的人工波纹。 + * 3. 最终效果:变体会在地图上呈现自然的“斑块状”分布,而非杂乱的噪点。 + * + * @param x 地图 X 坐标 + * @param y 地图 Y 坐标 + * @param count 变体总数 (例如 9) + * @returns 1 到 count 之间的整数索引 + */ +export function getClusteredTileVariant(x: number, y: number, count: number): number { + if (count <= 1) return 1; + + // 1. 低频噪声 (Large Scale Noise) + // 决定区域的主色调。Scale 越小,斑块越大。 + // 0.15 意味着大约 2PI / 0.15 ~= 40 格一个完整周期, + // 视觉上大约 10-20 格为一个明显的“群落”。 + const scale = 0.15; + + // 使用两个不同频率和方向的波叠加,打破完美的对角线感 + const lowFreqNoise = Math.sin(x * scale + y * scale * 0.5) + + Math.cos(y * scale * 0.8 - x * scale * 0.3); + + // lowFreqNoise 范围约为 [-2, 2],归一化到 [0, 1] + const normalizedNoise = (lowFreqNoise + 2) / 4; + + // 2. 高频扰动 (Jitter) + // 如果完全依赖低频噪声,变体变化会像等高线一样生硬(1->2->3...)。 + // 引入扰动可以让边缘交错,且让同一群落内部也有少许变化。 + // range: [-0.5, 0.5] + const noiseVal = Math.abs(random(x, y)); // 0~1 + const jitter = (noiseVal - 0.5) * 0.5; // 强度系数 0.5 + + // 3. 混合计算 + let finalValue = normalizedNoise + jitter; + + // 钳制到 [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; + + // 边界保护 (防止浮点误差导致溢出) + if (index > count) index = count; + if (index < 1) index = 1; + + return index; +} +