Pygame: Making things move slower than 1 [duplicate] - python

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 1 year ago.
I made a little Space Invaders like game, everything is good except that I feel like my enemies I programmed move too fast. If I make their movement speed under 1 such as 0.5, they won't even move. Is there a way I can make the movement even slower?
Here is the code for my enemy unit:
import math
WINDOW = pygame.display.set_mode((800, 900))
class Enemy (pygame.sprite.Sprite):
def __init__(self):
super(). __init__ ()
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((20,20))
self.image.fill((255,0,0))
pygame.draw.circle(self.image,(COLOUR4), (10,10),10)
self.rect=self.image.get_rect()
self.rect.center=(0,0)
self.dx=2
self.dy=1
def update(self):
self.rect.centery += self.dy
for i in range (100):
enemy = Enemy()
enemy.rect.x=random.randrange(750)
enemy.rect.y=random.randrange(100)
enemy_list.add(enemy)
all_sprites_list.add(enemy)

The problem is that pygame.Rects can only have ints as their coordinates and floats get truncated. If you want floating point values as the velocities, you have to store the actual position as a separate attribute (or two as in the first example below), add the velocity to it every frame and then assign the new position to the rect.
class Enemy(pygame.sprite.Sprite):
def __init__(self, pos): # You can pass the position as a tuple, list or vector.
super().__init__()
self.image = pygame.Surface((20, 20))
self.image.fill((255, 0, 0))
pygame.draw.circle(self.image, (COLOUR4), (10, 10), 10)
# The actual position of the sprite.
self.pos_x = pos[0]
self.pos_y = pos[1]
# The rect serves as the blit position and for collision detection.
# You can pass the center position as an argument.
self.rect = self.image.get_rect(center=(self.pos_x, self.pos_y))
self.dx = 2
self.dy = 1
def update(self):
self.pos_x += self.dx
self.pos_y += self.dy
self.rect.center = (self.pos_x, self.pos_y)
I recommend using vectors, since they are more versatile and concise.
from pygame.math import Vector2
class Enemy(pygame.sprite.Sprite):
def __init__(self, pos):
# ...
self.pos = Vector2(pos)
self.velocity = Vector2(2, 1)
def update(self):
self.pos += self.velocity
self.rect.center = self.pos

Related

Sprites stuck to border of window when velocity less than 1 [duplicate]

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Problem with Pygame movement acceleration, platformer game
(1 answer)
How to draw a moving circle in Pygame with a small angle at a low speed and blinking?
(1 answer)
Ship moves up and left faster than down and right when rotating in pygame
(1 answer)
Closed 2 years ago.
I'm trying to make moving clouds for my game but sprites of clouds stuck to borders when I set the velocity of cloud less than 1. I want that cloud continues moving if a part of the cloud is already outside of the screen.
I found out that sprites stuck if x of rect equals 0. How to fix it?
My code:
class Cloud(pygame.sprite.Sprite):
def __init__(self):
super(Cloud, self).__init__()
images = [load_image(f"cloud{i}.png") for i in range(1, 5)]
self.image = random.choice(images)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(WIDTH - self.rect.w)
self.rect.y = random.randrange(HEIGHT - self.rect.h)
self.vel = 10 / FPS # It returns value less then 1
def update(self, event=None):
if not event:
self.rect.x -= self.vel
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the new position of the object is set to the Rect object:
self.rect.x -= self.vel
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .topleft) of the rectangle:
class Cloud(pygame.sprite.Sprite):
def __init__(self):
super(Cloud, self).__init__()
images = [load_image(f"cloud{i}.png") for i in range(1, 5)]
self.image = random.choice(images)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(WIDTH - self.rect.w)
self.rect.y = random.randrange(HEIGHT - self.rect.h)
self.x, self.y = self.rect.topleft
self.vel = 10 / FPS # It returns value less then 1
def update(self, event=None):
if not event:
self.x -= self.vel
self.rect.topleft = round(self.x), round(self.y)

Play animation upon collision with enemy

