Rotating character and sprite wall - python

I have a sprite that represents my character. This sprite rotates every frame according to my mouse position which in turn makes it so my rectangle gets bigger and smaller depending on where the mouse is.
Basically what I want is to make it so my sprite (Character) doesn't go into the sprite walls. Now since the rect for the walls are larger then the actual pictures seems and my rect keeps growing and shrinking depending on my mouse position it leaves me clueless as for how to make a statement that stops my sprite from moving into the walls in a convincing manner.
I already know for sure that my ColideList is only the blocks that are supposed to be collided with. I found Detecting collision of two sprites that can rotate, but it's in Java and I don't need to check collision between two rotating sprites but one and a wall.
My Character class looks like this:
class Character(pygame.sprite.Sprite):
walking_frame = []
Max_Hp = 100
Current_HP = 100
Alive = True
X_Speed = 0
Y_Speed = 0
Loc_x = 370
Loc_y = 430
size = 15
Current_Weapon = Weapon()
Angle = 0
reloading = False
shot = False
LastFrame = 0
TimeBetweenFrames = 0.05
frame = 0
Walking = False
Blocked = 0
rel_path = "Sprite Images/All.png"
image_file = os.path.join(script_dir, rel_path)
sprite_sheet = SpriteSheet(image_file) #temp
image = sprite_sheet.get_image(0, 0, 48, 48) #Temp
image = pygame.transform.scale(image, (60, 60))
orgimage = image
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.walking_frame.append(self.image)
image = self.sprite_sheet.get_image(48, 0, 48, 48)
self.walking_frame.append(image)
image = self.sprite_sheet.get_image(96, 0, 48, 48)
self.walking_frame.append(image)
image = self.sprite_sheet.get_image(144, 0, 48, 48)
self.walking_frame.append(image)
image = self.sprite_sheet.get_image(0, 48, 48, 48)
self.walking_frame.append(image)
image = self.sprite_sheet.get_image(48, 48, 48, 48)
self.walking_frame.append(image)
image = self.sprite_sheet.get_image(96, 48, 48, 48)
self.walking_frame.append(image)
image = self.sprite_sheet.get_image(144, 48, 48, 48)
self.walking_frame.append(image)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = [self.Loc_x,self.Loc_y]
print "Shabat Shalom"
def Shoot(self):
if self.Alive:
if(self.reloading == False):
if(self.Current_Weapon.Clip_Ammo > 0):
bullet = Bullet(My_Man)
bullet_list.add(bullet)
self.Current_Weapon.Clip_Ammo -= 1
def move(self):
if self.Alive:
self.Animation()
self.Loc_x += self.X_Speed
self.Loc_y += self.Y_Speed
Wall_hit_List = pygame.sprite.spritecollide(My_Man, CollideList, False)
self.Blocked = 0
for wall in Wall_hit_List:
if self.rect.right <= wall.rect.left and self.rect.right >= wall.rect.right:
self.Blocked = 1 #right
self.X_Speed= 0
elif self.rect.left <= wall.rect.right and self.rect.left >= wall.rect.left:
self.Blocked = 3 #Left
self.X_Speed = 0
elif self.rect.top <= wall.rect.bottom and self.rect.top >= wall.rect.top:
self.Blocked = 2 #Up
self.Y_Speed = 0
elif self.rect.top >= wall.rect.bottom and self.rect.top <= wall.rect.top:
self.Blocked = 4 #Down
self.Y_Speed = 0
self.image = pygame.transform.rotate(self.orgimage, self.Angle)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = [self.Loc_x, self.Loc_y]
def Animation(self):
# #Character Walk Animation
if self.X_Speed != 0 or self.Y_Speed != 0:
if(self.Walking == False):
self.LastFrame = time.clock()
self.Walking = True
if (self.frame < len(self.walking_frame)):
self.image = self.walking_frame[self.frame]
self.image = pygame.transform.scale(self.image, (60, 60))
self.orgimage = self.image
self.frame += 1
else:
self.frame = 0
else:
if self.frame != 0:
self.frame = 0
self.image = self.walking_frame[self.frame]
self.image = pygame.transform.scale(self.image, (60, 60))
self.orgimage = self.image
if self.Walking and time.clock() - self.LastFrame > self.TimeBetweenFrames:
self.Walking = False
def CalAngle(self,X,Y):
angle = math.atan2(self.Loc_x - X, self.Loc_y - Y)
self.Angle = math.degrees(angle) + 180
My Wall class looks like this:
class Wall(pygame.sprite.Sprite):
def __init__(self, PosX, PosY, image_file, ImageX,ImageY):
pygame.sprite.Sprite.__init__(self)
self.sprite_sheet = SpriteSheet(image_file)
self.image = self.sprite_sheet.get_image(ImageX, ImageY, 64, 64)
self.image = pygame.transform.scale(self.image, (32, 32))
self.image.set_colorkey(Black)
self.rect = self.image.get_rect()
self.rect.x = PosX
self.rect.y = PosY
My BuildWall function looks like this:
def BuildWall(NumberOfBlocks,TypeBlock,Direction,X,Y,Collide):
for i in range(NumberOfBlocks):
if Direction == 1:
wall = Wall(X + (i * 32), Y, spriteList, 0, TypeBlock)
wall_list.add(wall)
if Direction == 2:
wall = Wall(X - (i * 32), Y, spriteList, 0, TypeBlock)
wall_list.add(wall)
if Direction == 3:
wall = Wall(X, Y + (i * 32), spriteList, 0, TypeBlock)
wall_list.add(wall)
if Direction == 4:
wall = Wall(X, Y - (i * 32), spriteList, 0, TypeBlock)
wall_list.add(wall)
if(Collide):
CollideList.add(wall)
Lastly my walking events looks like this:
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: #Press escape also leaves game
Game = False
elif event.key == pygame.K_w and My_Man.Blocked != 2:
My_Man.Y_Speed = -3
elif event.key == pygame.K_s and My_Man.Blocked != 4:
My_Man.Y_Speed = 3
elif event.key == pygame.K_a and My_Man.Blocked != 3:
My_Man.X_Speed = -3
elif event.key == pygame.K_d and My_Man.Blocked != 1:
My_Man.X_Speed = 3
elif event.key == pygame.K_r and (My_Man.reloading == False):
lastReloadTime = time.clock()
My_Man.reloading = True
if (My_Man.Current_Weapon.Name == "Pistol"):
My_Man.Current_Weapon.Clip_Ammo = My_Man.Current_Weapon.Max_Clip_Ammo
else:
My_Man.Current_Weapon.Clip_Ammo, My_Man.Current_Weapon.Max_Ammo = Reload(My_Man.Current_Weapon.Max_Ammo,My_Man.Current_Weapon.Clip_Ammo,My_Man.Current_Weapon.Max_Clip_Ammo)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_w:
My_Man.Y_Speed = 0
elif event.key == pygame.K_s:
My_Man.Y_Speed = 0
elif event.key == pygame.K_a:
My_Man.X_Speed = 0
elif event.key == pygame.K_d:
My_Man.X_Speed = 0

