This question already has answers here:
How to rotate an image around its center while its scale is getting larger(in Pygame)
(2 answers)
Closed 2 years ago.
Okay so Im just trying to get it working with rotations first before scaling then once I nail that the rotozoom should be easy. For the life of me I cant seem to get it working. Heres a simple class that I want the object to rotate over time as well a transform in the x direction. The transform is working fine but i just cant get it to rotate.
class Puff(pygame.sprite.Sprite):
def __init__(self, screen):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
try:
self.imagePuff = pygame.image.load(os.path.join("Images","puff.tga")).convert()
except:
raise UserWarning, "Unable to find Puff image"
self.imagePuff.set_colorkey((0,0,255))
self.image = self.imagePuff
self.rect = self.image.get_rect()
self.x = self.screen.get_width()/3
self.y = self.screen.get_height()/3+10
self.rect.center = (self.x, self.y)
self.lifespan = 60
self.speed = 1
self.count = 0
self.angle = 0
def update(self):
self.calcPos()
self.rect.center = self.x, self.y
self.transform()
if self.count > self.lifespan:
self.kill()
def calcPos(self):
self.x -= 5
self.y = self.y
def transform(self):
self.count += 1.1
self.angle += self.count*25
self.newTrans = pygame.transform.scale(self.copyImage, (400,400))
self.newRect = self.newTrans.get_rect()
self.newRect.center = self.rect.center
This is how I've made it so you can rotate your puff class:
import pygame
class Puff(pygame.sprite.Sprite):
def __init__(self, screen):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
try:
self.imagePuff = pygame.image.load("puff.jpg").convert()
except:
raise UserWarning, "Unable to find Puff image"
self.image = self.imagePuff
self.imagePuff.set_colorkey((0,0,255))
self.rect = self.image.get_rect()
self.x = self.screen.get_width()/3
self.y = self.screen.get_height()/3+10
self.rect.center = (self.x, self.y)
self.lifespan = 60
self.speed = 1
self.count = 0
self.angle = 0
def update(self):
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imagePuff, self.angle)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def calcPos(self):
self.x -= 5
self.y = self.y
def turnLeft(self):
self.angle = (self.angle + 45) % 360
def turnRight(self):
self.angle = (self.angle - 45) % 360
if __name__ == "__main__":
pygame.init()
screen = pygame.display.set_mode((400,300))
clock = pygame.time.Clock()
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
pygame.display.set_caption("Spinning sprite!!!")
ball = Puff(screen)
sprites = pygame.sprite.Group()
sprites.add(ball)
keepGoing = True
while keepGoing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
ball.turnRight()
sprites.clear(screen, background)
sprites.update()
sprites.draw(screen)
pygame.display.flip()
clock.tick(30)
Essentially the key points are:
self.image = pygame.transform.rotate(self.imagePuff, self.angle)
In the update method, and the two methods for changing the angle:
def turnLeft(self):
self.angle = (self.angle + 45) % 360
def turnRight(self):
self.angle = (self.angle - 45) % 360
In the code above every clock tick the sprite is rotated to the right:
ball.turnRight()
As you omitted your transform method, I had to edit the code to show how to do just rotations.
Hope this helps and if you need any more help please leave a comment.
Edit:
if you want to rotate and move to the left then you only really need to add this:
def update(self):
self.calcPos() # This moves the position - 5 in the x direction
self.rect.center = self.x, self.y
self.rotate() # This calls the bit to change the angle
if self.count > self.lifespan:
self.kill()
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imagePuff, self.angle) # This rotates
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def calcPos(self):
self.x -= 5
def rotate(self):
self.angle = (self.angle + 45) % 360
You should check out:
http://www.gamedev.net/topic/444490-pygame-easy-as-py/
If you search for "rotate.py" you'll see the a section of code on rotating sprites
EDIT:
Try changing you transform method to:
def transform(self):
self.count += 1.1
self.angle += (self.count*25) % 360
self.copyImage = pygame.transform.rotate(self.imagePuff, self.angle)
self.newTrans = pygame.transform.scale(self.copyImage, (400,400))
self.image = self.newTrans
Related
This question already has answers here:
How can I make a sprite move when key is held down
(6 answers)
Closed last month.
Good day, I am writing a pygame program that uses sockets. Right now I am just trying to get the rectangles to move in the x-axis and I keep getting this error
self.rect.x += self.dx
AttributeError: 'tuple' object has no attribute 'x' ".
My goal is just to move the rect left and right. using the move method below.
import pygame
class Player():
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.rect = pygame.Rect((x, y, width, height))
self.vel = 3
self.dx = 0
self.dy = 0
self.jump = False
def draw(self, win):
pygame.draw.rect(win, self.color, self.rect)
def move(self, screen_width, screen_height):
SPEED = 10
dx = 0
dy = 0
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.dx = -SPEED
if keys[pygame.K_RIGHT]:
self.dx = SPEED
self.update()
def update(self):
# update player position
self.rect.x += self.dx
self.rect.y += self.dy
self.rect = (self.x, self.y, self.width, self.height)
self.dx = 0
self.dy = 0
I see that you already created a pygame.Rect object and updated it correct, why recreate that object?
Change this:
def update(self):
# update player position
self.rect.x += self.dx
self.rect.y += self.dy
self.rect = (self.x, self.y, self.width, self.height)
self.dx = 0
self.dy = 0
To:
def update(self):
# update player position
self.rect.x += self.dx
self.rect.y += self.dy
self.dx = 0
self.dy = 0
Also you might want to remove this as it doesn't do anything:
self.x = x
self.y = y
Unless you need the position where it was first created
So, I'm making my first game in pygame, and have done OK up to this point. I've been looking at many tutorials but they only show me how to move the object using keys. I just can't make my object move randomly in random directions. Can I please get some help?
import pygame
import random
#create display
screen_h = 500
screen_w = 500
points = 0
bombplacex = random.randint(1,325)
bombplacey = random.randint(1,325)
strawplacex = random.randint(1,325)
strawplacey = random.randint(1,325)
pepperplacex = random.randint(1,325)
pepperplacey = random.randint(1,325)
screen = pygame.display.set_mode((screen_h, screen_w))
pygame.display.set_caption('Button')
# load button image
bomb_img = pygame.image.load('bomb.png').convert_alpha()
straw_img = pygame.image.load('strawberry-png.png').convert_alpha()
pepper_img = pygame.image.load('green pepper.png').convert_alpha()
# button class
class Button():
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pygame.transform.scale(image, (int(width * scale),int(height * scale)))
self.rect = self.image.get_rect()
self.rect.topleft = (x,y)
self.clicked = False
def draw(self):
action = False
# get mouse position
position = pygame.mouse.get_pos()
# check mouseover and click conditions
if self.rect.collidepoint(position):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
# draw button on screen
screen.blit(self.image, (self.rect.x, self.rect.y))
return action
# create button instances
bomb_button = Button(bombplacex, bombplacey, bomb_img, 0.25)
strawberry_button = Button( strawplacex, strawplacey, straw_img,0.15)
pepper_button = Button(pepperplacex,pepperplacey,pepper_img,0.15)
#game loop
run = True
while run:
screen.fill((153, 50, 204))
# if the bomb is clicked the game will end and if the strawberry is clicked a point will be added.
if bomb_button.draw() == True:
print('GAME OVER')
run = False
elif strawberry_button.draw() == True:
points = points + 1
print('You have',points,'points')
elif pepper_button.draw() == True:
points = points + 1
print('You have',points,'points')
#event handler
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
You're close to implementing your own Sprite class. You should subclass the PyGame sprite and use sprite Groups, it will make your life easier and is worth the investment.
A sprite needs an update() function that is called each game loop iteration so that's where your movement logic would need to be. If wanted to adjust the x and y positions by a random amount you could do something like:
class ShiftyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("blueviolet"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
def update(self):
""" shift randomly - Note doesn't check screen boundaries"""
self.rect.x += random.randint(-5,5)
self.rect.y += random.randint(-5,5)
But perhaps this isn't the random movement you're after.
Here's a block that starts going in a random direction when created and only changes when it bounces off a wall.
class BouncyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("darkgreen"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
# initialise speed on creation
self.speedx = random.randint(-5, 5)
self.speedy = random.randint(-5, 5)
def update(self):
# simplistic bounds checking
width, height = screen.get_size()
if not 0 < self.rect.x < width:
self.speedx *= -1 # reverse direction
self.rect.x += self.speedx
if not 0 < self.rect.y < height:
self.speedy *= -1 # reverse direction
self.rect.y += self.speedy
These sprites will look like:
Full example code:
import pygame
import random
screen = pygame.display.set_mode((500,500))
pygame.init()
sprite_list = pygame.sprite.Group()
class ShiftyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("blueviolet"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
def update(self):
""" shift randomly - Note doesn't check screen boundaries"""
self.rect.x += random.randint(-5,5)
self.rect.y += random.randint(-5,5)
class BouncyBlock(pygame.sprite.Sprite):
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("darkgreen"))
self.rect = self.image.get_rect()
self.rect[0] = pos[0]
self.rect[1] = pos[1]
# initialise speed on creation
self.speedx = random.randint(-5, 5)
self.speedy = random.randint(-5, 5)
def update(self):
# simplistic bounds checking
width, height = screen.get_size()
if not 0 < self.rect.x < width:
self.speedx *= -1 # reverse direction
self.rect.x += self.speedx
if not 0 < self.rect.y < height:
self.speedy *= -1 # reverse direction
self.rect.y += self.speedy
for _ in range(5):
block = ShiftyBlock((80,random.randint(40,100)), (random.randint(100,400),random.randint(100,400)))
sprite_list.add(block)
for _ in range(4):
block = BouncyBlock((80,random.randint(40,100)), (random.randint(100,400),random.randint(100,400)))
sprite_list.add(block)
run = True
clock = pygame.time.Clock()
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(pygame.Color("white"))
sprite_list.update()
sprite_list.draw(screen)
pygame.display.update()
clock.tick(60) # limit to 60 FPS
pygame.quit()
I am teaching myself pygame and am looking at making my character able to rotate and then move in the direction they are facing.
I can do the rotation but cannot get the character to move in the direction the image is then facing.
The code is on Trinket HERE
class Bob(pygame.sprite.Sprite):
def __init__(self, color , height , width):
super().__init__()
self.image = pygame.Surface([width , height])
self.image.fill(BLACK)
self.image.set_colorkey(BLACK)
#Loading the image for the character
self.img = pygame.image.load("char.jfif")
#creating a copy of the image
self.img_orig = self.img.copy()
#defining the starting angle of the character image
self.angle = 0
self.velocity = 5
self.rect = self.img_orig.get_rect()
def moveLeft(self):
self.angle += 1
self.img = pygame.transform.rotate(self.img_orig, self.angle)
def moveRight(self):
self.rect.x += self.velocity
if self.rect.x > 485:
self.rect.x = 485
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
pSprite.moveForward()
if keys[pygame.K_DOWN]:
pSprite.moveDown()
if keys[pygame.K_LEFT]:
pSprite.moveLeft()
if keys[pygame.K_RIGHT]:
pSprite.moveRight()
#---- Game Logic Here
#--- Drawing Code Here
#Reset the screen to blank
screen.fill(BLUE)
#Draw Play Area
#Draw Sprites
screen.blit(pSprite.img,(pSprite.rect.x, pSprite.rect.y))
You can use pygame's Vector2 class instead of calculating the position of your sprite yourself.
I also suggest to let the sprite itself handle its movement instead of doing so in the main loop and using a clock for constant framerates and easy control of the speed of your sprites.
You also probably want to use an image format with alpha channel (like PNG).
Here's a simple example:
import pygame
class Actor(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.image.load('char.png').convert_alpha()
self.image_org = self.image.copy()
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.Vector2(pos)
self.direction = pygame.Vector2((0, -1))
def update(self, events, dt):
pressed = pygame.key.get_pressed()
# if a is pressed, rotate left with 360 degress per second
if pressed[pygame.K_a]: self.direction.rotate_ip(dt * -360)
# if d is pressed, rotate right with 360 degress per second
if pressed[pygame.K_d]: self.direction.rotate_ip(dt * 360)
# check if should move forward or backward
movement = 0
if pressed[pygame.K_w]: movement = 1
if pressed[pygame.K_s]: movement = -1
movement_v = self.direction * movement
if movement_v.length() > 0:
movement_v.normalize_ip()
# move 100px per second in the direction we're facing
self.pos += movement_v * dt * 100
# rotate the image
self.image = pygame.transform.rotate(self.image_org, self.direction.angle_to((0, -1)))
self.rect = self.image.get_rect(center=self.pos)
def main():
pygame.init()
screen = pygame.display.set_mode((600, 480))
sprites = pygame.sprite.Group()
Actor((100, 100), sprites)
clock, dt = pygame.time.Clock(), 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill('grey')
sprites.draw(screen)
sprites.update(events, dt)
pygame.display.flip()
dt = clock.tick(60) / 1000
main()
char.png
Rotate the player around its center (see How do I rotate an image around its center using PyGame?):
self.angle += 1
self.img = pygame.transform.rotate(self.img_orig, self.angle)
self.rect = self.img.get_rect(center = self.rect.center)
Use an attribute x and y to store the position of the player with floating point accuracy.
class Bob(pygame.sprite.Sprite):
def __init__(self, color , height , width):
# [...]
self.x, self.y = self.rect.center
Compute the direction of the player dependent on the angle with the trgonometric function math.sin and math.cos. Change the position attributes and update the rect attribute:
self.x += self.velocity * math.cos(math.radians(self.angle + 90))
self.y -= self.velocity * math.sin(math.radians(self.angle + 90))
self.rect.center = round(self.x), round(self.y)
The y-axis needs to be reversed (-dy) as the y-axis is generally pointing up, but in the PyGame coordinate system the y-axis is pointing down. In addition, a correction angle must be deducted (+ 90). Since the "angle" is 0 ° when the sprite is looking up, you need to add 90 ° to the angle for the calculation of the direction vector.
See also te in pygame while moving with the keys](How to turn the sprite in pygame while moving with the keys.
Class Bob:
import pygame
import math
BLACK = (0,0,0)
class Bob(pygame.sprite.Sprite):
def __init__(self, color , height , width):
super().__init__()
self.image = pygame.Surface([width , height])
self.image.fill(BLACK)
self.image.set_colorkey(BLACK)
#Loading the image for the character
self.img = pygame.image.load("char.jfif")
#creating a copy of the image
self.img_orig = self.img.copy()
#defining the starting angle of the character image
self.angle = 0
self.velocity = 5
self.rect = self.img_orig.get_rect()
self.x, self.y = self.rect.center
def rotate(self, change_angle):
self.angle += change_angle
self.img = pygame.transform.rotate(self.img_orig, self.angle)
self.rect = self.img.get_rect(center = self.rect.center)
def move(self, distance):
self.x += distance * math.cos(math.radians(self.angle + 90))
self.y -= distance * math.sin(math.radians(self.angle + 90))
self.rect.center = round(self.x), round(self.y)
def moveLeft(self):
self.rotate(1)
def moveRight(self):
self.rotate(-1)
def moveForward(self):
self.move(self.velocity)
def moveDown(self):
self.move(-self.velocity)
When setting the starting position of the player, you need to set the x, y and rect attribute:
pSprite = Bob(WHITE , 25,25)
pSprite.rect.x = 50
pSprite.rect.y = 50
pSprite.x, pSprite.y = pSprite.rect.center
In my game the problem is that bullets are coming only from one place i.e, from the center. As my player rotates in direction of cursor, I want the bullets to be shot from top of the player even if the player is rotated and travel in a straight line in the direction player is facing towards, As the player rotates in the direction of cursor.
As you can view here the the bullets are always in same direction and always come out of same place.
I tried to use getpos() method to get cursor position and tried to subtract from the player coordinates but failed to get the result.
I think the problem is within the def shoot(self) method of Rotator class, I need to get the coordinates spaceship's tip even when it is rotating all time.
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
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.rect = self.image.get_rect()
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
self.speedx = random.randrange(-3, 3)
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
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.speedy = random.randrange(1, 8)
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.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):
bullet = Bullet(self.rect.centerx, self.rect.centery)
all_sprites.add(bullet)
bullets.add(bullet)
class Bullet(pg.sprite.Sprite):
def __init__(self, x, y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('bullet.png').convert_alpha()
self.image = pg.transform.smoothscale(pg.image.load('bullet.png'), (10,10))
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
self.speedy = -8
def update(self):
self.rect.y += self.speedy
# kill if it moves off the top of the screen
if self.rect.bottom < 0:
self.kill()
all_sprites = pg.sprite.Group()
bullets = pg.sprite.Group()
mobs = pg.sprite.Group()
rotator = Rotator(screen_rect)
all_sprites.add(rotator)
for i in range(5):
m = Mob()
all_sprites.add(m)
mobs.add(m)
while running:
keys = pg.key.get_pressed()
for event in pg.event.get():
if event.type == pg.QUIT:
sys.exit()
pygame.quit()
if event.type == pg.MOUSEBUTTONDOWN:
rotator.shoot()
screen.blit(background, [0, 0])
all_sprites.update()
hits = pg.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
m = Mob()
all_sprites.add(m)
mobs.add(m)
hits = pg.sprite.spritecollide(rotator, mobs, False)
if hits:
running = False
all_sprites.draw(screen)
clock.tick(60)
pg.display.update()
See Shooting a bullet in pygame in the direction of mouse and calculating direction of the player to shoot pygame.
Pass the mouse position to rotator.shoot(), when the mouse button is pressed:
if event.type == pg.MOUSEBUTTONDOWN:
rotator.shoot(event.pos)
Calculate the direction of from the rotator to the mouse position and pass it the constructor of the new bullet object:
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)
Use pygame.math.Vector2 to store the current positon of the bullet and the normalized direction of the bullet (Unit vector):
class Bullet(pg.sprite.Sprite):
def __init__(self, x, y, dx, dy):
pg.sprite.Sprite.__init__(self)
self.image = pg.transform.smoothscale(pg.image.load('bullet.png').convert_alpha(), (10,10))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.speed = 8
self.pos = pg.math.Vector2(x, y)
self.dir = pg.math.Vector2(dx, dy).normalize()
Calcualate the new position of the bullet in update() (self.pos += self.dir * self.speed) and update the .rect attribute by the new position.
.kill() the bullet when it leaves the screen. This can be checked by self.rect.colliderect():
class Bullet(pg.sprite.Sprite):
# [...]
def update(self):
self.pos += self.dir * self.speed
self.rect.center = (round(self.pos.x), round(self.pos.y))
if not self.rect.colliderect(0, 0, width, height):
self.kill()
I am new to pygame and I am trying to make a game where the player has to bypass some enemy's to get to a point where you can go to the next level. I need the enemy's to walk back and forward on a predetermined path but I can't figure out how to do it. So I was wondering if there is an easy way to do this?
This is my code.
import pygame
import random
import os
import time
from random import choices
from random import randint
pygame.init()
a = 0
b = 0
width = 1280
height = 720
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Game")
done = False
n = 0
x = 0
y = 0
x_wall = 0
y_wall = 0
clock = pygame.time.Clock()
WHITE = (255,255,255)
RED = (255,0,0)
change_x = 0
change_y = 0
HW = width / 2
HH = height / 2
background = pygame.image.load('mountains.png')
#player class
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("character.png")
self.rect = self.image.get_rect()
self.rect.x = width / 2
self.rect.y = height / 2
#enemy class
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("enemy.png")
self.image = pygame.transform.scale(self.image, (int(50), int(50)))
self.rect = self.image.get_rect()
self.rect.x = width / 3
self.rect.y = height / 3
#wall class
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("wall.png")
self.image = pygame.transform.scale(self.image, (int(50), int(50)))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#wall movement
def update(self):
self.vx = 0
self.vy = 0
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.vx = 5
self.vy = 0
elif key[pygame.K_RIGHT]:
self.vx = -5
self.vy = 0
if key[pygame.K_UP]:
self.vy = 5
self.vx = 0
elif key[pygame.K_DOWN]:
self.vy = -5
self.vx = 0
self.rect.x = self.rect.x + self.vx
self.rect.y = self.rect.y + self.vy
#player sprite group
sprites = pygame.sprite.Group()
player = Player()
sprites.add(player)
#enemy sprite group
enemys = pygame.sprite.Group()
enemy = Enemy()
enemy2 = Enemy()
enemys.add(enemy, enemy2)
#all the wall sprites
wall_list = pygame.sprite.Group()
wall = Wall(x_wall, y_wall)
wall2 = Wall((x_wall + 50), y_wall)
wall3 = Wall((x_wall + 100), y_wall)
wall4 = Wall((x_wall + 150), y_wall)
wall5 = Wall((x_wall + 200), y_wall)
wall6 = Wall((x_wall + 250), y_wall)
#add all the walls to the list to draw them later
wall_list.add(wall, wall2, wall3, wall4, wall5, wall6)
#add all the walls here to fix the collision
all_walls = (wall, wall2, wall3, wall4, wall5, wall6)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
sprites.update()
wall_list.update()
enemys.update()
#collision between player and and walls
if player.rect.collidelist(all_walls) >= 0:
print("Collision !!")
player.rect.x = player.rect.x - player.vx
player.rect.y = player.rect.y - player.vx
#fill the screen
screen.fill((0, 0, 0))
#screen.blit(background,(x,y))
#draw the sprites
sprites.draw(screen)
wall_list.draw(screen)
enemys.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
Here is the download link with the images if you want to run it:
https://geordyd.stackstorage.com/s/hZZ1RWcjal6ecZM
I'd give the sprite a list of points (self.waypoints) and assign the first one to a self.target attribute.
In the update method I subtract the self.pos from the self.target position to get a vector (heading) that points to the target and has a length equal to the distance. Scale this vector to the desired speed and use it as the velocity (which gets added to the self.pos vector each frame) and the entity will move towards the target.
When the target is reached, I just increment the waypoint index and assign the next waypoint in the list to self.target. It's a good idea to slow down when you're getting near the target, otherwise the sprite could get stuck and moves back and forth if it can't reach the target point exactly. Therefore I also check if the sprite is closer than the self.target_radius and decrease the velocity to a fraction of the maximum speed.
import pygame as pg
from pygame.math import Vector2
class Entity(pg.sprite.Sprite):
def __init__(self, pos, waypoints):
super().__init__()
self.image = pg.Surface((30, 50))
self.image.fill(pg.Color('dodgerblue1'))
self.rect = self.image.get_rect(center=pos)
self.vel = Vector2(0, 0)
self.max_speed = 3
self.pos = Vector2(pos)
self.waypoints = waypoints
self.waypoint_index = 0
self.target = self.waypoints[self.waypoint_index]
self.target_radius = 50
def update(self):
# A vector pointing from self to the target.
heading = self.target - self.pos
distance = heading.length() # Distance to the target.
heading.normalize_ip()
if distance <= 2: # We're closer than 2 pixels.
# Increment the waypoint index to swtich the target.
# The modulo sets the index back to 0 if it's equal to the length.
self.waypoint_index = (self.waypoint_index + 1) % len(self.waypoints)
self.target = self.waypoints[self.waypoint_index]
if distance <= self.target_radius:
# If we're approaching the target, we slow down.
self.vel = heading * (distance / self.target_radius * self.max_speed)
else: # Otherwise move with max_speed.
self.vel = heading * self.max_speed
self.pos += self.vel
self.rect.center = self.pos
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
waypoints = [(200, 100), (500, 400), (100, 300)]
all_sprites = pg.sprite.Group(Entity((100, 300), waypoints))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
for point in waypoints:
pg.draw.rect(screen, (90, 200, 40), (point, (4, 4)))
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Instead of the waypoints list and index I'd actually prefer to use itertools.cycle and just call next to switch to the next point:
# In the `__init__` method.
self.waypoints = itertools.cycle(waypoints)
self.target = next(self.waypoints)
# In the `update` method.
if distance <= 2:
self.target = next(self.waypoints)
Use a list to have him walk back and forth.
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("enemy.png")
self.image = pygame.transform.scale(self.image, (int(50), int(50)))
self.rect = self.image.get_rect()
self.rect.x = width / 3
self.rect.y = height / 3
#x or y coordinates
self.list=[1,2,3,4,5]
self.index=0
def update(self):
# patrol up and down or left and right depending on x or y
if self.index==4:
#reverse order of list
self.list.reverse()
self.index=0
#set the x position of the enemy according to the list
self.rect.x=self.list[self.index]
self.index+=1