I am trying to make a survival game and I have a problem with perlin noise. My program gives me this:
But I want something like islands or rivers.
Here's my code:
#SetUp#
import pygame, sys, random
pygame.init()
win = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Isom')
x = 0
y = 0
s = 0
tilel = list()
random.seed(5843)
MAP = [random.randint(0, 1) for _ in range(192)]
#Tiles#
class tile():
grass = pygame.image.load('Sprites/Images/Grass.png')
water = pygame.image.load('Sprites/Images/Water.png')
#Loop#
while True:
for key in pygame.event.get():
if key.type == pygame.QUIT:
pygame.quit()
sys.exit()
#World#
for a in range(12):
for b in range(16):
if MAP[s] == 0:
win.blit((tile.grass), (x, y))
elif MAP[s] == 1:
win.blit((tile.water), (x, y))
x += 50
s += 1
x = 0
y += 50
x = 0
y = 0
s = 0
#Update#
pygame.display.update()
image of randomly genrated terrain
code for perlin noise in pygame:
from PIL import Image
import numpy as np
from perlin_noise import PerlinNoise
import random
import pygame
pygame.init()
noise = PerlinNoise(octaves=6, seed=random.randint(0, 100000))
xpix, ypix = 500, 500
pic = [[noise([i/xpix, j/ypix]) for j in range(xpix)] for i in range(ypix)]
screen = pygame.display.set_mode ((500, 500), pygame.RESIZABLE)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
run = False
for i, row in enumerate(pic):
for j, column in enumerate(row):
if column>=0.6:
pygame.draw.rect(screen, (250, 250, 250), pygame.Rect(j, i, 1, 1))
elif column>=0.2:
pygame.draw.rect(screen, (80, 80, 80), pygame.Rect(j, i, 1, 1))
elif column>=0.09:
pygame.draw.rect(screen, (30, 90, 30), pygame.Rect(j, i, 1, 1))
elif column >=0.009:
pygame.draw.rect(screen, (10, 100, 10), pygame.Rect(j, i, 1, 1))
elif column >=0.002:
pygame.draw.rect(screen, (100, 150, 0), pygame.Rect(j, i, 1, 1))
elif column >=-0.06:
pygame.draw.rect(screen, (30, 190, 0), pygame.Rect(j, i, 1, 1))
elif column >=-0.02:
pygame.draw.rect(screen, (40, 200, 0), pygame.Rect(j, i, 1, 1))
elif column >=-0.1:
pygame.draw.rect(screen, (10, 210, 0), pygame.Rect(j, i, 1, 1))
elif column >=-0.8:
pygame.draw.rect(screen, (0, 0, 200), pygame.Rect(j, i, 1, 1))
#------------
#run the game class
pygame.display.update()
pygame.quit()
quit()
I recommend installing the noise package. Then use noise.pnoise1(x) for 1 dimensional Perlin noise, noise.pnoise2(x, y) for 2 dimensional Perlin noise, and noise.pnoise3(x, y, z) for 3 dimensional Perlin noise.
First, the critical thinking: Perlin is a popular term but the actual "Perlin" noise algorithm is old and visibly square-aligned. Better, as a general rule, to use a Simplex-type noise.
I suggest PyFastNoiseLite: https://github.com/tizilogic/PyFastNoiseLite Follow the install instructions, then mirror the C++ example in the FastNoiseLite documentation here: https://github.com/Auburn/FastNoiseLite/tree/master/Cpp Be sure to note its internal frequency multiplication, which you can change with SetFrequency(f)
You can also use the Python noise library for Simplex-type noise, with noise snoise2(x, y) though if you wish to use snoise3(x, y, z) I would first consider the info here: https://www.reddit.com/r/proceduralgeneration/comments/qr6snt/countdown_timer_simplex_patent_expiration/
Related
I am trying to make a simple 2d game map in Pygame but have run into a problem with the tile layout. I tried to make the map with a 2d array but it is not working. They are all appearing on one line. My goal is to make a simple 2d map via an array with just the rects. Here is the code.
import pygame
pygame.init()
root = pygame.display.set_mode((500,500))
pygame.display.set_caption("Game")
surface = pygame.Surface((60, 60), pygame.SRCALPHA)
rect = pygame.draw.rect(surface, (255, 0, 0), (50, 50, 50, 50)) , (50, 50)
surface_one = pygame.Surface((60, 60), pygame.SRCALPHA)
rect_one = pygame.draw.rect(surface_one, (0, 255, 0), (50, 50, 50, 50)) , (50, 50)
def main():
tileX = 0
tileY = 0
map = [[0,0,0,0],
[1,1,1,1],
[1,0,1,0]]
for x in map:
for x in x:
if x == 0:
root.blit(surface, [tileX, tileY])
tileX = tileX+16
if x == 1:
root.blit(surface_one, [tileX, tileY])
tileX = tileX+16
tileX = 0
tileY += 30
pygame.display.update()
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
main()
You must set tileX = 0 at the begin of the outer loop and increment tileY at the end of the outer loop and:
def main():
map = [[0,0,0,0],
[1,1,1,1],
[1,0,1,0]]
tileY = 0
for row in map:
tileX = 0
for x in row:
if x == 0:
root.blit(surface, [tileX, tileY])
if x == 1:
root.blit(surface_one, [tileX, tileY])
tileX += 16
tileY += 16
pygame.display.update()
However, you can simplify the code using enumerate:
def main():
map = [[0,0,0,0],
[1,1,1,1],
[1,0,1,0]]
for y, row in enumerate(map):
tileX = 0
for x, cell in enumerate(row):
image = surface_one if cell == 1 else surface
root.blit(image, [x*16, y*16])
pygame.display.update()
I want that when I click on one of the possible squares an X is printed.
I have made a list of all the coordinates of the tic-tac-toe grid. I've also added the centre points of each of the rectangles coordinates. I'm trying to make that if I click within the area of one of the squares that the an X button shows up. The eventual aim is to make that a double-click results in a lodged result.
import matplotlib.pyplot as plt
import pygame
import sys
import pygame
pygame.font.init()
size = 320, 240
black = 0, 0, 0
white = 255,255,255
red = 255, 0, 0
x1y1 = [(100, 0), (100, 300)]
x2y2 = [(200, 0), (200, 300)]
x3y3 = [(0, 100), (300, 100)]
x4y4 = [(0, 200), (300, 200)]
def centroid(coord1, coord2):
xx = 50
yy = 50
coords = []
for a in range(0,3):
for b in range(0,3):
if a + 1 == int(coord1) and b + 1 == int(coord2):
coords += tuple([xx + a*100, yy + b*100])
return tuple(coords)
def fourCorners(a,b,length,width):
center = (a, b)
corner3 = (int(a + length/2), int(b + width/2))
corner2 = (int(a + length/2), int(b - width/2))
corner4 = (int(a - length/2), int(b + width/2))
corner1 = (int(a - length/2), int(b - width/2))
return [corner1 ,corner2 ,corner3 ,corner4]
def withinRect(a,b,corners):
if len(corners) != 4:
print('Pass a list parameter of length 4.')
elif int(corners[0][0]) >= int(a) >= int(corners[1][0]) and int(corners[0][1]) >= int(b) >= int(corners[1][1]):
return True
screen = pygame.display.set_mode((300,300))
screen.fill(white)
pygame.draw.line(screen, (0, 0, 255), x1y1[0], x1y1[1], 3)
pygame.draw.line(screen, (0, 0, 255), x2y2[0], x2y2[1], 3)
pygame.draw.line(screen, (0, 0, 255), x3y3[0], x3y3[1], 3)
pygame.draw.line(screen, (0, 0, 255), x4y4[0], x4y4[1], 3)
while True:
ev = pygame.event.get()
for event in ev:
# handle get_pressed
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
x, y = event.pos
v = fourCorners(centroid(1,1)[0],centroid(1,1)[1],100,100)
button = pygame.Rect(v[1][0], v[1][1] ,100,100)
if (withinRect(x,y,v) == True and button.collidepoint(event.pos)):
print('X')
pygame.display.flip()
else:
break
pygame.display.flip()
Traverse all the 9 fields by 2 nested loos and define a pygame.Rect for the corresponding field:
for a in range(3):
for b in range(3):
button = pygame.Rect(a*100, b*100, 100, 100)
If you want you can define a margin, to limit the area to the center of a field:
margin = 4
button = pygame.Rect(a*100+margin, b*100+margin, 100-2*margin, 100-2*margin)
Use colliderect() to check if the click was inside the area:
if button.collidepoint(pos):
# [...]
Draw a line from the .bottomleft to the .topright and a line from the .topleft to the .bottomright, to form a cross:
pygame.draw.line(screen, (0, 0, 255), button.bottomleft, button.topright, 3)
pygame.draw.line(screen, (0, 0, 255), button.topleft, button.bottomright, 3)
ev = pygame.event.get()
for event in ev:
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
margin = 4
for a in range(3):
for b in range(3):
button = pygame.Rect(a*100+margin, b*100+margin, 100-2*margin, 100-2*margin)
if button.collidepoint(pos):
pygame.draw.line(screen, (0, 0, 255), button.bottomleft, button.topright, 3)
pygame.draw.line(screen, (0, 0, 255), button.topleft, button.bottomright, 3)
I finally figured out how to animate my sprite, but now I have a new problem. When running the game at my desired FPS (60), the character animation is way too quick. The animation looks smooth at around 10FPS, but the game looks choppy at that framerate. It is possible for my game to run at 60FPS, while the animation runs at a seperate FPS (ex. 10)? Any help appreciated!
Images and Sound FX Dowload
My code:
import pygame
import random
import time
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" %(0, 20)
pygame.init()
SIZE = W, H = 400, 700
screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()
# colours
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
BACKGROUND = (94, 194, 222)
STRIPE = (60, 160, 190)
LANELINE = (255, 255, 255)
x1 = 30
x2 = 330
lane1 = 30
lane2 = 130
lane3 = 230
lane4 = 330
y = 530
width = 40
height = 64
toggle1 = 0
toggle2 = 0
target_x1 = 30
target_x2 = 330
vel_x = 10
def drawScene():
screen.fill(BACKGROUND)
pygame.draw.polygon(screen, STRIPE, ((200, 700), (300, 700), (400, 600), (400, 500)))
pygame.draw.polygon(screen, STRIPE, ((0, 700), (100, 700), (400, 400), (400, 300)))
pygame.draw.polygon(screen, STRIPE, ((0, 500), (0, 600), (400, 200), (400, 100)))
pygame.draw.polygon(screen, STRIPE, ((0, 300), (0, 400), (400, 0), (300, 0)))
pygame.draw.polygon(screen, STRIPE, ((0, 100), (0, 200), (200, 0), (100, 0)))
pygame.draw.line(screen, LANELINE, (100, 0), (100, 700), 2)
pygame.draw.line(screen, LANELINE, (200, 0), (200, 700), 4)
pygame.draw.line(screen, LANELINE, (300, 0), (300, 700), 2)
mainsheet = pygame.image.load("dolphinSheet.png").convert()
sheetSize = mainsheet.get_size()
horiz_cells = 6
vert_cells = 1
cell_width = int(sheetSize[0] / horiz_cells)
cell_height = int(sheetSize[1] / vert_cells)
cellList = []
for vert in range(0, sheetSize[1], cell_height):
for horz in range(0, sheetSize[0], cell_width):
surface = pygame.Surface((cell_width, cell_height))
surface.blit(mainsheet, (0, 0),
(horz, vert, cell_width, cell_height))
colorkey = surface.get_at((0, 0))
surface.set_colorkey(colorkey)
cellList.append(surface)
cellPosition = 0
# main loop
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
pygame.mixer.music.load('percussiveHit.mp3')
pygame.mixer.music.play()
toggle1 += 1
if toggle1 % 2 == 1:
target_x1 += 100
else:
target_x1 -= 100
elif event.key == pygame.K_d:
pygame.mixer.music.load('percussiveHit.mp3')
pygame.mixer.music.play()
toggle2 += 1
if toggle2 % 2 == 1:
target_x2 -= 100
else:
target_x2 += 100
if x1 < target_x1:
x1 = min(x1 + vel_x, target_x1)
else:
x1 = max(x1 - vel_x, target_x1)
if x2 < target_x2:
x2 = min(x2 + vel_x, target_x2)
else:
x2 = max(x2 - vel_x, target_x2)
if cellPosition < len(cellList) - 1:
cellPosition += 1
else:
cellPosition = 0
drawScene()
pygame.draw.rect(screen, RED, (x1, y, width, height))
pygame.draw.rect(screen, RED, (x2, y, width, height))
screen.blit(cellList[cellPosition], (x1 + 4, y - 1))
screen.blit(cellList[cellPosition], (x2 + 4, y - 1))
# players
# screen.blit(playerImg, (x1 + 4, y - 5))
# screen.blit(playerImg, (x2 + 4, y - 5))
pygame.display.update()
pygame.quit()
Update the image based on a real-time millisecond delay, rather than every frame. Use pygame.time.get_ticks() (returns the number of milliseconds since pygame.init()) to update the image based on a specific time.
For example:
MS_FRAME_TIME = 100 # Mow many milliseconds a frame is shown for
...
last_paint_at = 0 # Start condition to ensure first paint
...
while True:
clock.tick(60)
ticks = pygame.time.get_ticks() # millliseconds since start
# If enough milliseconds have elapsed since the last frame, update!
if ( ticks - last_paint_at > MS_FRAME_TIME ):
last_paint_at = ticks
if ( cellPosition < len(cellList) - 1 ):
cellPosition += 1
else:
cellPosition = 0
screen.blit(cellList[cellPosition], (x1 + 4, y - 1))
screen.blit(cellList[cellPosition], (x2 + 4, y - 1))
If it fits your code, it may also be possible to simply look at the modulus of the time to select the animation frame.
I am trying to make a button in pygame. It is supposed to display three new images everytime it is clicked. Everything works, except these 3 images only appear while the button is clicked on. The code to display the images is below, it is part of a larger while loop which continues infinitely for now. How can i make it so that the images stay until the button is pressed again and new images appear? Any help is appreciated, thanks in advance.
mouse = pygame.mouse.get_pos ()
click = pygame.mouse.get_pressed ()
element = -1
if 1120 > mouse [0] > 865 and 330 > mouse [1] > 250:
screen.blit (dice_button_light, (870, 250))
if click [0] == 1:
dice_choices = Dice.dice_choice()
print(dice_choices)
element = -1
for i in dice_choices:
element += 1
x = 750
if element == 1:
x += 100
if element == 2:
x += 200
if i == 1:
screen.blit (dice1,(x,100))
elif i == 2:
screen.blit (dice2,(x,100))
elif i == 3:
screen.blit (dice3,(x,100))
elif i == 4:
screen.blit (dice4,(x,100))
elif i == 5:
screen.blit (dice5,(x,100))
elif i == 6:
screen.blit (dice6,(x,100))
else:
screen.blit (dice_button, (870, 250))
To cycle through different images, you can put them into a list and whenever the button rect is clicked, increment an index variable that represents the current image. In your while loop just use the index to get the correct image out of the list.
import sys
import pygame as pg
# Three images.
IMG1 = pg.Surface((100, 100))
IMG1.fill((240, 240, 240))
pg.draw.circle(IMG1, (0, 0, 0), (50, 50), 10)
IMG2 = pg.Surface((100, 100))
IMG2.fill((240, 240, 240))
pg.draw.circle(IMG2, (0, 0, 0), (25, 25), 10)
pg.draw.circle(IMG2, (0, 0, 0), (75, 75), 10)
IMG3 = pg.Surface((100, 100))
IMG3.fill((240, 240, 240))
pg.draw.circle(IMG3, (0, 0, 0), (20, 20), 10)
pg.draw.circle(IMG3, (0, 0, 0), (50, 50), 10)
pg.draw.circle(IMG3, (0, 0, 0), (80, 80), 10)
# Put the images into a list or tuple.
IMAGES = [IMG1, IMG2, IMG3]
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
bg_color = pg.Color(30, 40, 60)
# Use this rect for the collision detection and blitting.
img_rect = IMG1.get_rect(topleft=(200, 200))
img_index = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
if img_rect.collidepoint(event.pos):
img_index += 1
# Modulo to cycle between 0, 1, 2.
img_index %= len(IMAGES)
screen.fill(bg_color)
# Use the index to get the current image and blit
# it at the img_rect (topleft) position.
screen.blit(IMAGES[img_index], img_rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
It sounds like a simple thing to do , but I'm struggling.
I have a png image of a rectangle with a transparent centre called "zero". I want to change the colour of the visible part of the image. To try and change it, I have tried using "zero.fill" but no option I try changes only the non-transparent part, or it merges the new colour with the old and stays like that.
I'm not keen on installing numpy as I wish to take the finished program to a friend who doesn't have it. Any simple suggestions welcome.
I've got an example that works with per pixel alpha surfaces that can also be translucent. The fill function just loops over the surface's pixels and sets them to the new color, but keeps their alpha value. It's probably not recommendable to do this every frame with many surfaces. (Press f, g, h to change the color.)
import sys
import pygame as pg
def fill(surface, color):
"""Fill all pixels of the surface with color, preserve transparency."""
w, h = surface.get_size()
r, g, b, _ = color
for x in range(w):
for y in range(h):
a = surface.get_at((x, y))[3]
surface.set_at((x, y), pg.Color(r, g, b, a))
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
# Uncomment this for a non-translucent surface.
# surface = pg.Surface((100, 150), pg.SRCALPHA)
# pg.draw.circle(surface, pg.Color(40, 240, 120), (50, 50), 50)
surface = pg.image.load('bullet2.png').convert_alpha()
surface = pg.transform.rotozoom(surface, 0, 2)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_f:
fill(surface, pg.Color(240, 200, 40))
if event.key == pg.K_g:
fill(surface, pg.Color(250, 10, 40))
if event.key == pg.K_h:
fill(surface, pg.Color(40, 240, 120))
screen.fill(pg.Color('lightskyblue4'))
pg.draw.rect(screen, pg.Color(40, 50, 50), (210, 210, 50, 90))
screen.blit(surface, (200, 200))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
In this version, the visible and transparent parts are the other way round to the original question as per Ankur's suggestion.
Here is the essential working code:
import pygame, sys
from pygame.locals import *
pygame.init()
def load_image(name):
image = pygame.image.load(name).convert()
return image
def resize(obj, w, h):
global scale
return pygame.transform.scale(obj, (int(w * scale), int(h * scale)))
pink = (255, 0, 160)
red = (255, 0, 0)
peach = (255, 118, 95)
blue = (0, 0, 255)
blue_1 = (38, 0, 160)
dark_yellow = (255, 174, 0)
green = (38, 137, 0)
orange = (255, 81, 0)
colour = [pink, red, peach, blue, blue_1, dark_yellow, green, orange, green]
clock = pygame.time.Clock()
scale = 4
screen = pygame.display.set_mode((292 * scale, 240 * scale),0, 32)
banner = load_image("banner.png") #292x35
zero = load_image("zero.png") #5x7
c = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
rgb = colour[c]
c = c + 1
if c > 7:
c = 0
pygame.draw.line(banner, rgb, (53, 21), (53, 27), 5) #line the same size as zero image
banner.blit(zero, (51, 21)) #blit image with transparency over line
large_banner = resize(banner, 292, 35)
screen.blit(large_banner, (0, 0))
clock.tick(120)
pygame.display.flip()
pygame.quit()
sys.exit()
This code for drop all color brightness
def drop(surface):
w, h = surface.get_size()
for x in range(w):
for y in range(h):
r = surface.get_at((x, y))[0]
if r>150:
r-=50
g = surface.get_at((x, y))[1]
if g>150:
g-=50
b = surface.get_at((x, y))[2]
if b>150:
b-=50
a = surface.get_at((x, y))[3]
surface.set_at((x, y), pygame.Color(r, g, b, a))
img = pygame.image.load("image.png").convert_alpha()
drop(img)