0

I started off by generating a tilemap using this method:

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

The problem with this is that it merely generated a map that was just randomly chosen tiles, and didn't generate a map that resembled a realistic island shape.

So I then decided to use Perlin noise to generate random island shapes like this: A randomly generated island with Perlin noise

This is the section of code which generates the 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]

This is the part of the code which I tried to make it so that it set the tiles in the tilemap to the right tiles.

    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()

The problem I have is that when I run the code, it is extremely laggy, and just displays my character over a screen of water tiles. I did try doing a separate code to set the tiles:

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

When I did this though, I got a strange error:

if color_world == blue:

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

I've been messing around with my code for ages and I can't see what I've done wrong - someone please help it would be greatly appreciated :)

Just a quick update, turns out it isn't rendering just a blue screen because I saw a sand tile rendered so it must be to do with the placement of the player, but it is extremely laggy and unplayable.

1 Answers1

0

Tricky to answer since your answer lacks a runnable example, but two thing:

if color_world == blue:

It's not strange that you get an error here. The error message tells you what's wrong:

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

You try to check if color_world equals blue, but color_world is a multidimensional list, while blue is a list of integers, so basically Python does now know what to. I suppose the line should be

if color_world[i][j] == blue

Also, remove this part:

 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))

out of your main loop.

Run it once before and draw the background to a new Surface, then use that new Surface in your main loop, like this:

...
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()

so you don't have to loop over every row and column in tilesettings.tilemap multiple times per second. You'll get the idea.

That should help you improve the performance.

sloth
  • 99,095
  • 21
  • 171
  • 219