so I have loaded the images into pygame:
Explosion = [pygame.image.load('Explosion1.png'), pygame.image.load('Explosion2.png'), pygame.image.load('Explosion3.png'), pygame.image.load('Explosion4.png'), pygame.image.load('Explosion5.png'), pygame.image.load('Explosion6.png'), pygame.image.load('Explosion7.png'), pygame.image.load('Explosion8.png'), pygame.image.load('Explosion9.png'), pygame.image.load('Explosion10.png')]
and I would like, when the bullet, which is a separate class, makes a collision with the missile, it plays this animation at the position where both the bullet and the enemy collide, I'm not sure how I go around doing this?
Collision Script (In the main loop):
hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
Bullet Class:
class Bullet (pygame.sprite.Sprite):
def __init__ (self, x, y):
super (Bullet, self).__init__()
self.surf = pygame.image.load("Bullet.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedx = bullet_speed
def update(self):
self.rect.x += self.speedx
if self.rect.left > SCREEN_WIDTH:
self.kill()
Enemy Class:
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super(Enemy, self).__init__()
self.surf = pygame.image.load("Missiles.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect(
center=(
random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
random.randint(0, SCREEN_HEIGHT),
)
)
self.speed = random.randint(Enemy_SPEED_Min, Enemy_SPEED_Max)
def update(self):
self.rect.move_ip(-self.speed, 0)
if self.rect.right < 0:
self.kill()
all of the code is here https://pastebin.com/CG2C6Bkc if you need it!
thank you in advance!
Do not destroy the enemies, when it collides with an bullet. Iterate through the enemies and which are returned in hits and start the explosion animation instead of killing the enemies:
hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
hits = pygame.sprite.groupcollide(enemies, bullets, False, True)
for enemy in hits:
enemy.start_animation()
Add the attributes animation_count, animation_frames and animation to th class Enemy. When the explosion animation is started then animation is set True. animation_frames controls the speed of the animation. If the animation is started, then the enemy stops to move and the Explosion image is shown instead of the enemy. After the last image of the animation is shown, the enemy is killed:
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super(Enemy, self).__init__()
self.surf = pygame.image.load("Missiles.png").convert()
self.surf.set_colorkey((255,255,255), RLEACCEL)
self.rect = self.surf.get_rect(
center=(
random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
random.randint(0, SCREEN_HEIGHT),
)
)
self.speed = random.randint(Enemy_SPEED_Min, Enemy_SPEED_Max)
self.animation_count = 0
self.animation_frames = 10
self.animation = False
def start_animation(self):
self.animation = True
def update(self):
if self.animation:
image_index = self.animation_count // self.animation_frames
self.animation_count += 1
if image_index < len(Explosion):
self.surf = Explosion[image_index]
self.rect = self.surf.get_rect(center = self.rect.center)
else:
self.kill()
self.rect.move_ip(-self.speed, 0)
if self.rect.right < 0:
self.kill()
Well i dont want to do it for you, But i will say one way you could do it, then you can go and try to do it, and if you run into a problem/error, you can update the question and we can help you.
I would create another class called Explosion and and give it the images, when when you draw it, draw the first image, then change to the second, then draw that one, then change...
Then after it finishes, destroy the object.
So, create the class, create a spritegroup, when the bullet collides, create a new instance of the class, giving it the x and y co ordinates of the collision, update it every frame, and it will draw all the images then destroy itself
also, an easier way to get all of your images instead of typing them all out is
Explosion_imgs = [pygame.image.load("explosion" + str(x) + ".png") for x in range(1,11,1)]

Pygame: How to call defined variable that is inside a class in my main game loop?

So, i'm making some stupid platform game, and i'm currently working with a projectile. Right now everything works fine, but I don't know how to make the projectile move left, I can only move it right. I have a variable called 'Bullet' that is inside a class, but I don't know how to call it in the main loop. When ever I try to call it, its not defined. Heres my code for the bullet:
def shoot(self):
if self.ammo > 0:
Bullet = bullet(self.Game,self.rect.x,self.rect.top)
self.Game.all_sprites.add(Bullet)
self.Game.bullets.add(Bullet)
self.ammo -= 1
Heres the bullet class
class bullet(pg.sprite.Sprite):
def __init__(self, Game,x,y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('magicBullet.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.Game = Game
self.Player = Player
self.speed = 10
def update(self):
self.rect.x += self.speed
Can anyone help?
Why start the variable name with a capital letter and the class name is lowercase? It should be the other way around. See Style Guide for Python Code - Class Names
Anyway, you have to define the direction of movement when the bullet spawns.
Add an additional argument for the speed to the constructor of the class bullet:
class bullet(pg.sprite.Sprite):
def __init__(self, Game, x, y, speed):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('magicBullet.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.Game = Game
self.Player = Player
self.speed = speed
def update(self):
self.rect.x += self.speed
Add an argument which defines the direction to the method shoot. The accepted values for the argument direction are either -1 for the movement to the left or 1 for the movement to the right:
def shoot(self, direction):
if self.ammo > 0:
speed = 10 * direction
Bullet = bullet(self.Game, self.rect.x, self.rect.top, speed)
self.Game.all_sprites.add(Bullet)
self.Game.bullets.add(Bullet)
self.ammo -= 1

Image doesn't move in Pygame

Please, My Pygame image doesn't move but when I change the code to print and I press the arrow keys, it does work. Any help would be greatly appreciated. I have imported OS, random and pygame in my original code as well as set width and height to 800, 600
class Alien(pygame.sprite.Sprite):
def __init__(self,speed, size):
self.x = random.randrange(0,width)
self.y = random.randrange(-200, -20)
self.size = random.randint(3, 20)
self.speed = speed
def move_down(self):
self.y += self.speed
def draw(self, screen, colour):
pygame.draw.circle(screen, colour, (self.x, self.y), self.size)
def check_landed(self):
if self.y > height:
self.x = random.randrange(0,width)
self.y = random.randrange(-400, -20)
self.speed = random.randrange(1,4)
class Actor(Alien):
def __init__(self, filename):
self.score = 0
self.image = pygame.image.load(os.path.join("../images", filename)).convert_alpha()
self.rect = self.image.get_rect()
def move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
self.rect.centerx -= 10
print("let")
elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
self.rect.centerx += 10
def draw(self, screen):
screen.blit(self.image, (width/2, height - 40))
In the main game implementation, i have
enemies = []
actor = Actor("ship.jpg")
for i in range (20):
aliens = Alien(2, 4)
enemies.append(aliens)
done = False
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(white)
actor.x = 500
actor.y = 500
actor.draw(screen)
actor.move()
for i in range(len(enemies)):
enemies[i].draw(screen, red)
enemies[i].move_down()
enemies[i].check_landed()
pygame.display.flip()
clock.tick(30)
You've overriden the Sprite.draw method.
The normal method, as the docs say, "uses the Sprite.image attribute for the source surface, and Sprite.rect for the position".
In your Alien subclass, you aren't using that, but you're instead using the x and y attributes, which might also work, because that's what you're modifying everywhere:
def draw(self, screen, colour):
pygame.draw.circle(screen, colour, (self.x, self.y), self.size)
But your Actor subclass doesn't use either, it just blits to screen at a fixed location of (width/2, height - 40):
def draw(self, screen):
screen.blit(self.image, (width/2, height - 40))
So, no matter what attributes you change, you're always going to draw at the same position.
I'm not sure why you're overriding draw for either of these classes. I'm also not sure why Alien inherits from Sprite but then uses x and y attributes while ignoring rect, while Actor inherits from Alien but then ignores x and y while using rect again. This is almost certainly going to confuse you. But if you want the smallest possible change:
screen.blit(self.image, self.rect)
Of course you'll also need to set up self.rect to the initial starting position at startup, and prevent the user from going all the way off the edge of the screen, and so on. And, again, it would be better to clean this up to not be confusing in the first place. But this should get you started.

Random direction bullets?

I've been working on a bullet-hell game, but when I try to make the bullets spawn in random directions and travel in a straight line, I can't seem to be able to do it. I can see that making the bullet travel +/- certain pixels in x and y axis only causes them to travel in a random pattern that seems unnatural.
My code:
class Bullet(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Bullet.png")
self.rect = self.image.get_rect()
def update(self) :
self.rect.x += random.randrange(-10,10)
self.rect.y += random.randrange(-10,10)
In the __init__ method, you can create self.dx and self.dy variables that will be randomly chosen once upon instantiation and then stay constant every update:
class Bullet(pygame.sprite.Sprite):
def init(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Bullet.png")
self.rect = self.image.get_rect()
self.dx = random.randrange(-10,10)
self.dy = random.randrange(-10,10)
def update(self):
self.rect.x += self.dx
self.rect.y += self.dy

Categories

Resources