I am creating a game where you shoot enemies in a top-down format. My bullet and enemy function properly in every aspect except where it has to delete itself. I have hooked it up to where if the bullet collides with the enemy or 5 seconds have passed, the bullet would delete itself. It does do that, except the code continually iterates to create a sprite and kill it as the bullet moves for some reason, and still shows the bullet on the screen as if nothing is happening to it. Can someone tell me why the code is iterating the creation of a bullet and the deletion of it so many times?
-------- CODE --------
method in player class
def shoot(self):
pos = pygame.mouse.get_pos()
pos += camera_group.offset
click = pygame.mouse.get_pressed()
if click[0] and not self.shooting:
self.shooting = True
# create bullet instance if player shoots
bullet = Bullet(self.rect.centerx, self.rect.centery, "bullet", pos, camera_group)
bullet_group.add(bullet)
if not click[0]:
self.shooting = False
bullet class
class Bullet(Entity):
def __init__(self, x, y, image, pos, group):
super().__init__(x, y, image, group)
pygame.sprite.Sprite.__init__(self)
# creating and scaling image
self.image = pygame.transform.flip(self.image, True, False)
self.image = pygame.transform.scale(self.image, (32, 16))
# creating rect
self.x = x
self.y = y
self.speed = 8
self.mousepos = pos
# calculating angle of bullet
self.angle = math.atan2(pos[1] - y, pos[0] - x)
self.angle_deg = (self.angle * (180 / math.pi))
self.image = pygame.transform.rotate(self.image, self.angle_deg * -1)
# calculating vel_x/vel_y
dx = self.mousepos[0] - player.rect.centerx
dy = self.mousepos[1] - player.rect.centery
total = abs(dx) + abs(dy)
self.vel_x = (self.speed * (dx / total))
self.vel_y = (self.speed * (dy / total))
# setting variable to store time of bullet creation
self.start_time = pygame.time.get_ticks()
def update(self):
if pygame.time.get_ticks() - self.start_time > 50 and len(bullet_group) >= 1:
bullet_group.sprites()[0].kill()
elif self.rect.colliderect(enemy):
bullet_group.sprites()[0].kill()
enemy.kill()
else:
# movement
self.x += self.vel_x
self.y += self.vel_y
self.rect.x = int(self.x)
self.rect.y = int(self.y)
Don't kill the first bullet in the Group, just delete the bullet object itself:
bullet_group.sprites()[0].kill()
self.kill()
method update in class Bullet:
class Bullet(Entity):
# [...]
def update(self):
if pygame.time.get_ticks() - self.start_time > 50 and len(bullet_group) >= 1:
self.kill()
elif self.rect.colliderect(enemy):
self.kill()
enemy.kill()
else:
# movement
self.x += self.vel_x
self.y += self.vel_y
self.rect.x = int(self.x)
self.rect.y = int(self.y)
Related
I am creating a game where you shoot enemies in a top-down format. My bullet and enemy function properly in every aspect except where it has to delete itself. I have hooked it up to where if the bullet collides with the enemy or 5 seconds have passed, the bullet would delete itself. It does do that, except the code continually iterates to create a sprite and kill it as the bullet moves for some reason, and still shows the bullet on the screen as if nothing is happening to it. Can someone tell me why the code is iterating the creation of a bullet and the deletion of it so many times?
-------- CODE --------
method in player class
def shoot(self):
pos = pygame.mouse.get_pos()
pos += camera_group.offset
click = pygame.mouse.get_pressed()
if click[0] and not self.shooting:
self.shooting = True
# create bullet instance if player shoots
bullet = Bullet(self.rect.centerx, self.rect.centery, "bullet", pos, camera_group)
bullet_group.add(bullet)
if not click[0]:
self.shooting = False
bullet class
class Bullet(Entity):
def __init__(self, x, y, image, pos, group):
super().__init__(x, y, image, group)
pygame.sprite.Sprite.__init__(self)
# creating and scaling image
self.image = pygame.transform.flip(self.image, True, False)
self.image = pygame.transform.scale(self.image, (32, 16))
# creating rect
self.x = x
self.y = y
self.speed = 8
self.mousepos = pos
# calculating angle of bullet
self.angle = math.atan2(pos[1] - y, pos[0] - x)
self.angle_deg = (self.angle * (180 / math.pi))
self.image = pygame.transform.rotate(self.image, self.angle_deg * -1)
# calculating vel_x/vel_y
dx = self.mousepos[0] - player.rect.centerx
dy = self.mousepos[1] - player.rect.centery
total = abs(dx) + abs(dy)
self.vel_x = (self.speed * (dx / total))
self.vel_y = (self.speed * (dy / total))
# setting variable to store time of bullet creation
self.start_time = pygame.time.get_ticks()
def update(self):
if pygame.time.get_ticks() - self.start_time > 50 and len(bullet_group) >= 1:
bullet_group.sprites()[0].kill()
elif self.rect.colliderect(enemy):
bullet_group.sprites()[0].kill()
enemy.kill()
else:
# movement
self.x += self.vel_x
self.y += self.vel_y
self.rect.x = int(self.x)
self.rect.y = int(self.y)
Don't kill the first bullet in the Group, just delete the bullet object itself:
bullet_group.sprites()[0].kill()
self.kill()
method update in class Bullet:
class Bullet(Entity):
# [...]
def update(self):
if pygame.time.get_ticks() - self.start_time > 50 and len(bullet_group) >= 1:
self.kill()
elif self.rect.colliderect(enemy):
self.kill()
enemy.kill()
else:
# movement
self.x += self.vel_x
self.y += self.vel_y
self.rect.x = int(self.x)
self.rect.y = int(self.y)
This question already has answers here:
How do I detect collision in pygame?
(5 answers)
How do I prevent the player from moving through the walls in a maze?
(3 answers)
Closed 9 months ago.
I am trying to learn the basics of pygame by making a simple pacman game with a friend, but I have been having trouble figuring out how to make walls and check collision with the pacman and the wall, and also stopping the movement through a wall.
(this is a 2 player pacman, ghost is arrow keys, and pacman is wasd)
This is main.py
import pygame
import random
import time
from Pacman import pacman
from Ghost import ghost
#colors
Yellow = (255,255,0)
Blackish_Blue = (20,0,70)
Red = (255,0,0)
P_B = (40,60,100)
clock = pygame.time.Clock()
#display
pygame.init()
pygame.display.set_caption(' Scuffed Pacman')
width, height = 640, 480
screen = pygame.display.set_mode((width, height))
pacman = pacman(screen)
ghost = ghost(screen)
font = pygame.font.Font('freesansbold.ttf', 32)
text = font.render('Game Over', True, Yellow)
textRect = text.get_rect()
textRect.center = (width / 2, height / 2)
run = True
while run == True:
pygame.time.delay(10)
clock.tick(60)
screen.fill(Blackish_Blue)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
color = (182,163,192)
#attempt to make wall and check collision
ghostspawn = pygame.Rect(0,0,60,40)
ghostspawn.center = (width / 2, (height / 2)-50)
pygame.draw.rect(screen, color, ghostspawn)
if ghostspawn.collidepoint(pacman.x, pacman.y):
print("collision detected")
pacman.draw(screen)
ghost.draw(screen)
ghost.update()
pacman.update()
distance = (((pacman.x - ghost.x)**2) + ((pacman.y - ghost.y)**2))**(1/2)
sumrad = pacman.radius + ghost.radius
if distance < sumrad:
while True:
screen.fill(P_B)
screen.blit(text, textRect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
pygame.display.update()
pygame.quit()
This is the code for pacman:
import pygame
Yellow = (255,255,0)
class pacman(pygame.sprite.Sprite):
def __init__(self, mainscreen):
super().__init__()
self.x = 320
self.y = 240
self.radius = 15
self.velocity = 2
self.origin = (self.x, self.y)
def draw(self, mainscreen):
pygame.draw.circle(mainscreen, Yellow, (self.x, self.y), self.radius)
def update(self):
self.movement()
self.origin = (self.x, self.y)
def movement(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_d] and self.velocity > 0 and self.x < 625:
self.x += self.velocity
if keys[pygame.K_a] and self.velocity > 0 and self.x > 15:
self.x -= self.velocity
if keys[pygame.K_w] and self.velocity > 0 and self.y > 15:
self.y -= self.velocity
if keys[pygame.K_s] and self.velocity > 0 and self.y < 465:
self.y += self.velocity
and this is the code for ghost:
import pygame
Red = (255,0,0)
class ghost(pygame.sprite.Sprite):
def __init__(self, mainscreen):
super().__init__()
self.x = 213
self.y = 240
self.radius = 15
self.velocity = 2
self.origin = (self.x, self.y)
def draw(self, mainscreen):
pygame.draw.circle(mainscreen, Red, (self.x, self.y), self.radius)
def update(self):
self.movement()
self.origin = (self.x, self.y)
def movement(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT] and self.velocity > 0 and self.x < 625:
self.x += self.velocity
if keys[pygame.K_LEFT] and self.velocity > 0 and self.x > 15:
self.x -= self.velocity
if keys[pygame.K_UP] and self.velocity > 0 and self.y > 15:
self.y -= self.velocity
if keys[pygame.K_DOWN] and self.velocity > 0 and self.y < 465:
self.y += self.velocity
this is my attempt at checking for wall collision, but I am unaware of how to stop pacman and the ghost from going through. the collision also only checks the middle of the circle.
ghostspawn = pygame.Rect(0,0,60,40)
ghostspawn.center = (width / 2, (height / 2)-50)
pygame.draw.rect(screen, color, ghostspawn)
if ghostspawn.collidepoint(pacman.x, pacman.y):
print("allowed")
here is how i detect collision between pacman and ghost, as it may be helpful for doing it with walls, but i don't know how.
distance = (((pacman.x - ghost.x)**2) + ((pacman.y - ghost.y)**2))**(1/2)
sumrad = pacman.radius + ghost.radius
ive looked at a few similar questions on here, but I can't quite grasp how it works.
So, if anyone could help, I'm having trouble with checking collision between a circle and rectangle, and also preventing the circle/pacman from moving through.
To check if a circle is colliding with a wall you just need to do 2 things
Get the distance between the circle and the wall
Then check if the distance is smaller or equal to the circles distance
Pretty much like this:
distance = abs(circle.x - wall.x) + abs(circle.y - wall.y)
if distance <= circle.radius: # abs() Makes the value in the brackets positive
circle.x = oldX
circle.y = oldY
To implement this I with your game would first add a self.oldX and a self.oldY in the self.__init__() in both of the pacman and ghost classes. And I would set them to 0 for now.
Then I would update the oldX and oldY in the movement function before I move the object, like this:
def movement(self):
keys = pygame.key.get_pressed()
self.oldX = self.x
self.oldY = self.y
if keys[pygame.K_d] and self.velocity > 0 and self.x < 625:
self.x += self.velocity
if keys[pygame.K_a] and self.velocity > 0 and self.x > 15:
self.x -= self.velocity
if keys[pygame.K_w] and self.velocity > 0 and self.y > 15:
self.y -= self.velocity
if keys[pygame.K_s] and self.velocity > 0 and self.y < 465:
self.y += self.velocity
Then I would have a list that containes every wall in the game and a list that containes every ghost in the game (That's if you want to have more than one ghost).
Then I would go in the main loop (The while loop that you have) and I would add this After calling the movement function of the ghosts and the pacman:
for wall in walls:
distance = abs(pacman.x - wall.x) + abs(pacman.y - wall.y)
if distance <= pacman.radius:
pacman.x = pacman.oldX
pacman.y = pacman.oldY
for ghost in ghosts:
distance = abs(ghost.x - wall.x) + abs(ghost.y - wall.y)
if distance <= ghost.radius:
ghost.x = ghost.oldX
ghost.y = ghost.oldY
and that should work, Thanks.
Apologies for asking so many questions recently. I'm just starting to get into pygame.
With my previous questions I don't think I've been wording it properly for what I am trying to do.
Here is a quick picture I did to try demonstrate
This is a single background image or map that I would like the player to move across. The red X is just a starting place for the character.
I'm try to make it so when I move the player the background will also follow as if the player is moving through the map. I've already limited the player from not being able to go off the borders of the actual screen. Just having a bit of trouble now trying to make it so the single image will move along the player and if the player reaches the end of the map picture movement stops. I have seen people use scrolling and duplicating the image when the player moves. I just want to see it to the single image that the player will move across. I don't want to worry about collisions just be able to get the movement working.
This is the code I am currently using:
from pygame.locals import *
from math import sin
pygame.display.set_caption("TEST")
clock = pygame.time.Clock()
time_passed = 0
class Player():
def __init__(self,x,y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def getX(self):
return self.rect.x
def getY(self):
return self.rect.y
def handle_keys(self,screenHeight,screenWidth):
key = pygame.key.get_pressed()
dist = 2
if key[K_LEFT] and self.x > 0:
self.x -= 500 * time_passed
if key[K_RIGHT] and self.x < screenWidth -20:
self.x += 500 * time_passed
if key[K_UP] and self.y > 0:
self.y -= 500 * time_passed
if key[K_DOWN] and self.y < screenHeight -20:
self.y += 500 * time_passed
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (int(self.x), int(self.y)))
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
self.rect = self.Image.get_rect()
self.x = 0
self.y = 0
def draw(self, game_window,screenHeight,screenWidth):
self.x = min(max(self.x, player.x - 2 * screenWidth / 2), player.x - screenWidth / 2)
self.y = min(max(self.y, player.y -2 * screenHeight / 2), player.y - screenHeight / 2)
game_window.blit(self.Image,(-self.x,-self.y))
class Enemy():
def __init__ (self,x,y):
self.Image = pygame.image.load("WC.jpg").convert()
self.rect = self.Image.get_rect(topleft = (x,y))
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (self.rect.x, self.rect.y))
pygame.init()
clock = pygame.time.Clock()
screenWidth = 400
screenHeight = 400
game_window = pygame.display.set_mode((screenWidth,screenHeight))
player = Player(200,200)
map = Map()
enemy = Enemy(250,250)
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
player.handle_keys(screenHeight,screenWidth)
game_window.fill((0,0,0))
map.draw(game_window,screenHeight,screenWidth)
#enemy.draw(game_window)
player.draw(game_window)
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()
quit()
Thanks
Shay
The player's movement depends on the size of the map. The x and y attributes of the "player" store the position on the map and are limited to the size of the map (map_size). The player is always drawn in the center of the screen, except it is near the boarders of the map:
class Player():
def __init__(self, x, y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def handle_keys(self, map_size):
key = pygame.key.get_pressed()
self.x += (key[K_RIGHT] - key[K_LEFT]) * 500 * time_passed
self.y += (key[K_DOWN] - key[K_UP]) * 500 * time_passed
self.x = max(0, min(map_size[0]-20, self.x))
self.y = max(0, min(map_size[1]-20, self.y))
def draw(self, game_window, map_size):
window_size = game_window.get_size()
center = window_size[0] // 2, window_size[0] // 2
pos = [self.x, self.y]
for i in range(2):
if center[i] < pos[i] <= map_size[i]-center[i]:
pos[i] = center[i]
elif pos[i] > map_size[i] - center[i]:
pos[i] = window_size[i] - map_size[i] + pos[i]
game_window.blit(self.Image, (int(pos[0]), int(pos[1])))
The player's position on the map is centered in the window:
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
def draw(self, game_window):
window_size = game_window.get_size()
map_size = self.Image.get_size()
x = max(0, min(map_size[0] - window_size[0], player.x - 200))
y = max(0, min(map_size[1] - window_size[1], player.y - 200))
game_window.blit(self.Image, (-x, -y))
Application loop:
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
leave = True
player.handle_keys(map.Image.get_size())
game_window.fill((0,0,0))
map.draw(game_window)
#enemy.draw(game_window)
player.draw(game_window, map.Image.get_size())
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()
I was just getting some help to figure out how to get my player fire bullets when I realized that they only go (kinda expected this but however as only had y value for movement). I don't know how I'll make the bullets fire off in the direction the player is facing.
I have some idea of what to but I just don't know how to do it... I thought I could somehow use the cursor and player tracking that's in this game for the visuals but I don't know how to make that a one-time thing instead of a constant. For diagonal movement of the bullet, I have no clue.
Code below (split into two parts/file Main.py and PlayerSprite.py):
Main:
py.init()
py.mixer.init()
screen = py.display.set_mode((WIDTH, HEIGHT))
py.display.set_caption("Dimensional Drifter")
clock = py.time.Clock()
all_sprites = py.sprite.Group()
NPCs = py.sprite.Group()
bullets = py.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(14):
n = NPC(player)
all_sprites.add(n)
NPCs.add(n)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
for event in py.event.get():
# check for closing window
if event.type == py.QUIT:
running = False
elif event.type == py.KEYDOWN:
if event.key == py.K_SPACE:
New_bullet = player.Shoot()
all_sprites.add(New_bullet)
bullets.add(New_bullet)
# Update
all_sprites.update()
# # check if there a collision between the bullet and NPC
hits = py.sprite.groupcollide(NPCs, bullets, True, True)
# check if there a collision between the player and NPC
hits = py.sprite.spritecollide(player, NPCs, True)
if hits:
running = False
# updates the position of of mouse and rotates it towards the mouse position
mouse_x, mouse_y = py.mouse.get_pos()
player.rotate(mouse_x, mouse_y)
# render
screen.fill(BLACK)
all_sprites.draw(screen)
# flip the display
py.display.flip()
py.quit()
PlayerSprite
import pygame as py
import math
import random
WIDTH = 800
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
class Player(py.sprite.Sprite):
def __init__(self):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((40, 40), py.SRCALPHA)
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT / 2
self.Yspeed = 0
self.rotatableimage = self.image
def update(self):
self.Xspeed = 0
self.Yspeed = 0
# line below allow for key press to equate to move of sprite
keypreesed = py.key.get_pressed()
if keypreesed[py.K_a]:
self.Xspeed = - 11
if keypreesed[py.K_d]:
self.Xspeed = 11
if keypreesed[py.K_w]:
self.Yspeed = - 11
if keypreesed[py.K_s]:
self.Yspeed = 11
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# line below allow the sprite to wrap around the screen
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.top = 0
if self.rect.bottom < 0:
self.rect.bottom = HEIGHT
def rotate(self, mouse_x, mouse_y):
rel_x = mouse_x - self.rect.x
rel_y = mouse_y - self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = py.transform.rotate(self.rotatableimage, int(angle))
self.rect = self.image.get_rect(center=(self.rect.centerx, self.rect.centery))
return
def Shoot(self):
return Bullet(self.rect.centerx, self.rect.top)
class NPC(py.sprite.Sprite):
def __init__(self, player):
py.sprite.Sprite.__init__(self)
self.player = player
self.image = py.Surface((30, 30)).convert_alpha()
self.image.fill(RED)
self.originalimage = self.image
self.rect = self.image.get_rect()
self.spawn()
# allows of spawning from all four side of the screen and set the x, y speed and spawn position
def spawn(self):
self.direction = random.randrange(4)
if self.direction == 0:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Xspeed = random.randrange(-2, 2)
self.Yspeed = random.randrange(4, 8)
elif self.direction == 1:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(HEIGHT, HEIGHT + 60)
self.Xspeed = random.randrange(-2, 2)
self.Yspeed = -random.randrange(4, 8)
elif self.direction == 2:
self.rect.x = random.randrange(-100, -40)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = random.randrange(4, 8)
self.Yspeed = random.randrange(-2, 2)
elif self.direction == 3:
self.rect.x = random.randrange(WIDTH, WIDTH + 60)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = -random.randrange(4, 8)
self.Yspeed = random.randrange(-2, 2)
def update(self):
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# makes it so that NPC point to wards the player as it passes from side to side
dir_x, dir_y = self.player.rect.x - self.rect.x, self.player.rect.y - self.rect.y
self.rot = (180 / math.pi) * math.atan2(-dir_x, -dir_y)
self.image = py.transform.rotate(self.originalimage, self.rot)
# Respawns the NPC when they hit an side
if self.direction == 0:
if self.rect.top > HEIGHT + 10:
self.spawn()
elif self.direction == 1:
if self.rect.bottom < -10:
self.spawn()
elif self.direction == 2:
if self.rect.left > WIDTH + 10:
self.spawn()
elif self.direction == 3:
if self.rect.right < -10:
self.spawn()
class Bullet(py.sprite.Sprite):
def __init__(self, x, y):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((5, 5))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.Yspeed = -10
def update(self):
self.rect.y += self.Yspeed
# kill if moved of screen
if self.rect.bottom > HEIGHT or self.rect.top < 0:
self.kill()
if self.rect.right > WIDTH or self.rect.left < 0:
self.kill()
Add 2 attributes self.lastX and self.lastY to the class Player and change the attributes when the player changes the direction:
class Player(py.sprite.Sprite):
def __init__(self):
# [...]
self.lastX = 0
self.lastY = -10
def update(self):
# [...]
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
if self.Xspeed != 0 or self.Yspeed != 0:
self.lastX = self.Xspeed
self.lastY = self.Yspeed
Add an argument Xspeed ans Yspeed to the class Bullet
class Bullet(py.sprite.Sprite):
def __init__(self, x, y, Xspeed, Yspeed):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((5, 5))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.Xspeed = Xspeed
self.Yspeed = Yspeed
def update(self):
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# [...]
Set the attributes when the bullet spawns
class Player(py.sprite.Sprite):
# [...]
def Shoot(self):
return Bullet(self.rect.centerx, self.rect.centery, self.lastX, self.lastY)
Alternatively it is also possible to set the speed dependent on the direction to the mouse cursor.
Get the position of the player and the mouse cursor and compute the x and y distance (Vector ):
pos = self.rect.center
mpos = py.mouse.get_pos()
vx = mpos[0] - pos[0]
vy = mpos[1] - pos[1]
If the mouse position and the bullet position are equal, that does not make any sense, thus the bullet is skipped
if vx == 0 and vy == 0:
return None
Of course this vector is far to long, if you would use it for the direction (Xspeed, Yspeed) directly, then the bullet would step to the mouse cursor in one turn.
In the following I use pygame.math.Vector2, because it provides the handy method scale_to_length, that scales a vector to a specified Euclidean length:
direction = py.math.Vector2(vx, vy)
direction.scale_to_length(10)
Now the x and y component of the vector contain the x and y component of the speed. Since the components are floating point values, they are round to integral values:
return Bullet(pos[0], pos[1], round(direction.x), round(direction.y))
Method Shoot:
class Player(py.sprite.Sprite):
# [...]
def Shoot(self):
pos = self.rect.center
mpos = py.mouse.get_pos()
vx, vy = mpos[0] - pos[0], mpos[1] - pos[1]
if vx == 0 and vy == 0:
return None
direction = py.math.Vector2(vx, vy)
direction.scale_to_length(10)
return Bullet(pos[0], pos[1], round(direction.x), round(direction.y))
Note, if you set the bullet dependent on the direction to the mouse cursor, then it may be useful to spawn the bullet by a mouse click:
while running:
# [...]
for event in py.event.get():
if event.type == py.QUIT:
# [...]
elif event.type == py.MOUSEBUTTONDOWN:
if event.button == 1:
New_bullet = player.Shoot()
if New_bullet:
all_sprites.add(New_bullet)
bullets.add(New_bullet)
You can use pygames Vector2 to move in any direction. You calculate the angle of the player you can use that.
class Player(py.sprite.Sprite):
def __init__(self):
...
self.angle = 0
def rotate(self, mouse_x, mouse_y):
...
self.angle = -angle #make negative otherwise it will be going away from mouse
def Shoot(self):
return Bullet(self.rect.centerx, self.rect.top, py.math.Vector2(1,0).rotate(self.angle))
then in your bullet class, get the direction and add to its position
class Bullet(py.sprite.Sprite):
def __init__(self, x, y, Dir):
...
self.Dir = Dir
def update(self):
self.rect.y += self.Dir[1] * self.speed
self.rect.x += self.Dir[0] * self.speed
...
Apologies for asking so many questions recently. I'm just starting to get into pygame.
With my previous questions I don't think I've been wording it properly for what I am trying to do.
Here is a quick picture I did to try demonstrate
This is a single background image or map that I would like the player to move across. The red X is just a starting place for the character.
I'm try to make it so when I move the player the background will also follow as if the player is moving through the map. I've already limited the player from not being able to go off the borders of the actual screen. Just having a bit of trouble now trying to make it so the single image will move along the player and if the player reaches the end of the map picture movement stops. I have seen people use scrolling and duplicating the image when the player moves. I just want to see it to the single image that the player will move across. I don't want to worry about collisions just be able to get the movement working.
This is the code I am currently using:
from pygame.locals import *
from math import sin
pygame.display.set_caption("TEST")
clock = pygame.time.Clock()
time_passed = 0
class Player():
def __init__(self,x,y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def getX(self):
return self.rect.x
def getY(self):
return self.rect.y
def handle_keys(self,screenHeight,screenWidth):
key = pygame.key.get_pressed()
dist = 2
if key[K_LEFT] and self.x > 0:
self.x -= 500 * time_passed
if key[K_RIGHT] and self.x < screenWidth -20:
self.x += 500 * time_passed
if key[K_UP] and self.y > 0:
self.y -= 500 * time_passed
if key[K_DOWN] and self.y < screenHeight -20:
self.y += 500 * time_passed
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (int(self.x), int(self.y)))
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
self.rect = self.Image.get_rect()
self.x = 0
self.y = 0
def draw(self, game_window,screenHeight,screenWidth):
self.x = min(max(self.x, player.x - 2 * screenWidth / 2), player.x - screenWidth / 2)
self.y = min(max(self.y, player.y -2 * screenHeight / 2), player.y - screenHeight / 2)
game_window.blit(self.Image,(-self.x,-self.y))
class Enemy():
def __init__ (self,x,y):
self.Image = pygame.image.load("WC.jpg").convert()
self.rect = self.Image.get_rect(topleft = (x,y))
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (self.rect.x, self.rect.y))
pygame.init()
clock = pygame.time.Clock()
screenWidth = 400
screenHeight = 400
game_window = pygame.display.set_mode((screenWidth,screenHeight))
player = Player(200,200)
map = Map()
enemy = Enemy(250,250)
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
player.handle_keys(screenHeight,screenWidth)
game_window.fill((0,0,0))
map.draw(game_window,screenHeight,screenWidth)
#enemy.draw(game_window)
player.draw(game_window)
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()
quit()
Thanks
Shay
The player's movement depends on the size of the map. The x and y attributes of the "player" store the position on the map and are limited to the size of the map (map_size). The player is always drawn in the center of the screen, except it is near the boarders of the map:
class Player():
def __init__(self, x, y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def handle_keys(self, map_size):
key = pygame.key.get_pressed()
self.x += (key[K_RIGHT] - key[K_LEFT]) * 500 * time_passed
self.y += (key[K_DOWN] - key[K_UP]) * 500 * time_passed
self.x = max(0, min(map_size[0]-20, self.x))
self.y = max(0, min(map_size[1]-20, self.y))
def draw(self, game_window, map_size):
window_size = game_window.get_size()
center = window_size[0] // 2, window_size[0] // 2
pos = [self.x, self.y]
for i in range(2):
if center[i] < pos[i] <= map_size[i]-center[i]:
pos[i] = center[i]
elif pos[i] > map_size[i] - center[i]:
pos[i] = window_size[i] - map_size[i] + pos[i]
game_window.blit(self.Image, (int(pos[0]), int(pos[1])))
The player's position on the map is centered in the window:
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
def draw(self, game_window):
window_size = game_window.get_size()
map_size = self.Image.get_size()
x = max(0, min(map_size[0] - window_size[0], player.x - 200))
y = max(0, min(map_size[1] - window_size[1], player.y - 200))
game_window.blit(self.Image, (-x, -y))
Application loop:
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
leave = True
player.handle_keys(map.Image.get_size())
game_window.fill((0,0,0))
map.draw(game_window)
#enemy.draw(game_window)
player.draw(game_window, map.Image.get_size())
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()