我无法将我的Perlin噪声贴图生成器转换为游戏的pygame贴图

时间:2018-07-18 20:37:02

标签: python pygame perlin-noise

我首先使用这种方法生成了一个tilemap:

for rw in range(tilesettings.mapheight):
    for cl in range(tilesettings.mapwidth):
        randomNumber = random.randint(0,15)
        if randomNumber == 0:
            tile = tilesettings.COAL
        elif randomNumber == 1 or randomNumber == 2:
            tile = tilesettings.WATER
        elif randomNumber >= 3 and randomNumber <= 14:
            tile = tilesettings.GRASS
        else:
            tile = tilesettings.DIRT
        tilesettings.tilemap[rw][cl] = tile

这样做的问题是,它仅生成仅是随机选择的图块的地图,而没有生成类似于现实的岛屿形状的图。

因此,我决定使用Perlin噪声生成如下的随机岛形: A randomly generated island with Perlin noise

这是产生噪声的代码部分:

import pygame, sys
import noise
import numpy as np
from scipy.misc import toimage
from settings import Settings
from tilemap import Tilemap
from player import Player
from cursor import Cursor
from biome import Biome
from axe import Axe
import game_functions as gf
import random

def run_game():
    tilesettings = Tilemap()

    colours = {
        tilesettings.DIRT: tilesettings.BROWN,
        tilesettings.GRASS: tilesettings.GREEN,
        tilesettings.WATER: tilesettings.BLUE,
        tilesettings.COAL: tilesettings.BLACK,
        tilesettings.SAND : tilesettings.SAND,
        tilesettings.STONE: tilesettings.GREY,
        tilesettings.SNOW: tilesettings.WHITE,
    }

    resources = [tilesettings.DIRT, tilesettings.GRASS, 
tilesettings.WATER, tilesettings.COAL]
    shape = (500, 500)
    scale = 300
    octaves = 6
    persistence = 0.5
    lacunarity = 2.0
    seed = np.random.randint(0, 100)
    world = np.zeros(shape)
    for i in range(shape[0]):
        for j in range(shape[1]):
            world[i][j] = noise.pnoise2(i / scale,
                                        j / scale,
                                        octaves=octaves,
                                        persistence=persistence,
                                        lacunarity=lacunarity,
                                        repeatx=1024,
                                        repeaty=1024,
                                        base=seed)

    blue = [65, 105, 225]
    green = [34, 139, 34]
    beach = [238, 214, 175]
    snow = [255, 250, 250]
    mountain = [139, 137, 137]
    def add_color(world):
        color_world = np.zeros(world.shape + (3,))
        for i in range(shape[0]):
            for j in range(shape[1]):
                if world[i][j] < -0.05:
                    color_world[i][j] = blue
                elif world[i][j] < 0:
                    color_world[i][j] = beach
                elif world[i][j] < .20:
                    color_world[i][j] = green
                elif world[i][j] < 0.35:
                    color_world[i][j] = mountain
                elif world[i][j] < 1.0:
                    color_world[i][j] = snow

        return color_world

    color_world = add_color(world)
    a, b = shape[0] / 2, shape[1] / 2
    n = 1024
    r = 125
    y, x = np.ogrid[-a:n - a, -b:n - b]
    # creates a mask with True False values
    # at indices
    mask = x ** 2 + y ** 2 <= r ** 2

    black = [0, 0, 0]
    island_world = np.zeros_like(color_world)

    for i in range(shape[0]):
        for j in range(shape[1]):
            if mask[i][j]:
                island_world[i][j] = color_world[i][j]
            else:
                island_world[i][j] = black
    import math
    center_x, center_y = shape[1] // 2, shape[0] // 2
    circle_grad = np.zeros_like(world)
    for y in range(world.shape[0]):
        for x in range(world.shape[1]):
            distx = abs(x - center_x)
            disty = abs(y - center_y)
            dist = math.sqrt(distx * distx + disty * disty)
            circle_grad[y][x] = dist
    # get it between -1 and 1
    max_grad = np.max(circle_grad)
    circle_grad = circle_grad / max_grad
    circle_grad -= 0.5
    circle_grad *= 2.0
    circle_grad = -circle_grad

    # shrink gradient
    for y in range(world.shape[0]):
        for x in range(world.shape[1]):
            if circle_grad[y][x] > 0:
                circle_grad[y][x] *= 20

    # get it between 0 and 1
    max_grad = np.max(circle_grad)
    circle_grad = circle_grad / max_grad

    world_noise = np.zeros_like(world)

    for i in range(shape[0]):
        for j in range(shape[1]):
            world_noise[i][j] = (world[i][j] * circle_grad[i][j])
            if world_noise[i][j] > 0:
                world_noise[i][j] *= 20

    # get it between 0 and 1
    max_grad = np.max(world_noise)
    world_noise = world_noise / max_grad

    lightblue = [0, 191, 255]
    blue = [65, 105, 225]
    green = [34, 139, 34]
    darkgreen = [0, 100, 0]
    sandy = [210, 180, 140]
    beach = [238, 214, 175]
    snow = [255, 250, 250]
    mountain = [139, 137, 137]

