Especialista em Geração Procedural de Conteúdo para Ambientes de Jogos

De Wikiprompt, a enciclopédia livre de prompts

loshu2000
Contribuído porloshu2000Fonte

29 de abr. de 2026

Especialista em Geração Procedural de Conteúdo para Ambientes de Jogos Atribua à IA o papel de especialista em PCG para projetar algoritmos de geração de ambientes de jogo não repetitivos, com uma primeira tarefa específica para um gerador de masmorras 2D.

Conteúdo do PromptSalvar

🌐
Atue como um Especialista em Geração Procedural de Conteúdo (PCG). Seu objetivo é projetar algoritmos para gerar ambientes de jogo não repetitivos. Você deve fornecer o pseudocódigo para o algoritmo de geração, a estrutura de dados para o sistema de grade/tilemap e a lógica para garantir a acessibilidade (por exemplo, verificações A* ou Flood Fill). Concentre-se em parâmetros como entropia, densidade e aleatoriedade baseada em semente. Não inclua elementos narrativos ou design de UI. Minha primeira solicitação é: "Crie um gerador de masmorras infinitas 2D usando Autômatos Celulares para paredes semelhantes a cavernas e uma lógica separada de BSP (Particionamento Binário de Espaço) para conectividade de salas." --- **Pseudocódigo do Algoritmo de Geração** ``` // Parâmetros globais seed = 12345 rng = new Random(seed) tileSize = 32 chunkSize = 64 // tamanho do chunk em tiles caveDensity = 0.45 // densidade inicial de paredes para CA smoothingIterations = 4 // iterações de suavização CA roomMinSize = 6 // tamanho mínimo de sala BSP roomMaxSize = 12 // tamanho máximo de sala BSP corridorWidth = 2 // largura do corredor // Estrutura de dados do tilemap (por chunk) struct Tile { int type; // 0 = vazio, 1 = parede, 2 = piso de sala, 3 = piso de corredor bool visited; // para flood fill } // Função principal: gera um chunk infinito baseado em coordenadas function generateChunk(int chunkX, int chunkY) -> Tile[chunkSize][chunkSize] { // Inicializa o chunk com base na semente derivada das coordenadas int localSeed = hash(seed, chunkX, chunkY) rng = new Random(localSeed) // Passo 1: Geração de cavernas via Autômatos Celulares Tile[chunkSize][chunkSize] caveMap = initializeCaveMap(rng) for (i = 0; i < smoothingIterations; i++) { caveMap = applyCellularAutomata(caveMap) } // Passo 2: Geração de salas via BSP List<Room> rooms = generateBSPRooms(rng, chunkSize, chunkSize) // Passo 3: Integração - sobrepõe salas no mapa de cavernas Tile[chunkSize][chunkSize] finalMap = caveMap for (room in rooms) { for (y = room.y; y < room.y + room.height; y++) { for (x = room.x; x < room.x + room.width; x++) { finalMap[x][y].type = 2 // piso de sala } } } // Passo 4: Conectividade - conecta salas com corredores (BSP tree) connectRooms(finalMap, rooms) // Passo 5: Verificação de acessibilidade (Flood Fill) if (!isReachable(finalMap)) { // Se falhar, ajusta com corredores adicionais ou remove blocos isolados addFallbackCorridors(finalMap, rooms) } return finalMap } // Função de Autômatos Celulares function initializeCaveMap(rng) -> Tile[chunkSize][chunkSize] { Tile[chunkSize][chunkSize] map for (y = 0; y < chunkSize; y++) { for (x = 0; x < chunkSize; x++) { if (rng.nextFloat() < caveDensity) { map[x][y].type = 1 // parede } else { map[x][y].type = 0 // vazio } } } return map } function applyCellularAutomata(Tile[chunkSize][chunkSize] map) -> Tile[chunkSize][chunkSize] { Tile[chunkSize][chunkSize] newMap = copy(map) for (y = 0; y < chunkSize; y++) { for (x = 0; x < chunkSize; x++) { int wallCount = countNeighborWalls(map, x, y) if (map[x][y].type == 1) { // Regra: parede sobrevive se tiver 4-7 vizinhos parede if (wallCount >= 4 && wallCount <= 7) { newMap[x][y].type = 1 } else { newMap[x][y].type = 0 } } else { // Regra: vazio vira parede se tiver 5+ vizinhos parede if (wallCount >= 5) { newMap[x][y].type = 1 } else { newMap[x][y].type = 0 } } } } return newMap } function countNeighborWalls(Tile[chunkSize][chunkSize] map, int x, int y) -> int { int count = 0 for (dy = -1; dy <= 1; dy++) { for (dx = -1; dx <= 1; dx++) { if (dx == 0 && dy == 0) continue int nx = x + dx int ny = y + dy // Trata bordas como parede (para consistência entre chunks) if (nx < 0 || nx >= chunkSize || ny < 0 || ny >= chunkSize) { count++ } else if (map[nx][ny].type == 1) { count++ } } } return count } // Função BSP para salas function generateBSPRooms(rng, int width, int height) -> List<Room> { // Cria árvore BSP recursivamente Node root = new Node(0, 0, width, height) splitNode(root, rng, 0) // profundidade máxima 4 List<Room> rooms = [] collectRooms(root, rng, rooms) return rooms } function splitNode(Node node, rng, int depth) { if (depth >= 4 || node.width < roomMinSize * 2 || node.height < roomMinSize * 2) { // Nó folha: cria sala int roomW = rng.nextInt(roomMaxSize - roomMinSize) + roomMinSize int roomH = rng.nextInt(roomMaxSize - roomMinSize) + roomMinSize int roomX = node.x + rng.nextInt(node.width - roomW) int roomY = node.y + rng.nextInt(node.height - roomH) node.room = new Room(roomX, roomY, roomW, roomH) return } // Divide horizontal ou verticalmente boolean splitHorizontal = rng.nextBoolean() if (splitHorizontal) { int splitY = node.y + rng.nextInt(node.height - roomMinSize * 2) + roomMinSize node.left = new Node(node.x, node.y, node.width, splitY - node.y) node.right = new Node(node.x, splitY, node.width, node.y + node.height - splitY) } else { int splitX = node.x + rng.nextInt(node.width - roomMinSize * 2) + roomMinSize node.left = new Node(node.x, node.y, splitX - node.x, node.height) node.right = new Node(splitX, node.y, node.x + node.width - splitX, node.height) } splitNode(node.left, rng, depth + 1) splitNode(node.right, rng, depth + 1) } function collectRooms(Node node, rng, List<Room> rooms) { if (node.room != null) { rooms.add(node.room) } else { collectRooms(node.left, rng, rooms) collectRooms(node.right, rng, rooms) } } // Conecta salas usando a árvore BSP (corredores entre nós irmãos) function connectRooms(Tile[chunkSize][chunkSize] map, List<Room> rooms) { // Para cada par de salas irmãs na árvore, cria corredor // (Simplificação: conecta salas em ordem sequencial) for (i = 0; i < rooms.size() - 1; i++) { Room roomA = rooms[i] Room roomB = rooms[i + 1] createCorridor(map, roomA, roomB) } } function createCorridor(Tile[chunkSize][chunkSize] map, Room a, Room b) { int startX = a.x + a.width / 2 int startY = a.y + a.height / 2 int endX = b.x + b.width / 2 int endY = b.y + b.height / 2 // Corredor em L: primeiro horizontal, depois vertical for (int x = min(startX, endX); x <= max(startX, endX); x++) { for (int w = 0; w < corridorWidth; w++) { int y = startY + w - corridorWidth / 2 if (y >= 0 && y < chunkSize) { map[x][y].type = 3 // piso de corredor } } } for (int y = min(startY, endY); y <= max(startY, endY); y++) { for (int w = 0; w < corridorWidth; w++) { int x = endX + w - corridorWidth / 2 if (x >= 0 && x < chunkSize) { map[x][y].type = 3 } } } } // Verificação de acessibilidade via Flood Fill function isReachable(Tile[chunkSize][chunkSize] map) -> bool { // Encontra primeiro piso (tipo 2 ou 3) int startX = -1, startY = -1 for (y = 0; y < chunkSize; y++) { for (x = 0; x < chunkSize; x++) { if (map[x][y].type >= 2) { startX = x startY = y break } } if (startX != -1) break } if (startX == -1) return false // sem piso // Flood Fill iterativo Stack<Point> stack = new Stack() stack.push(new Point(startX, startY)) map[startX][startY].visited = true int reachableCount = 0 int totalFloorCount = 0 // Conta total de pisos for (y = 0; y < chunkSize; y++) { for (x = 0; x < chunkSize; x++) { if (map[x][y].type >= 2) totalFloorCount++ } } while (!stack.isEmpty()) { Point p = stack.pop() reachableCount++ for (int dy = -1; dy <= 1; dy++) { for (int dx = -1; dx <= 1; dx++) { if (abs(dx) + abs(dy) != 1) continue // apenas 4 direções int nx = p.x + dx int ny = p.y + dy if (nx >= 0 && nx < chunkSize && ny >= 0 && ny < chunkSize) { if (map[nx][ny].type >= 2 && !map[nx][ny].visited) { map[nx][ny].visited = true stack.push(new Point(nx, ny)) } } } } } // Reseta visited para próximo uso for (y = 0; y < chunkSize; y++) { for (x = 0; x < chunkSize; x++) { map[x][y].visited = false } } return reachableCount == totalFloorCount } function addFallbackCorridors(Tile[chunkSize][chunkSize] map, List<Room> rooms) { // Se flood fill falhar, conecta salas diretamente com corredores adicionais for (i = 0; i < rooms.size(); i++) { for (j = i + 1; j < rooms.size(); j++) { if (!isReachable(map)) break createCorridor(map, rooms[i], rooms[j]) } } } // Estrutura de dados auxiliar struct Room { int x, y, width, height } struct Node { int x, y, width, height Node left, right Room room } struct Point { int x, y } ``` --- **Estrutura de Dados para o Sistema de Grade/Tilemap** ``` // Tilemap por chunk, com hash de semente para infinito class InfiniteTilemap { Map<ChunkCoord, Tile[chunkSize][chunkSize]> chunks int chunkSize function getTile(int worldX, int worldY) -> Tile { int chunkX = floor(worldX / chunkSize) int chunkY = floor(worldY / chunkSize) int localX = worldX mod chunkSize int localY = worldY mod chunkSize Tile[chunkSize][chunkSize] chunk = getOrGenerateChunk(chunkX, chunkY) return chunk[localX][localY] } function getOrGenerateChunk(int chunkX, int chunkY) -> Tile[chunkSize][chunkSize] { ChunkCoord key = new ChunkCoord(chunkX, chunkY) if (!chunks.containsKey(key)) { chunks.put(key, generateChunk(chunkX, chunkY)) } return chunks.get(key) } } struct ChunkCoord { int x, y // Implementar equals/hashCode para uso como chave } ``` --- **Lógica de Acessibilidade (A* ou Flood Fill)** - **Flood Fill** (usado acima): Verifica se todos os tiles de piso (tipo 2 ou 3) são alcançáveis a partir de um ponto inicial. Complexidade O(N), onde N é o número de tiles no chunk. - **A*** (alternativa para pathfinding em tempo real): Usado para encontrar caminhos entre salas específicas, mas não necessário para validação de geração. Para garantir acessibilidade global, o Flood Fill é mais eficiente. **Parâmetros de Controle** - **Entropia**: Controlada pela `caveDensity` (0.45) e pelas regras de CA (limiares 4-7 e 5). Aumentar `caveDensity` gera mais paredes, reduzindo entropia de espaços abertos. - **Densidade**: `roomMinSize` e `roomMaxSize` definem a densidade de salas. Corredores com `corridorWidth` controlam a densidade de conexões. - **Aleatoriedade baseada em semente**: `seed` global + `hash(seed, chunkX, chunkY)` garante que cada chunk seja determinístico e infinito, sem repetição global. **Notas de Implementação** - Para chunks vizinhos, as bordas do CA são tratadas como paredes para evitar inconsistências, mas isso pode criar desconexões entre chunks. Para resolver, considere gerar chunks com sobreposição de 1 tile e mesclar bordas. - A conectividade entre chunks é garantida por corredores que cruzam bordas, mas a verificação de acessibilidade deve ser feita em uma área maior (por exemplo, 3x3 chunks) para evitar ilhas isoladas.

Entre para ver o prompt completo

Continuar com:

Ao entrar, você concorda com nossos Termos de uso e Política de privacidade

Uso

Este prompt foi projetado para uso com coding. Copie o conteúdo acima e cole na sua ferramenta de IA preferida.

Para melhores resultados, personalize os marcadores (indicados por colchetes ou maiúsculas) com seus requisitos específicos.

Referências

Categorias:coding| prompts.chat| procedural-content-generation| game-development

Discussão

0 comentários