In the game I'm trying to create, I want to spawn mummies outside of the screen and have them run towards the player. The problem I'm getting is that it won't take one variable for both the x and y coordinates. How can I make it so I can use just one variable for both the x and y coordinates?
screenx = 800
screeny = 600
class enemy():
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.alive = False
self.vel = 2
randomspawnabove = (random.randint(0, screenx), -100)
randomspawnbelow = (random.randint(0, screenx), (screeny + 100))
randomspawnleft = (-100, random.randint(0, screeny))
randomspawnright = ((screenx + 100), random.randint(0, screeny))
mummy_Spawn = [randomspawnleft, randomspawnright, randomspawnabove, randomspawnbelow]
mummy = enemy(random.choice(mummy_Spawn), 134, 134)
You can use the * operator to unpack the coordinates tuple:
mummy = enemy(*random.choice(mummy_Spawn), 134, 134)
Related
I started playing a little with pygame and so far I'm not doing badly. I encountered a problem and managed to solve it. The solution is possible. It is not 100% correct. I want to implement an eat method when the head of the subject meets the food.
It is probably a relatively simple method when the rest of the value of X and Y are equal to the head of the snake being eaten.
For some reason I was not able to fully understand the overlap of pixels and I am not really correct in the method.
The problem at the moment is that there is no overlap between the pixels and "eating is not done".
BACKGROUND = pygame.image.load("background.jpg")
WIDTH, HEIGHT = BACKGROUND.get_width(), BACKGROUND.get_height()
pygame.font.init()
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
def checkForEat(self):
head = self.body[-1]
x = self.food.getPos()[0]
y= self.food.getPos()[1]
# if abs(head[0] - x ) < 9 and abs(head[1] - y ) < 9: -- This is my temporary solution
if head[0] == x and head[1] == y:
self.food = Food()
self.eat()
I try not to add too much unnecessary code.
class Food:
def __init__(self):
self.color = (5, 5, 255)
self.pos = (random.randint(10,WIDTH-50),random.randint(10,HEIGHT-50))
def draw(self,win):
pygame.draw.circle(win,self.color, self.pos, 5)
def getPos(self):
return self.pos
class Snake:
START_POS = (85, 85)
def __init__(self):
self.food = Food()
self.block_size = 11
self.x , self.y = self.START_POS
self.body = self.create_body()
def create_body(self):
body = []
for i in range(self.length):
body.append((85,85+i*self.block_size))
return body
def draw(self,win):
WIN.blit(BACKGROUND, (0, 0))
self.food.draw(win)
for i in range(self.length):
pygame.draw.circle(win, (255, 0, 0), self.body[i], 5)
I'm not adding the rest of the program.
Just saying that apart from the problem I wrote above everything works fine.
Use pygame.Rect/pygame.Rect.colliderect to check if the bounding rectangle of the food overlaps with the head of the snake:
class Food:
def __init__(self):
self.color = (5, 5, 255)
self.pos = (random.randint(10,WIDTH-50),random.randint(10,HEIGHT-50))
def draw(self,win):
pygame.draw.circle(win,self.color, self.pos, 5)
def getPos(self):
return self.pos
def getRect(self):
return pygame.Rect(self.pos[0]-5, self.pos[1]-5, 10, 10)
class Snake:
START_POS = (85, 85)
def __init__(self):
self.food = Food()
self.block_size = 11
self.x , self.y = self.START_POS
self.body = self.create_body()
# [...]
def checkForEat(self):
head = self.body[-1]
head_rect = pygame.Rect(head[0]-5, head[1]-5, self.block_size, self.block_size)
food_rect = self.food.getRect()
if food_rect.colliderect(head_rect):
self.food = Food()
self.eat()
Also see How do I detect collision in pygame?.
Alternatively you can compute the Euclidean distance between the circle center of the circles and compare the distance to the sum of the radii:
class Snake:
# [...]
def checkForEat(self):
dx = self.food.getPos()[0] - self.body[-1][0]
dy = self.food.getPos()[1] - self.body[-1][1]
dist_center = math.hypot(dx, dy)
if dist_center <= 20:
self.food = Food()
self.eat()
I started learning Pygame and Tiled map editor. I have the following test:
Example
It looks fine in the editor, the objects that have the black BG are currently on layer_4 (but they do it regardless of layer count).
I already tried using convert_alpha on it, ticking and unticking the transparency color in Tiled when loading in the tile map.
This is the original picture:
Full picture
This is the part where I load in the picture:
def import_cut_graphics(path):
surface = pygame.image.load(path).convert_alpha()
tile_num_x = int(surface.get_size()[0] / TILESIZE)
tile_num_y = int(surface.get_size()[1] / TILESIZE)
cut_tiles = []
for row in range(tile_num_y):
for col in range(tile_num_x):
x = col * TILESIZE
y = row * TILESIZE
new_surface = pygame.Surface((TILESIZE, TILESIZE))
new_surface.blit(surface, (0, 0), pygame.Rect(x, y, TILESIZE, TILESIZE))
cut_tiles.append(new_surface)
return cut_tiles
And this is where I work with it:
class Level:
def __init__(self, level_data, surface):
# general setup
self.all_layers = import_cut_graphics("img\ProjectUtumno_full.png")
self.display_surface = surface
self.world_shift = 0
# terrain setup
layer1_layout = import_csv_layout(level_data["layer_1"])
self.layer1_sprites = self.create_tile_group(layer1_layout, 'layer_1')
# grass setup
layer2_layout = import_csv_layout(level_data["layer_2"])
self.layer2_sprites = self.create_tile_group(layer2_layout, "layer_2")
# crates
layer3_layout = import_csv_layout(level_data["layer_3"])
self.layer3_sprites = self.create_tile_group(layer3_layout, "layer_3")
# layer 4
layer4_layout = import_csv_layout(level_data["layer_4"])
self.layer4_sprites = self.create_tile_group(layer4_layout, "layer_4")
def create_tile_group(self, layout, type):
sprite_group = pygame.sprite.Group()
for row_index, row in enumerate(layout):
for col_index, val in enumerate(row):
if val != '-1':
x = col_index * TILESIZE
y = row_index * TILESIZE
if type == 'layer_1':
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
if type == "layer_2" :
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
if type == "layer_3" :
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
if type == "layer_4" :
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
return sprite_group
def run(self):
# run the entire game / level
self.camera()
# layer1
self.layer1_sprites.update(self.world_shift)
self.layer1_sprites.draw(self.display_surface)
# layer2
self.layer2_sprites.update(self.world_shift)
self.layer2_sprites.draw(self.display_surface)
# layer3
self.layer3_sprites.update(self.world_shift)
self.layer3_sprites.draw(self.display_surface)
# layer4
self.layer4_sprites.update(self.world_shift)
self.layer4_sprites.draw(self.display_surface)
This is my main game loop:
class Game:
def __init__(self):
pygame.init()
pygame.font.init()
self.screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
self.clock = pygame.time.Clock()
self.screen_name = pygame.display.set_caption("New Game")
self.running = True
self.playing = False
self.level = Level(level_0, self.screen)
self.character_spritesheet = Spritesheet('img\MainCharacter\B_witch_idle.png')
def main(self):
# game loop
while self.running:
self.events()
self.update()
self.draw()
self.running = False
def draw(self):
self.screen.fill(BLACK)
self.level.run()
self.all_sprites.draw(self.screen)
self.clock.tick(FPS)
pygame.display.update()
I did try commenting the screen fill black and got the same result. I tried out set_colorkey with white/black colors but didn't work out.
I add my tile create class too:
class Tile(pygame.sprite.Sprite):
def __init__(self, size, x, y):
super().__init__()
self.image = pygame.Surface((size, size))
self.rect = self.image.get_rect(topleft=(x, y))
def update(self, shift):
self.rect.x += shift
class StaticTile(Tile):
def __init__(self, size, x, y, surface):
super().__init__(size, x, y)
self.image = surface
Thanks in advance if you can figure out why i have this problem.
So for anyone who's having this problem, I managed to solve it by this:
Inside my import_cut_graphics methode I added a set_colorkey(BLACK), turns out the problem occured when I was originally cutting the picture, therefore it didnt matter how much I change it at the latter phases since the problem was in an earlier stage.
def import_cut_graphics(path):
surface = pygame.image.load(path).convert_alpha()
tile_num_x = int(surface.get_size()[0] / TILESIZE)
tile_num_y = int(surface.get_size()[1] / TILESIZE)
cut_tiles = []
for row in range(tile_num_y):
for col in range(tile_num_x):
x = col * TILESIZE
y = row * TILESIZE
new_surface = pygame.Surface((TILESIZE, TILESIZE))
new_surface.blit(surface, (0, 0), pygame.Rect(x, y, TILESIZE, TILESIZE))
new_surface.set_colorkey(BLACK)
cut_tiles.append(new_surface)
return cut_tiles
The enemy are being generated from above the screen and then move toward player in the middle, I want to generate enemies randomly around the screen from all directions but not inside the screen directly and proceed to move towards the player and also enemy sprites are sometimes joining combining and moving together how to repel the enemy sprites.
I have tried changing x,y coordinates of enemy objects using a random range but sometimes they generate objects inside the play screen, I want enemies to generate outside the playing window.
class Mob(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('enemy.png').convert_alpha()
self.image = pg.transform.smoothscale(pg.image.load('enemy.png'), (33, 33))
self.image_orig = self.image.copy()
self.radius = int(29 * .80 / 2)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = 4
self.rot = 0
self.rot_speed = 5
self.last_update = pg.time.get_ticks()
def rotate(self):
now = pg.time.get_ticks()
if now - self.last_update > 50:
self.last_update = now
self.rot = (self.rot + self.rot_speed) % 360
new_image = pg.transform.rotozoom(self.image_orig, self.rot, 1)
old_center = self.rect.center
self.image = new_image
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
self.rotate()
dirvect = pg.math.Vector2(rotator.rect.x - self.rect.x,
rotator.rect.y- self.rect.y)
if dirvect.length_squared() > 0:
dirvect = dirvect.normalize()
# Move along this normalized vector towards the player at current speed.
if dirvect.length_squared() > 0:
dirvect.scale_to_length(self.speed)
self.rect.move_ip(dirvect)
if self.rect.top > height + 10 or self.rect.left < -25 or self.rect.right > width + 20:
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = random.randrange(1, 4)
[UPDATE]
This the remaining code:
import math
import random
import os
import pygame as pg
import sys
pg.init()
height = 650
width = 1200
os_x = 100
os_y = 45
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (os_x, os_y)
screen = pg.display.set_mode((width, height), pg.NOFRAME)
screen_rect = screen.get_rect()
background = pg.image.load('background.png').convert()
background = pg.transform.smoothscale(pg.image.load('background.png'), (width, height))
clock = pg.time.Clock()
running = True
font_name = pg.font.match_font('Bahnschrift', bold=True)
def draw_text(surf, text, size, x, y, color):
[...]
class Mob(pg.sprite.Sprite):
[...]
class Rotator(pg.sprite.Sprite):
def __init__(self, screen_rect):
pg.sprite.Sprite.__init__(self)
self.screen_rect = screen_rect
self.master_image = pg.image.load('spaceship.png').convert_alpha()
self.master_image = pg.transform.smoothscale(pg.image.load('spaceship.png'), (33, 33))
self.radius = 12
self.image = self.master_image.copy()
self.rect = self.image.get_rect(center=[width / 2, height / 2])
self.delay = 10
self.timer = 0.0
self.angle = 0
self.distance = 0
self.angle_offset = 0
def get_angle(self):
mouse = pg.mouse.get_pos()
offset = (self.rect.centerx - mouse[0], self.rect.centery - mouse[1])
self.angle = math.degrees(math.atan2(*offset)) - self.angle_offset
old_center = self.rect.center
self.image = pg.transform.rotozoom(self.master_image, self.angle, 1)
self.rect = self.image.get_rect(center=old_center)
self.distance = math.sqrt((offset[0] * offset[0]) + (offset[1] * offset[1]))
def update(self):
self.get_angle()
self.display = 'angle:{:.2f} distance:{:.2f}'.format(self.angle, self.distance)
self.dx = 1
self.dy = 1
self.rect.clamp_ip(self.screen_rect)
def draw(self, surf):
surf.blit(self.image, self.rect)
def shoot(self, mousepos):
dx = mousepos[0] - self.rect.centerx
dy = mousepos[1] - self.rect.centery
if abs(dx) > 0 or abs(dy) > 0:
bullet = Bullet(self.rect.centerx, self.rect.centery, dx, dy)
all_sprites.add(bullet)
bullets.add(bullet)
There's not much informations to go by here, but you probably need to check the x and y range your play window has and make sure the random spawn coordinates you generate are outside of it:
In your init:
# These are just example min/max values. Maybe pass these as arguments to your __init__ method.
min_x = min_y = -1000
max_x = max_y = 1000
min_playwindow_x = min_playwindow_y = 500
max_playwindow_x = max_playwindow_y = 600
self.x = (random.randrange(min_x, min_playwindow_x), random.randrange(max_playwindow_x, max_x))[random.randrange(0,2)]
self.y = (random.randrange(min_y, min_playwindow_y), random.randrange(max_playwindow_y, max_y))[random.randrange(0,2)]
This solution should work in basically any setup. For x and y it generates a tuple of values outside the playing window. Then a coinflip decides on the value. This will only spawn mobs that are diagonally outside the playing field, but it will always generate valid random coordinates.
Another approach would be just generating as many random variables as needed to get a valid pair like this:
while min_playingwindow_x <= self.x <= max_playingwindow_x and
min_playingwindow_y <= self.y <= max_playingwindow_y:
# While within screen(undesired) calculate new random positions
self.x = random.randrange(min_x, max_x)
self.y = random.randrange(min_y, max_y)
This can be really slow however if your valid amount of positions is (for example) only 1% of the total positions.
IF you need something really fleshed out, you need to know the corners of both your map and the rectangle that is actually displayed, which is I assume smaller than the entire map(otherwise you cannot spawn enemies outside your view.
(0,0)
+----------------------+
| A |
|-----+-----------+----|
| D | W | B |
|-----+-----------+----|
| C |
+----------------------+(max_x, max_y)
In this diagram W is the window that is acutally visible to the player, and A,B,C,D together are the part of your map that is not currently visible. Since you only want to spawn mobs outside the player's view, you'll need to make sure that the coordinates you generate are inside your map and outside your view:
def generate_coordinates_outside_of_view(map_width=1000, map_height=1000, view_window_top_left=(100, 100),
view_width=600, view_height=400):
"""
A very over the top way to generate coordinates outside surrounding a rectangle within a map almost without bias
:param map_width: width of map in pixels (note that 0,0 on the map is top left)
:param map_height: height of map in pixels
:param view_window_top_left: top left point(2-tuple of ints) of visible part of map
:param view_width: width of view in pixels
:param view_height: height of view in pixels
"""
from random import randrange
# generate 2 samples for each x and y, one guaranteed to be random, and one outside the view for sure.
x = (randrange(0, map_width), (randrange(0, view_window_top_left[0]),
randrange(view_window_top_left[0] + view_width, map_width))[randrange(0, 2)])
y = (randrange(0, map_height), (randrange(0, view_window_top_left[1]),
randrange(view_window_top_left[1] + view_height, map_height))[randrange(0, 2)])
# now we have 4 values. To get a point outside our view we have to return a point where at least 1 of the
# values x/y is guaranteed to be outside the view.
if randrange(0, 2) == 1: # to be almost completely unbiased we randomize the check
selection_x = randrange(0, 2)
selection_y = randrange(0, 2) if selection_x == 1 else 1
else:
selection_y = randrange(0, 2)
selection_x = randrange(0, 2) if selection_y == 1 else 1
return x[selection_x], y[selection_y]
HTH
trying to get the ball to move along the y coordinate, it wont work, more explanation at bottom
from livewires import games, color
games.init(screen_width = 640, screen_height = 480, fps = 50)
points = games.Text(value = 0, size = 25, color = color.green,
bottom = games.screen.height - 5, left = 10)
games.screen.add(points)
class Paddle(games.Sprite):
image = games.load_image("paddle.bmp")
def __init__(self):
super(Paddle, self).__init__(image = Paddle.image, y = games.mouse.y, right = games.screen.width)
def update(self):
""" Move to mouse x position. """
self.y = games.mouse.y
if self.top < 0:
self.top = 0
if self.bottom > games.screen.height:
self.bottom = games.screen.height
self.check_bounce()
def check_bounce(self):
for bouncingBall in self.overlapping_sprites:
bouncingBall.handle_bounce()
class BouncingBall(games.Sprite):
image = games.load_image("ball.bmp")
def __init__(self, x, y, dx, dy):
super(BouncingBall, self).__init__(image = BouncingBall.image, x = x, y = y, dx = dx, dy = dy)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.top > 0 or self.bottom < games.screen.height:
self.dy = -self.dy
if self.left < 0:
self.dx = -self.dx
if self.right > games.screen.width:
self.end_game()
def handle_bounce(self):
global points
points.value += 10
points.left = 10
self.dx = -self.dx
def end_game(self):
end_message = games.Message(value = "GAME OVER",
size = 90,
color = color.red,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 10 * games.screen.fps,
after_death = games.screen.quit)
games.screen.add(end_message)
def main():
background_image = games.load_image("background.png", transparent = False)
games.screen.background = background_image
the_paddle = Paddle()
games.screen.add(the_paddle)
games.mouse.is_visible = False
new_ball = BouncingBall(x = games.screen.width/2, y = games.screen.height/2, dx = 2, dy = 2) #<-believe it is right here im messing up
games.screen.add(new_ball)
games.screen.mainloop()
main()
I am having a horrible time at getting my ball to correctly follow the y coordinate, I believe I am doing it wrong when i create instance new_ball, (main function) but i have no idea lol, anyone see what im doing wrong?
Make sure that this line
if self.top > 0 or self.bottom < games.screen.height:
self.dy = -self.dy
Isn't constantly evaluating to True. If so, your y-velocity will constantly toggle and the ball will never appear to change y coordinate.
This question already has an answer here:
Nested arguments not compiling
(1 answer)
Closed 8 years ago.
I'm just learning python/pygame and I'm doing a physics tutorial. Unfortunately, I think it was made in an older version of python. I copied the code in the video exactly, but when I run it it returns "Failed to run script - syntax error - invalid syntax(physics.py, line 7)". I'm sure it's just something stupid and obvious that I'm missing, but any answer would go a long way for me!
import os, sys, math, pygame, pygame.mixer
from pygame.locals import *
screen_size = screen_width, screen_height = 600, 400
class MyCircle:
def __init__(self, (x, y), size, color = (255,255,255), width = 1):
self.x = x
self.y = y
self.size = size
self.color = color
self.width = width
def display(self):
pygame.draw.circle(screen, self.color, (self.x, self.y), self.size, self.width)
screen = pygame.display.set_mode(screen_size)
my_circle = MyCircle((100,100), 10, red)
my_circle_2 = MyCircle((200,200), 30, blue)
my_circle_3 = MyCircle((300,150), 40, green, 4)
my_circle_4 = MyCircle((450,250), 120, black, 0)
fps_limit = 60
run_me = True
while run_me:
clock.tick(fps_limit)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run_me = False
my_circle.display()
my_circle_2.display()
my_circle_3.display()
my_circle_4.display()
pygame.display.flip()
pygame.quit()
sys.exit()
You're probably using Python 3. Tuple parameter unpacking was removed in 3.X, so you have to change this:
def __init__(self, (x, y), size, color = (255,255,255), width = 1):
self.x = x
self.y = y
self.size = size
self.color = color
self.width = width
To:
def __init__(self, position, size, color = (255,255,255), width = 1):
self.x, self.y = position
self.size = size
self.color = color
self.width = width
Well, line 7 is this line:
def __init__(self, (x, y), size, color = (255,255,255), width = 1):
See that (x, y) in the middle of the parameter list? This is a feature called "tuple parameter unpacking", and it was removed in Python 3.0. (It's also discouraged in Python 2.6-2.7, but it works, so some people still use it.)
PEP 3113 has the full details.
If you run the 2to3 tool on your script, it'll tell you how to fix it.
But the simple fix is to replace that tuple (x, y) with a single parameter xy, and split it inside the function:
def __init__(self, xy, size, color = (255,255,255), width = 1):
self.x, self.y = xy
self.size = size
self.color = color
self.width = width