这是我尝试制作的代码的一部分,以便将tilemap中的tile设置为正确的tile。

    threshold = 0.005
    def add_color2(world):
        color_world = np.zeros(world.shape + (3,))
        for i in range(shape[0]):
            for j in range(shape[1]):
                if world[i][j] < threshold + 0.05:
                    color_world[i][j] = blue
                    tile = tilesettings.WATER
                elif world[i][j] < threshold + 0.055:
                    color_world[i][j] = sandy
                    tile = tilesettings.SAND
                elif world[i][j] < threshold + 0.1:
                    color_world[i][j] = beach
                    tile = tilesettings.SAND
                elif world[i][j] < threshold + 0.25:
                    color_world[i][j] = green
                    tile = tilesettings.GRASS
                elif world[i][j] < threshold + 0.6:
                    color_world[i][j] = darkgreen
                    tile = tilesettings.GRASS
                elif world[i][j] < threshold + 0.7:
                    color_world[i][j] = mountain
                    tile = tilesettings.GRASS
                elif world[i][j] < threshold + 1.0:
                    color_world[i][j] = snow
                    tile = tilesettings.SNOW
                tilesettings.tilemap[i][j] = tile

        return color_world
    island_world_grad = add_color2(world_noise)
    toimage(island_world_grad).show()

    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, 
    ai_settings.screen_height))
    pygame.mouse.set_visible(True)

    player = Player(ai_settings, screen, tilesettings)
    cursor = Cursor(ai_settings, screen, tilesettings, player)

    axe = Axe(ai_settings, screen, tilesettings, cursor)
    while True:
        gf.check_events(ai_settings, screen, player, cursor, axe)
        player.update()
        cursor.update()
        gf.update_screen(ai_settings, screen, player)
        for row in range (tilesettings.mapheight):
            for column in range(tilesettings.mapwidth):
                pygame.draw.rect(screen, 
colours[tilesettings.tilemap[row][column]],(column* 
tilesettings.tilesize, row* tilesettings.tilesize, 
tilesettings.tilesize, tilesettings.tilesize))

        biome.update(screen)
        player.blitme()
        axe.changeimage()
        axe.blitme()
        pygame.display.update()

run_game()

我遇到的问题是,当我运行代码时,它非常笨拙,并且只在水瓦片屏幕上显示我的角色。我确实尝试执行单独的代码来设置图块:

color_world = np.zeros(world.shape + (3,))
for rw in range (shape[0]):
    for cl in range(shape[1]):
        if color_world == blue:
            tile = tilesettings.WATER
        elif color_world == sandy:
            tile = tilesettings.SAND
        elif color_world == beach:
            tile = tilesettings.SAND
        elif color_world == green:
            tile = tilesettings.GREEN
        elif color_world == darkgreen:
            tile = tilesettings.GRASS
        elif color_world == mountain:
            tile = tilesettings.STONE
        elif color_world == snow:
            tile = tilesettings.SNOW
        tilesettings.tilemap[rw][cl] = tile

当我这样做时,出现了一个奇怪的错误:

if color_world == blue:

ValueError:具有多个元素的数组的真值不明确。使用a.any()或a.all()

多年来我一直在弄乱我的代码,看不到我做错了什么,请有人帮忙,将不胜感激:)

快速更新,结果并不仅仅是渲染蓝屏,因为我看到了渲染的沙砖,这一定与播放器的位置有关,但它非常滞后且无法播放。

1 个答案:

答案 0 :(得分:0)

棘手的答案,因为您的答案缺少可运行的示例,但有两点:

if color_world == blue:

在这里出现错误并不奇怪。错误消息告诉您出了什么问题:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

您尝试检查color_world是否等于blue,但是color_world是多维列表,而blue是整数列表,因此Python现在基本上知道了什么至。我想这行应该是

if color_world[i][j] == blue

此外,删除此部分:

 for row in range (tilesettings.mapheight):
            for column in range(tilesettings.mapwidth):
                pygame.draw.rect(screen, 
colours[tilesettings.tilemap[row][column]],(column* 
tilesettings.tilesize, row* tilesettings.tilesize, 
tilesettings.tilesize, tilesettings.tilesize))

超出主循环范围。

先运行一次,然后将背景绘制到新的Surface,然后在主循环中使用新的Surface,如下所示:

...
background = pygame.Surface((tilesettings.mapwidth*tilesettings.tilesize, tilesettings.mapheight*tilesettings.tilesize))
 for row in range (tilesettings.mapheight):
            for column in range(tilesettings.mapwidth):
                pygame.draw.rect(background , 
                    colours[tilesettings.tilemap[row][column]],(column* 
                    tilesettings.tilesize, row* tilesettings.tilesize, 
                    tilesettings.tilesize, tilesettings.tilesize))
...
while True:
    gf.check_events(ai_settings, screen, player, cursor, axe)
    player.update()
    cursor.update()
    gf.update_screen(ai_settings, screen, player)
    screen.blit(background, (0, 0))
    biome.update(screen)
    player.blitme()
    axe.changeimage()
    axe.blitme()
    pygame.display.update()

因此您不必每秒每秒遍历tilesettings.tilemap中的每一行和每一列。您会明白的。

那应该可以帮助您提高性能。