Related
I am writing a simple invaders game. To add damage to the bases I figured I could blit a small, black surface on the base at bullet impact, and use a mask to check if the bullet was on the damage or the base, but it isn't working and I feel I am misunderstanding the mask. The first collision is detected but after that it also detects a collision but doesn't put any more damage on the base. I thought because the surface was black the base mask wouldn't include it, but it isn't working. Here is a short test to demo this. Press space (or any key) to fire a bullet at the base. I thought maybe I should generate a new mask for the base but that doesn't work. The mask collide is from the pygame sprite code on github.
import sys, pygame, random
from pygame.locals import *
screenwidth = 600
screenheight = 400
pygame.init()
screen = pygame.display.set_mode((screenwidth, screenheight))
pygame.display.set_caption("shoot 'em up")
screenrect = screen.get_rect()
black = (0, 0, 0)
blue = (10, 10, 255)
yellow = (238, 238, 0)
base_width = 80
base_height = 40
bullet_width = 3
bullet_height = 10
class Bullet(pygame.Surface):
def __init__(self, point):
super().__init__((bullet_width, bullet_height), pygame.SRCALPHA)
self.rect = self.get_rect()
self.rect.midbottom = point
self.fill(yellow)
self.velocity = -5
self.alive = True
self.mask = pygame.mask.from_surface(self)
def update(self):
self.rect.top += self.velocity
def draw(self, surf):
surf.blit(self, self.rect)
class Base(pygame.Surface):
def __init__(self, x, y, colour):
super().__init__((base_width, base_height), pygame.SRCALPHA)
self.rect = self.get_rect()
self.rect.x = x
self.rect.y = y
self.fill(colour)
self.alive = True
def add_damage(self, bullet):
width = random.randint(3, 6)
height = random.randint(8, 12)
damage = pygame.Surface((width, height), pygame.SRCALPHA)
damage.fill(black)
rect = damage.get_rect()
rect.x = bullet.rect.x - self.rect.x
rect.y = bullet.rect.top - self.rect.top
self.blit(damage, rect)
#self.mask = pygame.mask.from_surface(self)
def draw(self, surf):
surf.blit(self, self.rect)
class Test(pygame.Surface):
def __init__(self):
super().__init__((600, 400))
self. base = Base(50, 300, blue)
self.bullets = []
def run(self):
while 1:
self.get_events()
self.update()
self.draw()
def get_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
bullet = Bullet((60, 380))
self.bullets.append(bullet)
def update(self):
if self.bullets:
for bullet in self.bullets:
bullet.update()
self.collision_check(bullet)
for bullet in self.bullets:
if not bullet.alive:
self.bullets.remove(bullet)
def collision_check(self, bullet):
if bullet.rect.colliderect(self.base):
if self.collide_mask(bullet, self.base):
print("collide")
self.base.add_damage(bullet)
bullet.alive = False
def collide_mask(self, left, right):
xoffset = right.rect[0] - left.rect[0]
yoffset = right.rect[1] - left.rect[1]
try:
leftmask = left.mask
except AttributeError:
leftmask = pygame.mask.from_surface(left)
try:
rightmask = right.mask
except AttributeError:
rightmask = pygame.mask.from_surface(right)
return leftmask.overlap(rightmask, (xoffset, yoffset))
def draw(self):
self.fill(black)
self.base.draw(self)
for bullet in self.bullets:
bullet.draw(self)
screen.blit(self, (0,0))
pygame.display.flip()
if __name__=="__main__":
t = Test()
t.run()
As you can see this is not using pygame sprites.
if the pygame.Surface object is changed you need to recreate the mask with pygame.mask.from_surface. However, the mask is generated form the Surface's alpha channel. Therefore, you need to make the damaged area transparent. Create a completely transparent rectangle (RGBA = 0, 0, 0, 0) and blit the rectangle using the special flag BLEND_RGBA_MULT (or BLEND_RGBA_MIN). Finally recreate the mask:
damage = pygame.Surface((width, height), pygame.SRCALPHA)
self.blit(damage, rect, special_flags=pygame.BLEND_RGBA_MULT)
self.mask = pygame.mask.from_surface(self)
add_damage Mehtod:
class Base(pygame.Surface):
# [...]
def add_damage(self, bullet):
width = random.randint(3, 6)
height = random.randint(8, 12)
damage = pygame.Surface((width, height), pygame.SRCALPHA)
rect = damage.get_rect()
rect.x = bullet.rect.x - self.rect.x
rect.y = bullet.rect.top - self.rect.top
self.blit(damage, rect, special_flags=pygame.BLEND_RGBA_MULT)
self.mask = pygame.mask.from_surface(self)
I want to check when the worker object comes closely to any of the fence objects in order to print a notification message. By "closely" I mean the distance of 20 pixels between the worker and the border of fence. For example, something like "distances_list = worker.get_dist(fences)", where "distances_list" would contain the current distances to all fences.
I could adopt the approach proposed here, but maybe there is some built-in function for my task?
In pygame we can use different collision detection functions, but in the case described above there is no collision. Is there any built-in function to find the distance between the sprites?
import pygame, random
import sys
WHITE = (255, 255, 255)
GREEN = (20, 255, 140)
GREY = (210, 210 ,210)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)
SCREENWIDTH=1000
SCREENHEIGHT=578
IMG_BACKGROUND = "background.jpg"
IMG_WORKER_RUNNING = "images/workers/worker_1.png"
IMG_WORKER_IDLE = "images/workers/worker_2.png"
IMG_WORKER_ACCIDENT = "images/workers/accident.png"
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location, *groups):
# we set a _layer attribute before adding this sprite to the sprite groups
# we want the background to be actually in the back
self._layer = -1
pygame.sprite.Sprite.__init__(self, groups)
# let's resize the background image now and only once
self.image = pygame.transform.scale(pygame.image.load(image_file).convert(), (SCREENWIDTH, SCREENHEIGHT))
self.rect = self.image.get_rect(topleft=location)
class GeoFenceInfluenceZone(pygame.sprite.Sprite):
def __init__(self, rect, *groups):
# we set a _layer attribute before adding this sprite to the sprite groups
self._layer = 0
pygame.sprite.Sprite.__init__(self, groups)
self.image = pygame.surface.Surface((rect.width, rect.height))
self.image.fill(GREY)
self.rect = rect
class GeoFence(pygame.sprite.Sprite):
def __init__(self, rect, risk_level, *groups):
# we set a _layer attribute before adding this sprite to the sprite groups
self._layer = 1
pygame.sprite.Sprite.__init__(self, groups)
self.image = pygame.surface.Surface((rect.width, rect.height))
self.image.fill(GREEN)
self.rect = rect
self.risk_level = risk_level
self.font = pygame.font.SysFont('Arial', 20)
text = self.font.render(risk_level, 1, (255,0,0), GREEN)
text_rect = text.get_rect(center=(rect.width/2, rect.height/2))
self.image.blit(text, text_rect)
class Worker(pygame.sprite.Sprite):
# we introduce to possible states: RUNNING and IDLE
RUNNING = 0
IDLE = 1
ACCIDENT = 2
NUMBER_OF_ACCIDENTS = 0
def __init__(self, image_running, image_idle, image_accident, location, *groups):
self.font = pygame.font.SysFont('Arial', 10)
# each state has it's own image
self.images = {
Worker.RUNNING: pygame.transform.scale(get_image(image_running), (45, 45)),
Worker.IDLE: pygame.transform.scale(get_image(image_idle), (20, 45)),
Worker.ACCIDENT: pygame.transform.scale(get_image(image_accident), (40, 40))
}
# we set a _layer attribute before adding this sprite to the sprite groups
# we want the workers on top
self._layer = 2
pygame.sprite.Sprite.__init__(self, groups)
# let's keep track of the state and how long we are in this state already
self.state = Worker.IDLE
self.ticks_in_state = 0
self.image = self.images[self.state]
self.rect = self.image.get_rect(topleft=location)
self.direction = pygame.math.Vector2(0, 0)
self.speed = random.randint(1, 3)
self.set_random_direction()
def set_random_direction(self):
# random new direction or standing still
vec = pygame.math.Vector2(random.randint(-100,100), random.randint(-100,100)) if random.randint(0, 5) > 1 else pygame.math.Vector2(0, 0)
# check the new vector and decide if we are running or fooling around
length = vec.length()
speed = sum(abs(int(v)) for v in vec.normalize() * self.speed) if length > 0 else 0
if (length == 0 or speed == 0) and (self.state != Worker.ACCIDENT):
new_state = Worker.IDLE
self.direction = pygame.math.Vector2(0, 0)
elif self.state != Worker.ACCIDENT:
new_state = Worker.RUNNING
self.direction = vec.normalize()
else:
new_state = Worker.ACCIDENT
self.ticks_in_state = 0
self.state = new_state
# use the right image for the current state
self.image = self.images[self.state]
def update(self, screen):
self.ticks_in_state += 1
# the longer we are in a certain state, the more likely is we change direction
if random.randint(0, self.ticks_in_state) > 70:
self.set_random_direction()
# now let's multiply our direction with our speed and move the rect
vec = [int(v) for v in self.direction * self.speed]
self.rect.move_ip(*vec)
# if we're going outside the screen, change direction
if not screen.get_rect().contains(self.rect):
self.direction = self.direction * -1
# spritecollide returns a list of all sprites in the group that collide with
# the given sprite, but if the sprite is in this group itself, we have
# to ignore a collision with itself
if any(s for s in pygame.sprite.spritecollide(self, building_materials, False) if s != self):
self.direction = self.direction * -1
if any(s for s in pygame.sprite.spritecollide(self, machines, False) if s != self):
self.direction = self.direction * -1
# Risk handling
self.handle_risks()
self.rect.clamp_ip(screen.get_rect())
def handle_risks(self):
for s in pygame.sprite.spritecollide(self, fences, False):
if s != self:
self.speed = 0
self.state = Worker.ACCIDENT
self.image = self.images[self.state]
Worker.NUMBER_OF_ACCIDENTS += 1
class BuildingMaterials(pygame.sprite.Sprite):
def __init__(self, image_file, location, *groups):
# we set a _layer attribute before adding this sprite to the sprite groups
self._layer = 2
pygame.sprite.Sprite.__init__(self, groups)
self.image = pygame.transform.scale(pygame.image.load(image_file).convert_alpha(), (40, 40))
self.rect = self.image.get_rect(topleft=location)
class Excavator(pygame.sprite.Sprite):
def __init__(self, image_file, location, *groups):
# we set a _layer attribute before adding this sprite to the sprite groups
self._layer = 3
pygame.sprite.Sprite.__init__(self, groups)
self.image = pygame.transform.scale(pygame.image.load(image_file).convert_alpha(), (170, 170))
self.rect = self.image.get_rect(topleft=location)
image_cache = {}
def get_image(key):
if not key in image_cache:
image_cache[key] = pygame.image.load(key)
return image_cache[key]
pygame.init()
# currently, one group would be enough
# but if you want to use some collision handling in the future
# it's best to group all sprites into special groups (no pun intended)
all_sprites = pygame.sprite.LayeredUpdates()
workers = pygame.sprite.Group()
building_materials = pygame.sprite.Group()
fences = pygame.sprite.Group()
fences_infl_zones = pygame.sprite.Group()
screen = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
pygame.display.set_caption("TEST")
# create multiple workers
for pos in ((30,30), (50, 400), (200, 100), (700, 200)):
Worker(IMG_WORKER_RUNNING, IMG_WORKER_IDLE, IMG_WORKER_ACCIDENT, pos, all_sprites, workers, building_materials, machines, fences)
# create multiple building material stocks
for pos in ((50,460),(50,500),(100,500),(850,30),(800,30)):
BuildingMaterials("images/materials/building_blocks{}.png".format(random.randint(1,3)), pos, all_sprites, building_materials)
# create multiple geo-fences
risks = ["H","M","L"]
for rect in (pygame.Rect(510,150,75,52), pygame.Rect(450,250,68,40), pygame.Rect(450,370,68,48),
pygame.Rect(0,0,20,SCREENHEIGHT),pygame.Rect(0,0,SCREENWIDTH,20),
pygame.Rect(SCREENWIDTH-20,0,20,SCREENHEIGHT),pygame.Rect(0,SCREENHEIGHT-20,SCREENWIDTH,20)):
risk = risks[random.randint(0,2)]
GeoFence(rect, risk, all_sprites, fences)
# create influence zones for all geo-fences
for rect in (pygame.Rect(495,135,105,80), pygame.Rect(435,235,98,68), pygame.Rect(435,355,98,76)):
GeoFenceInfluenceZone(rect, all_sprites, fences_infl_zones)
# and the background
Background(IMG_BACKGROUND, [0,0], all_sprites)
carryOn = True
clock = pygame.time.Clock()
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn = False
pygame.display.quit()
pygame.quit()
quit()
all_sprites.update(screen)
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(20)
Here's this solution ported to pygame. The y-axis in pygame is flipped, so I had to swap the top and bottom variables and the distance can be calculated with math.hypot. You need to pass the rects of the two sprites to rect_distance where they will be unpacked into the x1, y1 (top left) and x1b, y1b (bottom right) variables. (You can see the distance in the window title.)
import math
import pygame as pg
class Player(pg.sprite.Sprite):
def __init__(self, pos, *groups):
super().__init__(*groups)
self.image = pg.Surface((30, 50))
self.image.fill(pg.Color('dodgerblue1'))
self.rect = self.image.get_rect(topleft=pos)
def rect_distance(rect1, rect2):
x1, y1 = rect1.topleft
x1b, y1b = rect1.bottomright
x2, y2 = rect2.topleft
x2b, y2b = rect2.bottomright
left = x2b < x1
right = x1b < x2
top = y2b < y1
bottom = y1b < y2
if bottom and left:
print('bottom left')
return math.hypot(x2b-x1, y2-y1b)
elif left and top:
print('top left')
return math.hypot(x2b-x1, y2b-y1)
elif top and right:
print('top right')
return math.hypot(x2-x1b, y2b-y1)
elif right and bottom:
print('bottom right')
return math.hypot(x2-x1b, y2-y1b)
elif left:
print('left')
return x1 - x2b
elif right:
print('right')
return x2 - x1b
elif top:
print('top')
return y1 - y2b
elif bottom:
print('bottom')
return y2 - y1b
else: # rectangles intersect
print('intersection')
return 0.
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
player = Player((50, 80), all_sprites)
player2 = Player((100, 200), all_sprites)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEMOTION:
player.rect.topleft = event.pos
distance = rect_distance(player.rect, player2.rect)
pg.display.set_caption(str(distance))
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
So recently I got into OOP, which is a very new topic for me, and I had already made a pong game that did not employ objects and I'm thinking of doing a new script with objects utilised. The problem is, when I run the code, it displays an empty screen and I'm not sure what I did wrong (I'm still new to OOP). Can anyone help out?
import pygame
class Ball():
def __init__(self, x, y, xmove, ymove, color, size):
self.x = 0
self.y = 0
self.xmove = 0
self.ymove = 0
self.color = (255, 255, 255)
self.size = 10
def draw(self, screen):
pygame.draw.circle(screen, self.color, [self.x, self.y], self.size)
def ballmove(self):
self.x += self.xmove
self.y += self.ymove
done = False
pygame.init()
WIDTH = 640
HEIGHT = 480
clock = pygame.time.Clock()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
ball = Ball(0, 0, 1.5, 1.5, [255, 255, 255], 10)
while done != False:
screen.fill(0)
ball.ballmove()
ball.draw(screen)
pygame.display.update()
I think you used a wrong condtion in your loop. It should mean while done == False: or while done != True:
Also your constructor is wrong. The parameter you give to the ball will never set, because you initialized all paramters with default values. Use this constructor instead
def __init__(self, x, y, xmove, ymove, color, size):
self.x = x
self.y = y
self.xmove = xmove
self.ymove = ymove
self.color = color
self.size = size
When i try to run the game the code tries to run a method for the wrong sprite. I think the line "player.handle_keys()" is the problem as when i run it, it says that it can't find a "handle_keys()" method for the "meteor" class. I haven't got a line to run a "meteor.handle_keys()" as this class should not have this method.
Here is the code:
import pygame
import random
# Define some colors
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
bg = pygame.image.load("bg1.png")
class space_ship(pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
# Create an image of the space_ship1, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
#draw image
self.image = pygame.image.load("player1.gif").convert()
# Draw the ellipse
#pygame.draw.ellipse(self.image, color, [0, 0, width, height])
# x and y coordinates
self.x = 500
self.y = 450
def handle_keys(self):
""" Handles Keys """
key = pygame.key.get_pressed()
dist = 5 # distance moved in 1 frame
if key[pygame.K_RIGHT]: # right key
self.x += dist # move right
elif key[pygame.K_LEFT]: # left key
self.x -= dist # move left
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, (self.x, self.y))
class asteroid(pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
# Create an image of the space_ship1, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
# Draw the ellipse
#pygame.draw.ellipse(self.image, color, [0, 0, width, height])
self.image = pygame.image.load("ast1.gif").convert()
# x and y coordinates
self.x = random.randint(50,950)
self.y = 10
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, (self.x, self.y))
def fall(self):
dist = 5
self.y +=dist
if self.y > 600:
self.x = random.randint(50,950)
self.y = random.randint(-2000, -10)
def respawn(self):
self.y = -10
# Initialize Pygame
pygame.init()
# Set the height and width of the screen
screen_width = 1000
screen_height = 600
screen = pygame.display.set_mode([screen_width, screen_height])
# This is a list of 'sprites.' Each sprite in the program is
# added to this list.
# The list is managed by a class called 'Group.'
asteroid_list = pygame.sprite.Group()
# This is a list of every sprite.
# All asteroids and the player as well.
all_sprites_list = pygame.sprite.Group()
player = space_ship(RED, 20, 15)
all_sprites_list.add(player)
asteroid_1 = asteroid(BLACK, 40, 40)
asteroid_list.add(asteroid_1)
all_sprites_list.add(asteroid_1)
asteroid_2 = asteroid(BLACK, 40, 40)
asteroid_list.add(asteroid_2)
all_sprites_list.add(asteroid_2)
asteroid_3 = asteroid(BLACK,40, 40)
asteroid_list.add(asteroid_3)
all_sprites_list.add(asteroid_3)
asteroid_4 = asteroid(BLACK,40, 40)
asteroid_list.add(asteroid_4)
all_sprites_list.add(asteroid_4)
asteroid_5 = asteroid(BLACK,40, 40)
asteroid_list.add(asteroid_5)
all_sprites_list.add(asteroid_5)
asteroid_6 = asteroid(BLACK,40, 40)
asteroid_list.add(asteroid_6)
all_sprites_list.add(asteroid_6)
asteroid_7 = asteroid(BLACK,40, 40)
asteroid_list.add(asteroid_7)
all_sprites_list.add(asteroid_7)
asteroid_8 = asteroid(BLACK,40, 40)
asteroid_list.add(asteroid_8)
all_sprites_list.add(asteroid_list)
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
score = 0
# ----------------- Main Program Loop --------------------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Call upon function
player.handle_keys()
# Clear the screen
screen.fill(WHITE)
#INSIDE OF THE GAME LOOP
screen.blit(bg, (0, 0))
# See if the player space_ship1 has collided with anything.
blocks_hit_list = pygame.sprite.spritecollide(player, asteroid_list, True)
# Check the list of collisions.
for player in blocks_hit_list:
score +=1
print(score)
# Draw all the spites
player.draw(screen)
asteroid_1.draw(screen)
asteroid_1.fall()
asteroid_2.draw(screen)
asteroid_2.fall()
asteroid_3.draw(screen)
asteroid_3.fall()
asteroid_4.draw(screen)
asteroid_4.fall()
asteroid_5.draw(screen)
asteroid_5.fall()
asteroid_6.draw(screen)
asteroid_6.fall()
asteroid_7.draw(screen)
asteroid_7.fall()
asteroid_8.draw(screen)
asteroid_8.fall()
#all_sprites_list.draw(screen)
# Limit to 60 frames per second
clock.tick(60)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
pygame.quit()
You are overriding player in your for loop
# Check the list of collisions.
for player in blocks_hit_list:
score +=1
print(score)
change it to something else and all will be good
# Check the list of collisions.
for something_else in blocks_hit_list:
score +=1
print(score)
Enjoy
I am making a small game in python where balls are fired by a catapult, and then the balls destroy a structure of boxes and a snake. At the moment I have made the boxes and the snakes however, I am unable to make the balls come from the left direction to destroy the boxes. I want to make a catapult on the left side which can hold a ball and fire it. How can I go about making this?
Here is my code
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
from pymunk import Vec2d
import sys
## ~~~~~~~~~~~~~~~~~~~~~~~~~
class Ball(pygame.sprite.Sprite):
def __init__(self,screen):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('ball1.jpg')
self.original = pygame.image.load('ball1.jpg')
self.rect = self.image.get_rect()
self.area = screen.get_rect()
def add_ball(self, space, offset):#offset
self.mass = 3
self.radius = 15
self.inertia = pm.moment_for_circle(self.mass, 0, self.radius, (0,0))
self.body = pm.Body(self.mass, self.inertia)
self.body.position = offset+200, 550
self.rect.center = to_pygame(self.body.position)
self.shape = pm.Circle(self.body, self.radius, (0,0))
space.add(self.body, self.shape)
def draw(self,screen):
pygame.draw.circle(screen, (255,255,255), self.rect.center, int(15), 3)
def update(self):
print("{0} {1}".format(self.rect.center, self.body.position))
self.rect.center = to_pygame(self.body.position)
## ~~~~~~~~~~~~~~~~~~~~~~~~~
class Box(pygame.sprite.Sprite):
def __init__(self,screen):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('box1.jpg')
self.original = pygame.image.load('box1.jpg')
#self.image = pygame.image.load('snake.jpg')
self.rect = self.image.get_rect()
self.area = screen.get_rect()
def add_box(self, space, posX, posY):
global screen
self.size= 30
self.points = [(-self.size, -self.size), (-self.size, self.size), (self.size,self.size), (self.size, -self.size)]
self.mass = 0.3
self.moment = pm.moment_for_poly(self.mass, self.points, (0,0))
self.body = pm.Body(self.mass, self.moment)
self.body.position = Vec2d(posX, posY)
#print("Box a {0}".format(self.body.position))
self.rect.center = to_pygame(self.body.position)
#print("Box b {0}".format(self.rect))
self.shape = pm.Poly(self.body, self.points, (0,0))
self.shape.friction = 1
#self.shape.group = 1
space.add(self.body, self.shape)
def draw(self,screen):
pygame.draw.rect(screen, (255,255,255), self.rect, 2)
def update(self):
self.rect.center = to_pygame(self.body.position) #pysics simulations is movin the body
## ~~~~~~~~~~~~~~~~~~~~~~~~~
class Snake(pygame.sprite.Sprite):
def __init__(self,screen):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('snake.jpg')
self.original = pygame.image.load('snake.jpg')
self.rect = self.image.get_rect()
self.area = screen.get_rect()
def add_snake(self, space, posX, posY):
global screen
self.size= 30
self.points = [(-self.size, -self.size), (-self.size, self.size), (self.size,self.size), (self.size, -self.size)]
self.mass = 0.1
self.moment = pm.moment_for_poly(self.mass, self.points, (0,0))
self.body = pm.Body(self.mass, self.moment)
self.body.position = Vec2d(posX, posY)
#print("Box a {0}".format(self.body.position))
self.rect.center = to_pygame(self.body.position)
#print("Box b {0}".format(self.rect))
self.shape = pm.Poly(self.body, self.points, (0,0))
self.shape.friction = 1
#self.shape.group = 1
space.add(self.body, self.shape)
def draw(self,screen):
pygame.draw.rect(screen, (5,5,255), self.rect, 2)
def update(self):
self.rect.center = to_pygame(self.body.position) #pysics simulations is movin the body
## ~~~~~~~~~~~~~~~~~~~~~~~~~
def to_pygame(p):
"""Small hack to convert pymunk to pygame coordinates"""
return int(p[0]), int(-p[1]+600)
def main():
pygame.init()
screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption("Piling boxes")
clock = pygame.time.Clock()
space = pm.Space()
space.gravity = (0.0, -900.0)
space.damping = 0.5
### ground
body = pm.Body()
shape = pm.Segment(body, (0,100), (600,100), .0)
shape.friction = 1.0
space.add(shape)
allsprites = pygame.sprite.Group()
offsetY = 62
offsetX = 92
posX = 180
posY = 130
for j in range(3):
for i in range(5):
box = Box(screen) #Add more boxes
box.add_box(space, posX, posY)
allsprites.add(box)
posY = posY + offsetY
posY = 130
posX = posX + offsetX
snake = Snake(screen)
snake.add_snake(space, posX, posY)
allsprites.add(snake)
selected = None
##
## posY = 130
## offset = 92
## for i in range(5):
## #offset = offset - 248
## box = Box(screen) #Add more boxes
## #box.add_box(space)
## box.add_box(space, posX, posY)
## allsprites.add(box)
## posX = posX + offset
##
## #offset = offset + 62
posY =0
offset = 0
for i in range(5):
ball = Ball(screen)
ball.add_ball(space, offset)
offset = offset + 16
allsprites.add(ball)
#background = pygame.Surface(screen.get_size())
#background = background.convert()
#background.fill((0,0,0))
#screen.blit(background, (0,0))
test_image = pygame.image.load("AngryCloneBg.jpg")
#screen.blit(test_image, (0,0)) #coordinates (0,500))
#pygame.display.flip()
while 1:
clock.tick(60)
space.step(1/90.0)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit(0)
allsprites.update()
screen.blit(test_image, (0,0))
#pygame.draw.line(screen, THECOLORS["red"], to_pygame((150,100)), to_pygame((450,100)), 3)
allsprites.draw(screen)
for item in allsprites:
item.draw(screen)
pygame.display.flip()
## ~~~~~~~~~~~~~~~~~~~~~~~~~
if __name__ == '__main__':
main()
How can I make the balls to be used with a catapult to fire the balls?
After 2 minutes of looking over the pymunk docs: http://pymunk.googlecode.com/svn/tags/pymunk-2.0.0/docs/api/index.html
To create a cannon like game. Create a ball object, and call apply_force. It's as simple as that.
If you wish to create a catapult, a bit more physics are involved. You need to create line(Segment) with a fixed joint to the ground. Then connect the ball to the other end of the segment. Apply some force to the segment, and when the ball will be high enough, release the joint.
EDIT:
I also found a very helpful tutorial with joints:
http://code.google.com/p/pymunk/wiki/SlideAndPinJointsExample