It all depends on how your sprite looks and how you want the result to be. There are 3 different types of collision detection I believe could work in your scenario.
Keeping your rect from resizing
Since the image is getting larger when you rotate it, you could compensate by just removing the extra padding and keep the image in it's original size.
Say that the size of the original image is 32 pixels wide and 32 pixels high. After rotating, the image is 36 pixels wide and 36 pixels high. We want to take out the center of the image (since the padding is added around it).
To take out the center of the new image we simply take out a subsurface of the image the size of our previous rectangle centered inside the image.
def rotate(self, degrees):
self.rotation = (self.rotation + degrees) % 360 # Keep track of the current rotation.
self.image = pygame.transform.rotate(self.original_image, self.rotation))
center_x = self.image.get_width() // 2
center_y = self.image.get_height() // 2
rect_surface = self.rect.copy() # Create a new rectangle.
rect_surface.center = (center_x, center_y) # Move the new rectangle to the center of the new image.
self.image = self.image.subsurface(rect_surface) # Take out the center of the new image.
Since the size of the rectangle doesn't change we don't need to do anything to recalculate it (in other words: self.rect = self.image.get_rect() will not be necessary).
Rectangular detection
From here you just use pygame.sprite.spritecollide (or if you have an own function) as usual.
def collision_rect(self, walls):
last = self.rect.copy() # Keep track on where you are.
self.rect.move_ip(*self.velocity) # Move based on the objects velocity.
current = self.rect # Just for readability we 'rename' the objects rect attribute to 'current'.
for wall in pygame.sprite.spritecollide(self, walls, dokill=False):
wall = wall.rect # Just for readability we 'rename' the wall's rect attribute to just 'wall'.
if last.left >= wall.right > current.left: # Collided left side.
current.left = wall.right
elif last.right <= wall.left < current.right: # Collided right side.
current.right = wall.left
elif last.top >= wall.bottom > current.top: # Collided from above.
current.top = wall.bottom
elif last.bottom <= wall.top < current.bottom: # Collided from below.
current.bottom = wall.top
Circular collision
This probably will not work the best if you're tiling your walls, because you'll be able to go between tiles depending on the size of the walls and your character. It is good for many other things so I'll keep this in.
If you add the attribute radius to your player and wall you can use pygame.sprite.spritecollide and pass the callback function pygame.sprite.collide_circle. You don't need a radius attribute, it's optional. But if you don't pygame will calculate the radius based on the sprites rect attribute, which is unnecessary unless the radius is constantly changing.
def collision_circular(self, walls):
self.rect.move_ip(*self.velocity)
current = self.rect
for wall in pygame.sprite.spritecollide(self, walls, dokill=False, collided=pygame.sprite.collide_circle):
distance = self.radius + wall.radius
dx = current.centerx - wall.rect.centerx
dy = current.centery - wall.rect.centery
multiplier = ((distance ** 2) / (dx ** 2 + dy ** 2)) ** (1/2)
current.centerx = wall.rect.centerx + (dx * multiplier)
current.centery = wall.rect.centery + (dy * multiplier)
Pixel perfect collision
This is the hardest to implement and is performance heavy, but can give you the best result. We'll still use pygame.sprite.spritecollide, but this time we're going to pass pygame.sprite.collide_mask as the callback function. This method require that your sprites have a rect attribute and a per pixel alpha Surface or a Surface with a colorkey.
A mask attribute is optional, if there is none the function will create one temporarily. If you use a mask attribute you'll need to change update it every time your sprite image is changed.
The hard part of this kind of collision is not to detect it but to respond correctly and make it move/stop appropriately. I made a buggy example demonstrating one way to handle it somewhat decently.
def collision_mask(self, walls):
last = self.rect.copy()
self.rect.move_ip(*self.velocity)
current = self.rect
for wall in pygame.sprite.spritecollide(self, walls, dokill=False, collided=pygame.sprite.collide_mask):
if not self.rect.center == last.center:
self.rect.center = last.center
break
wall = wall.rect
x_distance = current.centerx - wall.centerx
y_distance = current.centery - wall.centery
if abs(x_distance) > abs(y_distance):
current.centerx += (x_distance/abs(x_distance)) * (self.velocity[0] + 1)
else:
current.centery += (y_distance/abs(y_distance)) * (self.velocity[1] + 1)
Full code
You can try out the different examples by pressing 1 for rectangular collision, 2 for circular collision and 3 for pixel-perfect collision. It's a little buggy in some places, the movement isn't top notch and isn't ideal performance wise, but it's just a simple demonstration.
import pygame
pygame.init()
SIZE = WIDTH, HEIGHT = (256, 256)
clock = pygame.time.Clock()
screen = pygame.display.set_mode(SIZE)
mode = 1
modes = ["Rectangular collision", "Circular collision", "Pixel perfect collision"]
class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super(Player, self).__init__()
self.original_image = pygame.Surface((32, 32))
self.original_image.set_colorkey((0, 0, 0))
self.image = self.original_image.copy()
pygame.draw.ellipse(self.original_image, (255, 0, 0), pygame.Rect((0, 8), (32, 16)))
self.rect = self.image.get_rect(center=pos)
self.rotation = 0
self.velocity = [0, 0]
self.radius = self.rect.width // 2
self.mask = pygame.mask.from_surface(self.image)
def rotate_clipped(self, degrees):
self.rotation = (self.rotation + degrees) % 360 # Keep track of the current rotation
self.image = pygame.transform.rotate(self.original_image, self.rotation)
center_x = self.image.get_width() // 2
center_y = self.image.get_height() // 2
rect_surface = self.rect.copy() # Create a new rectangle.
rect_surface.center = (center_x, center_y) # Move the new rectangle to the center of the new image.
self.image = self.image.subsurface(rect_surface) # Take out the center of the new image.
self.mask = pygame.mask.from_surface(self.image)
def collision_rect(self, walls):
last = self.rect.copy() # Keep track on where you are.
self.rect.move_ip(*self.velocity) # Move based on the objects velocity.
current = self.rect # Just for readability we 'rename' the objects rect attribute to 'current'.
for wall in pygame.sprite.spritecollide(self, walls, dokill=False):
wall = wall.rect # Just for readability we 'rename' the wall's rect attribute to just 'wall'.
if last.left >= wall.right > current.left: # Collided left side.
current.left = wall.right
elif last.right <= wall.left < current.right: # Collided right side.
current.right = wall.left
elif last.top >= wall.bottom > current.top: # Collided from above.
current.top = wall.bottom
elif last.bottom <= wall.top < current.bottom: # Collided from below.
current.bottom = wall.top
def collision_circular(self, walls):
self.rect.move_ip(*self.velocity)
current = self.rect
for wall in pygame.sprite.spritecollide(self, walls, dokill=False, collided=pygame.sprite.collide_circle):
distance = self.radius + wall.radius
dx = current.centerx - wall.rect.centerx
dy = current.centery - wall.rect.centery
multiplier = ((distance ** 2) / (dx ** 2 + dy ** 2)) ** (1/2)
current.centerx = wall.rect.centerx + (dx * multiplier)
current.centery = wall.rect.centery + (dy * multiplier)
def collision_mask(self, walls):
last = self.rect.copy()
self.rect.move_ip(*self.velocity)
current = self.rect
for wall in pygame.sprite.spritecollide(self, walls, dokill=False, collided=pygame.sprite.collide_mask):
if not self.rect.center == last.center:
self.rect.center = last.center
break
wall = wall.rect
x_distance = current.centerx - wall.centerx
y_distance = current.centery - wall.centery
if abs(x_distance) > abs(y_distance):
current.centerx += (x_distance/abs(x_distance)) * (self.velocity[0] + 1)
else:
current.centery += (y_distance/abs(y_distance)) * (self.velocity[1] + 1)
def update(self, walls):
self.rotate_clipped(1)
if mode == 1:
self.collision_rect(walls)
elif mode == 2:
self.collision_circular(walls)
else:
self.collision_mask(walls)
class Wall(pygame.sprite.Sprite):
def __init__(self, pos):
super(Wall, self).__init__()
size = (32, 32)
self.image = pygame.Surface(size)
self.image.fill((0, 0, 255)) # Make the Surface blue.
self.image.set_colorkey((0, 0, 0)) # Will not affect the image but is needed for collision with mask.
self.rect = pygame.Rect(pos, size)
self.radius = self.rect.width // 2
self.mask = pygame.mask.from_surface(self.image)
def show_rects(player, walls):
for wall in walls:
pygame.draw.rect(screen, (1, 1, 1), wall.rect, 1)
pygame.draw.rect(screen, (1, 1, 1), player.rect, 1)
def show_circles(player, walls):
for wall in walls:
pygame.draw.circle(screen, (1, 1, 1), wall.rect.center, wall.radius, 1)
pygame.draw.circle(screen, (1, 1, 1), player.rect.center, player.radius, 1)
def show_mask(player, walls):
for wall in walls:
pygame.draw.rect(screen, (1, 1, 1), wall.rect, 1)
for pixel in player.mask.outline():
pixel_x = player.rect.x + pixel[0]
pixel_y = player.rect.y + pixel[1]
screen.set_at((pixel_x, pixel_y), (1, 1, 1))
# Create walls around the border.
walls = pygame.sprite.Group()
walls.add(Wall(pos=(col, 0)) for col in range(0, WIDTH, 32))
walls.add(Wall(pos=(0, row)) for row in range(0, HEIGHT, 32))
walls.add(Wall(pos=(col, HEIGHT - 32)) for col in range(0, WIDTH, 32))
walls.add(Wall(pos=(WIDTH - 32, row)) for row in range(0, HEIGHT, 32))
walls.add(Wall(pos=(WIDTH//2, HEIGHT//2))) # Obstacle in the middle of the screen
player = Player(pos=(64, 64))
speed = 2 # Speed of the player.
while True:
screen.fill((255, 255, 255))
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.velocity[0] = -speed
elif event.key == pygame.K_d:
player.velocity[0] = speed
elif event.key == pygame.K_w:
player.velocity[1] = -speed
elif event.key == pygame.K_s:
player.velocity[1] = speed
elif pygame.K_1 <= event.key <= pygame.K_3:
mode = event.key - 48
print(modes[mode - 1])
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_d:
player.velocity[0] = 0
elif event.key == pygame.K_w or event.key == pygame.K_s:
player.velocity[1] = 0
player.update(walls)
walls.draw(screen)
screen.blit(player.image, player.rect)
if mode == 1:
show_rects(player, walls) # Show rectangles for circular collision detection.
elif mode == 2:
show_circles(player, walls) # Show circles for circular collision detection.
else:
show_mask(player, walls) # Show mask for pixel perfect collision detection.
pygame.display.update()
Last note
Before programming any further you really need to refactor your code. I tried to read some of your code but it's really hard to understand. Try follow Python's naming conventions, it'll make it much easier for other programmers to read and understand your code, which makes it easier for them to help you with your questions.
Just following these simple guidelines will make your code much readable:
Variable names should contain only lowercase letters. Names with more than 1 word should be separated with an underscore. Example: variable, variable_with_words.
Functions and attributes should follow the same naming convention as variables.
Class names should start with an uppercase for every word and the rest should be lowercase. Example: Class, MyClass. Known as CamelCase.
Separate methods in classes with one line, and functions and classes with two lines.
I don't know what kind of IDE you use, but Pycharm Community Edition is a great IDE for Python. It'll show you when you're breaking Python conventions (and much more of course).
It's important to note that these are conventions and not rules. They are meant to make code more readable and not to be followed strictly. Break them if you think it improves readability.

Related

How can you pre-calculate the balls trajectory and therefore its endpoint in the game Pong?

I am making the classic Pong in Python, but I am in need of help.
For/in this game I want to calculate the trajectory of the ball, so for a given beginPoint(bounce on the left paddle) and angle(green thing) I want to calculate the endpoint(The blue X). This all in order to move the right paddle to the ball so you can play Pong on your own.
Here is an image of the problem to make it more clear:
https://imgur.com/OySberQ
I've tried to come up with some type of algorithm using trigonometry, but could only calculate the first point and then get stuck:
def calculateTragectory(beginY, angle):
distanceX = 0
direction = ball.directionY
while True:
if direction == -1:
distanceX += (height - beginY) * math.tan(angle)
direction == 1
elif direction == 1:
distanceX += beginY * math.tan(angle)
direction == -1
Here is the rest of the code:
import pygame
import random
import math
pygame.init()
width = 1000
height = 600
leftScore = 0
rightScore = 0
delay = 2
paddleSpeed = 2
ballSpeed = 1
paddleWidth = 15
paddleHeight = 70
ballRadius = 10
spacingX = 3*paddleWidth
window = pygame.display.set_mode((width, height))
font = pygame.font.SysFont('Arial', 128)
class Paddle:
def __init__(self, side):
# left = true, right = false
if side:
self.x = spacingX
else: self.x = width-spacingX
self.y = 300
def move(self, UpOrDown):
if UpOrDown:
self.y -= paddleSpeed
else: self.y += paddleSpeed
def draw(self):
pygame.draw.rect(window, (255, 255, 255), (self.x, self.y, paddleWidth, paddleHeight))
class Ball():
def __init__(self):
self.x = width/2
self.y = height/2
self.directionX = -1
self.directionY = 1
def move(self):
if self.y > height or self.y < 0:
self.directionY *=-1
self.x += ballSpeed*self.directionX
self.y += ballSpeed*self.directionY
def draw(self):
pygame.draw.circle(window, (255, 255, 255), (int(self.x), int(self.y)), ballRadius)
def reset(self):
self.x = width/2
self.y = random.uniform(50, 550)
self.directionX *= -1
def keyInput():
# Key inputs True = Up, down is False
keys = pygame.key.get_pressed()
if keys[pygame.K_w] and leftPaddle.y > 0:
leftPaddle.move(True)
if keys[pygame.K_s] and leftPaddle.y < height - paddleHeight:
leftPaddle.move(False)
if keys[pygame.K_SPACE]:
ball.x = width/2
ball.y = height/2
def collisionDetection():
if ball.x == leftPaddle.x + paddleWidth + ballRadius and leftPaddle.y <= ball.y <= leftPaddle.y + paddleHeight:
ball.directionX *= -1
if ball.x == computerPaddle.x - ballRadius and computerPaddle.y <= ball.y <= computerPaddle.y + paddleHeight:
ball.directionX *= -1
def scoreUpdate():
global rightScore, leftScore
if ball.x <= 0:
rightScore += 1
ball.reset()
elif ball.x >= width:
leftScore +=1
ball.reset()
def calculateTragectory(beginY, angle):
distanceX = 0
direction = ball.directionY
while True:
if direction == -1:
distanceX += (height - beginY) * math.tan(angle)
direction == 1
elif direction == 1:
distanceX += beginY * math.tan(angle)
direction == -1
a = beginPoint * math.tan(angle)
#Init paddles
leftPaddle = Paddle(True)
computerPaddle = Paddle(False)
ball = Ball()
#Game loop:
while True:
pygame.time.delay(delay)
ball.move()
collisionDetection()
scoreUpdate()
scoreText_left = font.render(str(leftScore), True, (255, 255, 255))
scoreText_right = font.render(str(rightScore), True, (255, 255, 255))
keyInput()
window.fill((0, 0, 0))
window.blit(scoreText_left, (350-64, 50))
window.blit(scoreText_right, (650, 50))
leftPaddle.draw()
computerPaddle.y = ball.y
computerPaddle.draw()
ball.draw()
pygame.display.update()
#Exit
for event in pygame.event.get():
if event.type == pygame.QUIT:
if event.key == pygame.K_ESCAPE:
pygame.quit()
the following is the code you need:
# width and height are the width and height you want to calculate the endpoint for
def calculate_tragectory(begin_y, angle):
x_speed = math.cos(angle) # calculate the relative x of the angle
y_speed = math.sin(angle) # calculate the relative y of the angle
straight_pos = begin_y + width * y_speed / x_speed
# this is the final position if the ball wouldn't change direction
final_pos = abs(straight_pos) % (2 * height)
# a negative result just of straight_pos means it bounces one additional time
# but has no effect on the result.
# (that's why abs(straight_pos) instead of just straight_pos)
# if the result is 2 * height it means that the ball has bounced twice
# and has returned to y position 0.
if final_pos > height:
final_pos = 2 * height - final_pos
# if final_pos > height this means that the ball has bounced once and
# the final position is the remainder to 2 * height.
return final_pos
modifications you want to make:
you need to subtract the paddle spacing as well as the ball radius from the width.
make sure to set the middle of the paddle to this value not the top.
in your case you don't actually want to input an angle but rather x and y speeds:
def calculate_tragectory(begin_y, x_speed, y_speed):
# input x_speed and y_speed instead of calculating them from the angle
...
general tips to improve your code:
according to the python documentation variables and function should be named lowercase_with_underscores
the collision detection is quite crappy. it often visually collided but still went through the paddle.

Creating a Auto targeting bullet

I have been creating a typical type of space invader game using Python and pygame sprites. I have done most of the things and it's working fine. But actually in my game, the bullet always shoots straight, but I want it to target the enemy and always shoot where the enemy is.
In my player update I am just doing that whenever space is hit, it fires the bullet.
if (keys[pygame.K_SPACE]):
self.fire()
This below is my fire method which is just calling the Handgun (which is my bullet class):
def fire(self):
now = pygame.time.get_ticks()
self.shoot_delay = 600
self.shot_position = handguns.rect.x - enemy.rect.x
print (self.shot_position)
if (now - self.last_shot > self.shoot_delay):
self.last_shot = now
shot = HandGun(self.rect.centerx, self.rect.top)
Every_Sprite.add(shot)
handgun.add(shot)
This below is my enemy class where the position is just randomised:
class Enemy_Agent(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join(img_folder, "ship2.png")).convert()
self.image.set_colorkey(WHITE)
self.rect =self.image.get_rect()
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = random.randrange(1,8)
self.speedx = random.randrange(-3,3)
def update(self):
if (self.rect.right > width):
self.rect.right = width
if (self.rect.left < 0):
self.rect.left = 0
self.rect.y += self.speed
#print("pos: ", self.rect.y)
self.rect.x += self.speedx
if(self.rect.top > Height - 10 or self.rect.left < -30 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, 8)
And, last this my HandGun class. If anyone can help me and advise me to make the bullet target the enemy it would be a great help.
class HandGun(pygame.sprite.Sprite):
def __init__(self, cx, cy):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join(img_folder, "bullet.png")).convert()
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
self.rect.bottom = cy
self.rect.centerx =cx
self.speedy = -1
self.speedx = None
def update(self):
self.rect.y += self.speedy + enemy.rect.centerx
if(self.rect.bottom < 0):
self.kill()
pass
Here's a minimal example in which I just shoot bullets from the mouse position towards a target.
First we need a vector that points to the target (called direction here), therefore we subtract the mouse position from the target position.
We use the angle of the direction vector (which you can get with the as_polar method (polar coordinates)) to rotate the bullet.
To get the velocity vector, we can normalize the direction and multiply it by a scalar to scale it to the desired length (i.e. the speed).
import pygame as pg
from pygame.math import Vector2
BACKGROUND_COLOR = pg.Color(30, 30, 50)
BLUE = pg.Color('dodgerblue1')
LIME = pg.Color(192, 255, 0)
class Bullet(pg.sprite.Sprite):
""" This class represents the bullet. """
def __init__(self, pos, target, screen_rect):
"""Take the pos, direction and angle of the player."""
super().__init__()
self.image = pg.Surface((16, 10), pg.SRCALPHA)
pg.draw.polygon(self.image, LIME, ((0, 0), (16, 5), (0, 10)))
# The `pos` parameter is the center of the bullet.rect.
self.rect = self.image.get_rect(center=pos)
self.position = Vector2(pos) # The position of the bullet.
# This vector points from the mouse pos to the target.
direction = target - pos
# The polar coordinates of the direction vector.
radius, angle = direction.as_polar()
# Rotate the image by the negative angle (because the y-axis is flipped).
self.image = pg.transform.rotozoom(self.image, -angle, 1)
# The velocity is the normalized direction vector scaled to the desired length.
self.velocity = direction.normalize() * 11
self.screen_rect = screen_rect
def update(self):
"""Move the bullet."""
self.position += self.velocity # Update the position vector.
self.rect.center = self.position # And the rect.
# Remove the bullet when it leaves the screen.
if not self.screen_rect.contains(self.rect):
self.kill()
def main():
pg.init()
screen = pg.display.set_mode((800, 600))
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
bullet_group = pg.sprite.Group()
target = Vector2(400, 300)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
# Shoot a bullet. Pass the start position (in this
# case the mouse position) and the direction vector.
bullet = Bullet(event.pos, target, screen_rect)
all_sprites.add(bullet)
bullet_group.add(bullet)
all_sprites.update()
screen.fill(BACKGROUND_COLOR)
all_sprites.draw(screen)
pg.draw.rect(screen, BLUE, (target, (3, 3)), 1)
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
pg.quit()
If you're not familiar with vectors, you can use trigonometry as well.
If you want a homing missile, you have to pass the current target position to the bullet and recompute the direction and velocity each frame.
To aim at a moving target, you need to calculate the future position where the projectile will hit the target. You can do that with a quadratic equation. I'm using Jeffrey Hantin's solution from this answer here. You have to pass the start position of the bullet, its speed and the target position and velocity to the intercept function and then solve the quadratic equation. It will return the position vector where the bullet and the target meet. Then just shoot at this point instead of the current target point (you can still use the same code in the Bullet class).
import math
import pygame as pg
from pygame.math import Vector2
BACKGROUND_COLOR = pg.Color(30, 30, 50)
BLUE = pg.Color('dodgerblue1')
LIME = pg.Color(192, 255, 0)
class Bullet(pg.sprite.Sprite):
""" This class represents the bullet. """
def __init__(self, pos, target, screen_rect):
"""Take the pos, direction and angle of the player."""
super().__init__()
self.image = pg.Surface((16, 10), pg.SRCALPHA)
pg.draw.polygon(self.image, LIME, ((0, 0), (16, 5), (0, 10)))
# The `pos` parameter is the center of the bullet.rect.
self.rect = self.image.get_rect(center=pos)
self.position = Vector2(pos) # The position of the bullet.
# This vector points from the mouse pos to the target.
direction = target - pos
# The polar coordinates of the direction vector.
radius, angle = direction.as_polar()
# Rotate the image by the negative angle (because the y-axis is flipped).
self.image = pg.transform.rotozoom(self.image, -angle, 1)
# The velocity is the normalized direction vector scaled to the desired length.
self.velocity = direction.normalize() * 11
self.screen_rect = screen_rect
def update(self):
"""Move the bullet."""
self.position += self.velocity # Update the position vector.
self.rect.center = self.position # And the rect.
# Remove the bullet when it leaves the screen.
if not self.screen_rect.contains(self.rect):
self.kill()
def intercept(position, bullet_speed, target, target_velocity):
a = target_velocity.x**2 + target_velocity.y**2 - bullet_speed**2
b = 2 * (target_velocity.x * (target.x - position.x) + target_velocity.y * (target.y - position.y))
c = (target.x - position.x)**2 + (target.y - position.y)**2
discriminant = b*b - 4*a*c
if discriminant < 0:
print("Target can't be reached.")
return None
else:
t1 = (-b + math.sqrt(discriminant)) / (2*a)
t2 = (-b - math.sqrt(discriminant)) / (2*a)
t = max(t1, t2)
x = target_velocity.x * t + target.x
y = target_velocity.y * t + target.y
return Vector2(x, y)
def main():
pg.init()
screen = pg.display.set_mode((800, 600))
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
bullet_group = pg.sprite.Group()
target = Vector2(50, 300)
target_velocity = Vector2(4, 3)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
target_vector = intercept(Vector2(event.pos), 11, target, target_velocity)
# Shoot a bullet. Pass the start position (in this
# case the mouse position) and the target position vector.
if target_vector is not None: # Shoots only if the target can be reached.
bullet = Bullet(event.pos, target_vector, screen_rect)
all_sprites.add(bullet)
bullet_group.add(bullet)
target += target_velocity
if target.x >= screen_rect.right or target.x < 0:
target_velocity.x *= -1
if target.y >= screen_rect.bottom or target.y < 0:
target_velocity.y *= -1
all_sprites.update()
screen.fill(BACKGROUND_COLOR)
all_sprites.draw(screen)
pg.draw.rect(screen, BLUE, (target, (5, 5)))
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
pg.quit()
Actually, it would be better to use broofa's solution because it takes some special cases into account.
def intercept(position, bullet_speed, target, target_velocity):
tx, ty = target - position
tvx, tvy = target_velocity
v = bullet_speed
dstx, dsty = target
a = tvx*tvx + tvy*tvy - v*v
b = 2 * (tvx*tx + tvy*ty)
c = tx*tx + ty*ty
ts = quad(a, b, c)
sol = None
if ts:
t0 = ts[0]
t1 = ts[1]
t = min(t0, t1)
if t < 0:
t = max(t0, t1)
if t > 0:
sol = Vector2(dstx + tvx * t,
dsty + tvy * t)
return sol
def quad(a, b, c):
sol = None
if abs(a) < 1e-6:
if abs(b) < 1e-6:
sol = [0, 0] if abs(c) < 1e-6 else None
else:
sol = [-c/b, -c/b]
else:
disc = b*b - 4*a*c
if disc >= 0:
disc = math.sqrt(disc)
a = 2*a
sol = [(-b-disc)/a, (-b+disc)/a]
return sol

Python Pygame Collision [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 2 years ago.
Walls do block me but if i keep walking into them i teleport through. I've tried a number of things and cant seem to figure it out. Things that make it difficult is that my character rotates based on where my mouse position is. self.level_terrain is a sprite.group. update is called from my state manager.
def update(self, clock):
self.dT = clock.get_time()
pressed = pg.key.get_pressed()
mousePos = pg.mouse.get_pos()
self.player.update(mousePos, pressed, self.dT, self.level_terrain)
def get_angle(origin, destination):
"""Returns angle in radians from origin to destination.
This is the angle that you would get if the points were
on a cartesian grid. Arguments of (0,0), (1, -1)
return pi/4 (45 deg) rather than 7/4.
"""
x_dist = destination[0] - origin[0]
y_dist = destination[1] - origin[1]
return atan2(-y_dist, x_dist) % (2 * pi)
class Player(pg.sprite.Sprite):
def __init__(self, center_pos):
super(Player, self).__init__()
self.original_image = pg.image.load("data/resources/images/playerImage.png").convert_alpha()
self.facing_angle = 90
self.start_angle = 90
self.pos = center_pos
self.velocity = [0, 0]
self.set_image()
self.vx = 0
self.vy = 0
self.keys_dict = {
pg.K_w: (0, -1),
pg.K_s: (0, 1),
pg.K_a: (-1, 0),
pg.K_d: (1, 0)}
self.speed = .2
def set_image(self):
angle = self.facing_angle - self.start_angle
self.image = pg.transform.rotate(self.original_image, angle)
self.rect = self.image.get_rect()
def update(self, mouse_pos, pressed, dt, obstacles):
self.facing_angle = degrees(get_angle(self.pos, mouse_pos))
self.set_image()
last = self.rect.copy()
for k in self.keys_dict:
if pressed[k]:
x, y = self.keys_dict[k]
self.vx = x * self.speed * dt
self.vy = y * self.speed * dt
self.pos = (self.pos[0] + (self.vx),
self.pos[1] + (self.vy))
self.rect.center = self.pos
current = self.rect # Just for readability we 'rename' the objects rect attribute to 'current'.
for wall in pg.sprite.spritecollide(self, obstacles, dokill=False):
wall = wall.rect # Just for readability we 'rename' the wall's rect attribute to just 'wall'.
if last.left >= wall.right > current.left: # Collided left side.
current.left = wall.right
elif last.right <= wall.left < current.right: # Collided right side.
current.right = wall.left
elif last.top >= wall.bottom > current.top: # Collided from above.
current.top = wall.bottom
elif last.bottom <= wall.top < current.bottom: # Collided from below.
current.bottom = wall.top
def draw(self, surface):
surface.blit(self.image, self.rect)

Pygame Sprite can jump but can't walk

I've been working through this online tutorial on Pygame (Python Version 3.3.1), and have come to a point where my sprite can jump, but can walk only a few pixels in either direction. I'd really like to move forward with this code as I like how it is structured (not a lot of code in the Main method). Can anyone spot what might be causing my sprite to get stuck?
import pygame
# Define some colors
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
class Player(pygame.sprite.Sprite):
def __init__(self, *groups):
super(Player, self).__init__(groups)
self.image = pygame.image.load('Images\player1.png')
self.rect = pygame.rect.Rect((50, 650), self.image.get_size())
self.resting = False
self.dy = 0 #dy represents change in y velocity
def update(self, dt, game):
last = self.rect.copy()
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.rect.x -= 300 * dt
if key[pygame.K_RIGHT]:
self.rect.x += 300 * dt
#if key[pygame.K_UP]:
# self.rect.y -= 300 * dt
#if key[pygame.K_DOWN]:
# self.rect.y += 300 * dt
if self.resting and key[pygame.K_SPACE]:
self.dy = -500 #If space bar is pressed, increase velocity.
self.dy = min(400, self.dy + 40) #Speed capped at 400. Gravity set at 40.
self.rect.y += self.dy * dt
new = self.rect
self.resting = False
for cell in pygame.sprite.spritecollide(self, game.walls, False):
#self.rect = last
cell = cell.rect
if last.right <= cell.left and new.right > cell.left:
new.right = cell.left
if last.left >= cell.right and new.left < cell.right:
new.left = cell.right
if last.bottom <= cell.top and new.bottom > cell.top:
#if you hit something while jumping, stop.
self.resting = True
new.bottom = cell.top
self.dy = 0
if last.top >= cell.bottom and new.top < cell.bottom:
new.top = cell.bottom
self.dy = 0 #If you hit the floor while jumping, stop
class Game(object):
def main(self, screen):
clock = pygame.time.Clock()
dt = clock.tick(30)
#image = pygame.image.load('Images\player1.gif')
background = pygame.image.load('Images\_rec_bg.png')
sprites = pygame.sprite.Group()
self.player = Player(sprites)
self.walls = pygame.sprite.Group()
block = pygame.image.load('Images\dia_tile.png')
for x in range(0, 800, 20):
for y in range(0, 800, 20):
if x in (0, 800-20) or y in (0, 800-20):
wall = pygame.sprite.Sprite(self.walls)
wall.image = block
wall.rect = pygame.rect.Rect((x, y), block.get_size())
sprites.add(self.walls)
running = True
while running:
clock.tick(30) #run no more than 30 time per second
dt - clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
running = False
#sprites.update()
#sprites.update(dt / 1000.)
#screen.fill(black)
sprites.update(dt / 1000., self)
#screen.blit(image, (320, 240)) #Transfer to video RAM
screen.blit(background, (0,0))
sprites.draw(screen)
pygame.display.flip() #Dispaly to Screen
if __name__ == '__main__':
pygame.init()
screen = pygame.display.set_mode((800, 800))
Game().main(screen)
Im not sure but it looks like you're only letting the player move from side to side if the player is in the air (jumping)
If i'm right you need to enable youre player to move side to side even when self.resting = True
what happens if you do something like this:
if key[pygame.K_LEFT]:
self.rect.x -= 75
if key[pygame.K_RIGHT]:
self.rect.x += 75
the thing about that is the player will move from side to sdie but when you jump and press right or left the player will fly all over instead of going into a steady jump and fall
so you need to figure out how to include the *dt to regulate the players movement in air but at the same time have the player be able to move while on the ground to
try having two sets of if statments:
if key[pygame.K_LEFT]:
self.rect.x -= 300 * dt
if key[pygame.K_RIGHT]:
self.rect.x += 300 *dt
if key[pygame.K_LEFT]and self.resting:
self.rect.x -= 50
if key[pygame.K_RIGHT]and self.resting:
self.rect.x += 50
the first one is for in the air the second is for when the player is on the ground
try that and tell me if it works
i tried it on my computer and it worked but i probably used differnt art that could also be the problem becuase it might be blitting behind something so check everything
Good Luck!!

Add collison detection to enemy sprites?

i'd like to add the same collision detection used by the player sprite to the enemy sprites or 'creeps' ive added all the relevant code I can see yet collisons are still not being detected and handled, please find below the class, I have no idea what is wrong currently, the list of walls to collide with is 'wall_list'
import pygame
import pauseScreen as dm
import re
from pygame.sprite import Sprite
from pygame import Rect, Color
from random import randint, choice
from vec2d import vec2d
from simpleanimation import SimpleAnimation
import displattxt
black = (0,0,0)
white = (255,255,255)
blue = (0,0,255)
green = (101,194,151)
global currentEditTool
currentEditTool = "Tree"
global editMap
editMap = False
open('MapMaker.txt', 'w').close()
def draw_background(screen, tile_img):
screen.fill(black)
img_rect = tile_img.get_rect()
global rect
rect = img_rect
nrows = int(screen.get_height() / img_rect.height) + 1
ncols = int(screen.get_width() / img_rect.width) + 1
for y in range(nrows):
for x in range(ncols):
img_rect.topleft = (x * img_rect.width,
y * img_rect.height)
screen.blit(tile_img, img_rect)
def changeTool():
if currentEditTool == "Tree":
None
elif currentEditTool == "Rock":
None
def pauseGame():
red = 255, 0, 0
green = 0,255, 0
blue = 0, 0,255
screen.fill(black)
pygame.display.update()
if editMap == False:
choose = dm.dumbmenu(screen, [
'Resume',
'Enable Map Editor',
'Quit Game'], 64,64,None,32,1.4,green,red)
if choose == 0:
print("hi")
elif choose ==1:
global editMap
editMap = True
elif choose ==2:
print("bob")
elif choose ==3:
print("bob")
elif choose ==4:
print("bob")
else:
None
else:
choose = dm.dumbmenu(screen, [
'Resume',
'Disable Map Editor',
'Quit Game'], 64,64,None,32,1.4,green,red)
if choose == 0:
print("Resume")
elif choose ==1:
print("Dis ME")
global editMap
editMap = False
elif choose ==2:
print("bob")
elif choose ==3:
print("bob")
elif choose ==4:
print("bob")
else:
None
class Wall(pygame.sprite.Sprite):
# Constructor function
def __init__(self,x,y,width,height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(green)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class insertTree(pygame.sprite.Sprite):
def __init__(self,x,y,width,height, typ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images/map/tree.png").convert()
self.image.set_colorkey(white)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class insertRock(pygame.sprite.Sprite):
def __init__(self,x,y,width,height, typ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images/map/rock.png").convert()
self.image.set_colorkey(white)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class Creep(pygame.sprite.Sprite):
""" A creep sprite that bounces off walls and changes its
direction from time to time.
"""
change_x=0
change_y=0
def __init__(
self, screen, creep_image, explosion_images,
field, init_position, init_direction, speed):
""" Create a new Creep.
screen:
The screen on which the creep lives (must be a
pygame Surface object, such as pygame.display)
creep_image:
Image (surface) object for the creep
explosion_images:
A list of image objects for the explosion
animation.
field:
A Rect specifying the 'playing field' boundaries.
The Creep will bounce off the 'walls' of this
field.
init_position:
A vec2d or a pair specifying the initial position
of the creep on the screen.
init_direction:
A vec2d or a pair specifying the initial direction
of the creep. Must have an angle that is a
multiple of 45 degres.
speed:
Creep speed, in pixels/millisecond (px/ms)
"""
Sprite.__init__(self)
self.screen = screen
self.speed = speed
self.field = field
self.rect = creep_image.get_rect()
# base_image holds the original image, positioned to
# angle 0.
# image will be rotated.
#
self.base_image = creep_image
self.image = self.base_image
self.explosion_images = explosion_images
# A vector specifying the creep's position on the screen
#
self.pos = vec2d(init_position)
# The direction is a normalized vector
#
self.direction = vec2d(init_direction).normalized()
self.state = Creep.ALIVE
self.health = 15
def is_alive(self):
return self.state in (Creep.ALIVE, Creep.EXPLODING)
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self, time_passed, walls):
""" Update the creep.
time_passed:
The time passed (in ms) since the previous update.
"""
if self.state == Creep.ALIVE:
# Maybe it's time to change the direction ?
#
self._change_direction(time_passed)
# Make the creep point in the correct direction.
# Since our direction vector is in screen coordinates
# (i.e. right bottom is 1, 1), and rotate() rotates
# counter-clockwise, the angle must be inverted to
# work correctly.
#
self.image = pygame.transform.rotate(
self.base_image, -self.direction.angle)
# Compute and apply the displacement to the position
# vector. The displacement is a vector, having the angle
# of self.direction (which is normalized to not affect
# the magnitude of the displacement)
#
displacement = vec2d(
self.direction.x * self.speed * time_passed,
self.direction.y * self.speed * time_passed)
self.pos += displacement
# When the image is rotated, its size is changed.
# We must take the size into account for detecting
# collisions with the walls.
#
self.image_w, self.image_h = self.image.get_size()
bounds_rect = self.field.inflate(
-self.image_w, -self.image_h)
if self.pos.x < bounds_rect.left:
self.pos.x = bounds_rect.left
self.direction.x *= -1
elif self.pos.x > bounds_rect.right:
self.pos.x = bounds_rect.right
self.direction.x *= -1
elif self.pos.y < bounds_rect.top:
self.pos.y = bounds_rect.top
self.direction.y *= -1
elif self.pos.y > bounds_rect.bottom:
self.pos.y = bounds_rect.bottom
self.direction.y *= -1
# collision detection
old_x=bounds_rect.left
new_x=old_x+self.direction.x
bounds_rect.left = new_x
# hit a wall?
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
bounds_rect.left=old_x
old_y=self.pos.y
new_y=old_y+self.direction.y
self.pos.y = new_y
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.pos.y=old_y
elif self.state == Creep.EXPLODING:
if self.explode_animation.active:
self.explode_animation.update(time_passed)
else:
self.state = Creep.DEAD
self.kill()
elif self.state == Creep.DEAD:
pass
#------------------ PRIVATE PARTS ------------------#
# States the creep can be in.
#
# ALIVE: The creep is roaming around the screen
# EXPLODING:
# The creep is now exploding, just a moment before dying.
# DEAD: The creep is dead and inactive
#
(ALIVE, EXPLODING, DEAD) = range(3)
_counter = 0
def _change_direction(self, time_passed):
""" Turn by 45 degrees in a random direction once per
0.4 to 0.5 seconds.
"""
self._counter += time_passed
if self._counter > randint(400, 500):
self.direction.rotate(45 * randint(-1, 1))
self._counter = 0
def _point_is_inside(self, point):
""" Is the point (given as a vec2d) inside our creep's
body?
"""
img_point = point - vec2d(
int(self.pos.x - self.image_w / 2),
int(self.pos.y - self.image_h / 2))
try:
pix = self.image.get_at(img_point)
return pix[3] > 0
except IndexError:
return False
def _decrease_health(self, n):
""" Decrease my health by n (or to 0, if it's currently
less than n)
"""
self.health = max(0, self.health - n)
if self.health == 0:
self._explode()
def _explode(self):
""" Starts the explosion animation that ends the Creep's
life.
"""
self.state = Creep.EXPLODING
pos = ( self.pos.x - self.explosion_images[0].get_width() / 2,
self.pos.y - self.explosion_images[0].get_height() / 2)
self.explode_animation = SimpleAnimation(
self.screen, pos, self.explosion_images,
100, 300)
global remainingCreeps
remainingCreeps-=1
if remainingCreeps == 0:
print("all dead")
def draw(self):
""" Blit the creep onto the screen that was provided in
the constructor.
"""
if self.state == Creep.ALIVE:
# The creep image is placed at self.pos. To allow for
# smooth movement even when the creep rotates and the
# image size changes, its placement is always
# centered.
#
self.draw_rect = self.image.get_rect().move(
self.pos.x - self.image_w / 2,
self.pos.y - self.image_h / 2)
self.screen.blit(self.image, self.draw_rect)
# The health bar is 15x4 px.
#
health_bar_x = self.pos.x - 7
health_bar_y = self.pos.y - self.image_h / 2 - 6
self.screen.fill( Color('red'),
(health_bar_x, health_bar_y, 15, 4))
self.screen.fill( Color('green'),
( health_bar_x, health_bar_y,
self.health, 4))
elif self.state == Creep.EXPLODING:
self.explode_animation.draw()
elif self.state == Creep.DEAD:
pass
def mouse_click_event(self, pos):
""" The mouse was clicked in pos.
"""
if self._point_is_inside(vec2d(pos)):
self._decrease_health(3)
#begin new player
class Player(pygame.sprite.Sprite):
change_x=0
change_y=0
frame = 0
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
# LOAD PLATER IMAGES
# Set height, width
self.images = []
for i in range(1,17):
img = pygame.image.load("images/player/" + str(i)+".png").convert() #player images
img.set_colorkey(white)
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
self.health = 15
self.image_w, self.image_h = self.image.get_size()
health_bar_x = self.rect.x - 7
health_bar_y = self.rect.y - self.image_h / 2 - 6
screen.fill( Color('red'),
(health_bar_x, health_bar_y, 15, 4))
screen.fill( Color('green'),
( health_bar_x, health_bar_y,
self.health, 4))
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def _decrease_health(self, n):
""" Decrease my health by n (or to 0, if it's currently
less than n)
"""
self.health = max(0, self.health - n)
if self.health == 0:
self._explode()
def update(self,walls):
# collision detection
old_x=self.rect.x
new_x=old_x+self.change_x
self.rect.x = new_x
# hit a wall?
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.rect.x=old_x
old_y=self.rect.y
new_y=old_y+self.change_y
self.rect.y = new_y
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.rect.y=old_y
# right to left
if self.change_x < 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
# Grab the image, divide by 4
# every 4 frames.
self.image = self.images[self.frame//4]
# Move left to right.
# images 4...7 instead of 0...3.
if self.change_x > 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4]
if self.change_y > 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4+4]
if self.change_y < 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4+4+4]
score = 0
# initialize pyGame
pygame.init()
# 800x600 sized screen
global screen
screen = pygame.display.set_mode([800, 600])
screen.fill(black)
#bg_tile_img = pygame.image.load('images/map/grass.png').convert_alpha()
#draw_background(screen, bg_tile_img)
#pygame.display.flip()
# Set title
pygame.display.set_caption('Test')
#background = pygame.Surface(screen.get_size())
#background = background.convert()
#background.fill(black)
# Create the player
player = Player( 50,50 )
player.rect.x=50
player.rect.y=50
movingsprites = pygame.sprite.RenderPlain()
movingsprites.add(player)
# Make the walls. (x_pos, y_pos, width, height)
global wall_list
wall_list=pygame.sprite.RenderPlain()
wall=Wall(0,0,10,600) # left wall
wall_list.add(wall)
wall=Wall(10,0,790,10) # top wall
wall_list.add(wall)
#wall=Wall(10,200,100,10) # poke wall
wall_list.add(wall)
wall=Wall(790,0,10,600) #(x,y,thickness, height)
wall_list.add(wall)
wall=Wall(10,590,790,10) #(x,y,thickness, height)
wall_list.add(wall)
f = open('MapMaker.txt')
num_lines = sum(1 for line in f)
print(num_lines)
lineCount = 0
with open("MapMaker.txt") as infile:
for line in infile:
f = open('MapMaker.txt')
print(line)
coords = line.split(',')
#print(coords[0])
#print(coords[1])
#print(coords[2])
#print(coords[3])
#print(coords[4])
if "tree" in line:
print("tree in")
wall=insertTree(int(coords[0]),int(coords[1]), int(coords[2]),int(coords[3]),coords[4])
wall_list.add(wall)
elif "rock" in line:
print("rock in")
wall=insertRock(int(coords[0]),int(coords[1]), int(coords[2]),int(coords[3]),coords[4] )
wall_list.add(wall)
width = 20
height = 540
height = height - 48
for i in range(0,23):
width = width + 32
name = insertTree(width,540,790,10,"tree")
#wall_list.add(name)
name = insertTree(width,height,690,10,"tree")
#wall_list.add(name)
CREEP_SPAWN_TIME = 200 # frames
creep_spawn = CREEP_SPAWN_TIME
clock = pygame.time.Clock()
bg_tile_img = pygame.image.load('images/map/grass.png').convert()
img_rect = bg_tile_img
FIELD_RECT = Rect(50, 50, 700, 500)
CREEP_FILENAMES = [
'images/player/1.png',
'images/player/1.png',
'images/player/1.png']
N_CREEPS = 3
creep_images = [
pygame.image.load(filename).convert_alpha()
for filename in CREEP_FILENAMES]
explosion_img = pygame.image.load('images/map/tree.png').convert_alpha()
explosion_images = [
explosion_img, pygame.transform.rotate(explosion_img, 90)]
creeps = pygame.sprite.RenderPlain()
done = False
#bg_tile_img = pygame.image.load('images/map/grass.png').convert()
#draw_background(screen, bg_tile_img)
totalCreeps = 0
remainingCreeps = 3
while done == False:
creep_images = pygame.image.load("images/player/1.png").convert()
creep_images.set_colorkey(white)
draw_background(screen, bg_tile_img)
if len(creeps) != N_CREEPS:
if totalCreeps < N_CREEPS:
totalCreeps = totalCreeps + 1
print(totalCreeps)
creeps.add(
Creep( screen=screen,
creep_image=creep_images,
explosion_images=explosion_images,
field=FIELD_RECT,
init_position=( randint(FIELD_RECT.left,
FIELD_RECT.right),
randint(FIELD_RECT.top,
FIELD_RECT.bottom)),
init_direction=(choice([-1, 1]),
choice([-1, 1])),
speed=0.01))
for creep in creeps:
creep.update(60,wall_list)
creep.draw()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done=True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.changespeed(-2,0)
creep.changespeed(-2,0)
if event.key == pygame.K_RIGHT:
player.changespeed(2,0)
creep.changespeed(2,0)
if event.key == pygame.K_UP:
player.changespeed(0,-2)
creep.changespeed(0,-2)
if event.key == pygame.K_DOWN:
player.changespeed(0,2)
creep.changespeed(0,2)
if event.key == pygame.K_ESCAPE:
pauseGame()
if event.key == pygame.K_1:
global currentEditTool
currentEditTool = "Tree"
changeTool()
if event.key == pygame.K_2:
global currentEditTool
currentEditTool = "Rock"
changeTool()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.changespeed(2,0)
creep.changespeed(2,0)
if event.key == pygame.K_RIGHT:
player.changespeed(-2,0)
creep.changespeed(-2,0)
if event.key == pygame.K_UP:
player.changespeed(0,2)
creep.changespeed(0,2)
if event.key == pygame.K_DOWN:
player.changespeed(0,-2)
creep.changespeed(0,-2)
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
for creep in creeps:
creep.mouse_click_event(pygame.mouse.get_pos())
if editMap == True:
x,y = pygame.mouse.get_pos()
if currentEditTool == "Tree":
name = insertTree(x-10,y-25, 10 , 10, "tree")
wall_list.add(name)
wall_list.draw(screen)
f = open('MapMaker.txt', "a+")
image = pygame.image.load("images/map/tree.png").convert()
screen.blit(image, (30,10))
pygame.display.flip()
f.write(str(x) + "," + str(y) + ",790,10, tree\n")
#f.write("wall=insertTree(" + str(x) + "," + str(y) + ",790,10)\nwall_list.add(wall)\n")
elif currentEditTool == "Rock":
name = insertRock(x-10,y-25, 10 , 10,"rock")
wall_list.add(name)
wall_list.draw(screen)
f = open('MapMaker.txt', "a+")
f.write(str(x) + "," + str(y) + ",790,10,rock\n")
#f.write("wall=insertRock(" + str(x) + "," + str(y) + ",790,10)\nwall_list.add(wall)\n")
else:
None
#pygame.display.flip()
player.update(wall_list)
movingsprites.draw(screen)
wall_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
Sprite collide is the correct way to do it, you are just handling the return data incorrectly. Basically, sprite collide will no return a boolean of if you are colliding or not with a member of the group. It returns a list of sprites. Basically, it will give you a list of sprites colliding with you in that group. Another thing to watch out for is that the list will also invariably include the sprite itself. After all, it is colliding with itself. Here is a functioning way to test if you are colliding with a wall.
def is_colliding_with_wall(): #this is a function that will determine if you are touching a wall. You have repeated code to test collisions with walls multiple times, so it is best just to have a function
collide = pygame.sprite.spritecollide(self, walls, False) #this gets the list, it is the exact same code as in yours
for collision in collide: #goes through the list, checking each collision
if collision != self: #filters out self collision
return True #if there is a collision that is not self, returns True, that there is a collision with a wall
return False #if it has checked all the collisions and there is only a self collision, then it is not colliding with a wall, so it returns False

Categories

Resources