Hey guys am new to pygame .I have to make a ball image ball.jpg to bounce in polygonal way.I have also an another ball image which is running in a square way.What i need is to add an another ball image and bounce it in a polygonal way. My code is
import pygame
from itertools import cycle
pygame.init()
screen = pygame.display.set_mode((300, 300))
s_r = screen.get_rect()
ball = pygame.image.load('ball.jpg')
player = pygame.Rect((100, 100, 50, 50))
timer = pygame.time.Clock()
speed = 5
up, down, left, right = (0, -speed), (0, speed), (-speed, 0), (speed, 0)
dirs = cycle([up, right, down, left])
dir = next(dirs)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise
# move player
player.move_ip(dir)
# if it's outside the screen
if not s_r.contains(player):
# put it back inside
player.clamp_ip(s_r)
# and switch to next direction
dir = next(dirs)
screen.fill(pygame.color.Color('Black'))
screen.blit(ball,player)
pygame.display.flip()
timer.tick(25)
This code works perfectly with one image ..What i need is to add an another image and it must run in polygonal shape on pygame window..
Hope you guys can help me out ..Thanks
You'll need some vector math to do this.
Create a list of points that describe the path the object should move, then calculate a vector describing the direction from the current position to the target position. Then move the object by using that vector. Once you hit your target, aim for the next point to move.
Here's an example:
import pygame
import math
from itertools import cycle
# some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def dot(u, v):
return sum(u[i]*v[i] for i in range(len(u)))
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
class Ball(object):
def __init__(self, path):
self.x, self.y = (0, 0)
self.speed = 2.5
self.color = (200, 200, 200)
self.path = cycle(path)
self.set_target(next(self.path))
#property
def pos(self):
return self.x, self.y
# for drawing, we need the position as tuple of ints
# so lets create a helper property
#property
def int_pos(self):
return map(int, self.pos)
#property
def target(self):
return self.t_x, self.t_y
#property
def int_target(self):
return map(int, self.target)
def next_target(self):
self.set_target(self.pos)
self.set_target(next(self.path))
def set_target(self, pos):
self.t_x, self.t_y = pos
def update(self):
# if we won't move, don't calculate new vectors
if self.int_pos == self.int_target:
return self.next_target()
target_vector = sub(self.target, self.pos)
# a threshold to stop moving if the distance is to small.
# it prevents a 'flickering' between two points
if magnitude(target_vector) < 2:
return self.next_target()
# apply the balls's speed to the vector
move_vector = [c * self.speed for c in normalize(target_vector)]
# update position
self.x, self.y = add(self.pos, move_vector)
def draw(self):
pygame.draw.circle(screen, self.color, self.int_pos, 4)
pygame.init()
quit = False
path = [(26, 43),
(105, 110),
(45, 225),
(145, 295),
(266, 211),
(178, 134),
(250, 56),
(147, 12)]
path2 = [(26, 43),
(105, 10),
(45, 125),
(150, 134),
(150, 26),
(107, 12)]
ball = Ball(path)
ball.speed = 1.9
ball2 = Ball(path2)
ball2.color = (200, 200, 0)
balls = [ball, ball2]
while not quit:
quit = pygame.event.get(pygame.QUIT)
pygame.event.poll()
map(Ball.update, balls)
screen.fill((0, 0, 0))
map(Ball.draw, balls)
pygame.display.flip()
clock.tick(60)
Here's an example without a custom class:
import pygame
import math
from itertools import cycle
# some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
pygame.init()
quit = False
path = [(26, 43),
(105, 110),
(45, 225),
(145, 295),
(266, 211),
(178, 134),
(250, 56),
(147, 12)]
path = cycle(path)
target = next(path)
ball = pygame.rect.Rect(target[0], target[1], 10, 10)
speed = 3.6
while not quit:
quit = pygame.event.get(pygame.QUIT)
pygame.event.poll()
if ball.topleft == target:
target = next(path)
target_vector = sub(target, ball.topleft)
if magnitude(target_vector) < 2:
target = next(path)
else:
move_vector = [c * speed for c in normalize(target_vector)]
ball.move_ip(move_vector)
screen.fill((0, 0, 0))
pygame.draw.rect(screen, pygame.color.Color('Grey'), ball)
pygame.display.flip()
clock.tick(60)
Related
I am trying to learn PyMunk and I used their basic example from the website:
import pymunk
space = pymunk.Space()
space.gravity = 0,-1000
body = pymunk.Body(1,1666)
body.position = 50,100
poly = pymunk.Poly.create_box(body)
space.add(body, poly)
while True:
space.step(0.02)
But it does not create a window, does not show anything. How to use PyGame to create the graphical window?
What that example does is create a simulation, add a box shaped object inside and then run the simulation infinitely. The code doesn't print or draw anything, so you will not actually see the output. To get a better understanding and something on screen I suggest you start with the tutorial: http://www.pymunk.org/en/latest/tutorials/SlideAndPinJoint.html
Pymunk is a 2d rigid body physics library, which means that what it does is simulate how objects move and interact with each other in 2 dimensions. Its not made for drawing to the screen or read input.
You can of course use it as is without anything else, and just print out the result of the simulation. But more common is that you want to draw to the screen, read input and so on. One way to do that is by using the game library Pygame that helps out with drawing to the screen, reading input, having a game loop and so on.
Pymunk itself does have some helper functions so that you can easily connect it with Pygame (and a couple of other libraries), but this is not the core part. Usually these helper functions are good for when you want something quick-n-dirty such as a prototype and you don't have need to customize the drawing.
Now, this said, if you want to see something you can add a print statement to the while loop, so it becomes like this:
while True:
space.step(0.02)
print(body.position)
Then it will print out the position of the ball each step of the simulation, and you can see that its changing all the time (because of the gravity that is set on the space).
There are more advanced examples included in Pymunk that are both interactive and show something on screen. These examples depends on mostly either Pygame or Pyglet, but the principle is the same in case you have a different library you want to use it with.
Here's an example that shows how I use Pymunk in combination with pygame. The Entity class is a pygame.sprite.Sprite subclass to which I attach a pymunk.Body and a pymunk.Shape as well as a reference to the pm.Space, so that the bodies and shapes can be added and removed from it. The position of the sprite's rect gets set to the self.body.position each frame, so that we get the correct blit position for the self.image and can simply draw all sprites by calling self.sprite_group.draw(self.screen).
import math
import pygame as pg
import pymunk as pm
from pymunk import Vec2d
def flipy(p):
"""Convert chipmunk coordinates to pygame coordinates."""
return Vec2d(p[0], -p[1]+600)
class Entity(pg.sprite.Sprite):
def __init__(self, pos, space):
super().__init__()
self.image = pg.Surface((46, 52), pg.SRCALPHA)
pg.draw.polygon(self.image, (0, 50, 200),
[(0, 0), (48, 0), (48, 54), (24, 54)])
self.orig_image = self.image
self.rect = self.image.get_rect(topleft=pos)
vs = [(-23, 26), (23, 26), (23, -26), (0, -26)]
mass = 1
moment = pm.moment_for_poly(mass, vs)
self.body = pm.Body(mass, moment)
self.shape = pm.Poly(self.body, vs)
self.shape.friction = .9
self.body.position = pos
self.space = space
self.space.add(self.body, self.shape)
def update(self, dt):
pos = flipy(self.body.position)
self.rect.center = pos
self.image = pg.transform.rotate(
self.orig_image, math.degrees(self.body.angle))
self.rect = self.image.get_rect(center=self.rect.center)
# Remove sprites that have left the screen.
if pos.x < 20 or pos.y > 560:
self.space.remove(self.body, self.shape)
self.kill()
def handle_event(self, event):
if event.type == pg.KEYDOWN:
if event.key == pg.K_a:
self.body.angular_velocity = 5.5
elif event.key == pg.K_w:
self.body.apply_impulse_at_local_point(Vec2d(0, 900))
class Game:
def __init__(self):
self.done = False
self.clock = pg.time.Clock()
self.screen = pg.display.set_mode((800, 600))
self.gray = pg.Color('gray68')
self.red = pg.Color('red')
# Pymunk stuff.
self.space = pm.Space()
self.space.gravity = Vec2d(0.0, -900.0)
self.static_lines = [
pm.Segment(self.space.static_body, (60, 100), (370, 100), 0),
pm.Segment(self.space.static_body, (370, 100), (600, 300), 0),
]
for lin in self.static_lines:
lin.friction = 0.8
self.space.add(self.static_lines)
# A sprite group which holds the pygame.sprite.Sprite objects.
self.sprite_group = pg.sprite.Group(Entity((150, 200), self.space))
def run(self):
while not self.done:
self.dt = self.clock.tick(30) / 1000
self.handle_events()
self.run_logic()
self.draw()
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
if event.type == pg.MOUSEBUTTONDOWN:
self.sprite_group.add(Entity(flipy(event.pos), self.space))
for sprite in self.sprite_group:
sprite.handle_event(event)
def run_logic(self):
self.space.step(1/60) # Update physics.
self.sprite_group.update(self.dt) # Update pygame sprites.
def draw(self):
self.screen.fill(pg.Color(140, 120, 110))
for line in self.static_lines:
body = line.body
p1 = flipy(body.position + line.a.rotated(body.angle))
p2 = flipy(body.position + line.b.rotated(body.angle))
pg.draw.line(self.screen, self.gray, p1, p2, 5)
self.sprite_group.draw(self.screen)
# Debug draw. Outlines of the Pymunk shapes.
for obj in self.sprite_group:
shape = obj.shape
ps = [pos.rotated(shape.body.angle) + shape.body.position
for pos in shape.get_vertices()]
ps = [flipy((pos)) for pos in ps]
ps += [ps[0]]
pg.draw.lines(self.screen, self.red, False, ps, 1)
pg.display.flip()
if __name__ == '__main__':
pg.init()
Game().run()
pg.quit()
I was wondering how would I get an image to appear out of a list and then make it disappear once it is clicked on. And once it is clicked on a variable will be assigned.
When I run this in pygame I get a bunch all of these pictures printed and they go by really fast.
def game():
screen = pygame.display.set_mode((1400, 750))
pygame.display.set_caption("Goofspiel")
screenExit = False
while not screenExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
screenExit = True
keys = pygame.key.get_pressed()
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
screen.fill(lightgray)
fontname = pygame.font.SysFont("Denmark", 150)
font = pygame.font.SysFont("Denmark", 40)
font1 = pygame.font.SysFont("Denmark", 75)
name = fontname.render("Goofspeil", True, (black))
score = font.render("Player 1", True, (blue))
score1 = font.render("Player 2", True, (red))
player = font1.render("Player 1", True, (black))
que = font.render("Who's turn is it?", True, (black))
screen.blit(name, (490, 0))
#Score board
pygame.draw.rect(screen, sun, [0,20,375,80])
pygame.draw.rect(screen, black, [0,20,375,5])
pygame.draw.rect(screen, black, [0, 100.5, 375, 5])
pygame.draw.rect(screen, black, [375, 20, 5, 85])
pygame.draw.rect(screen, black, [305, 20, 5, 85])
pygame.draw.rect(screen, black, [180, 20, 5, 85])
pygame.draw.rect(screen, black, [110, 20, 5, 85])
screen.blit(score, (0, 50))
screen.blit(score1, (190, 50))
screen.blit(que, (1100, 20))
screen.blit(player, (1120, 70))
#Displaying the cards
screen.blit(DK, (5, 450))
screen.blit(DQ, (100, 450))
screen.blit(DJ, (200, 450))
screen.blit(D10, (300, 450))
screen.blit(D9, (400, 450))
screen.blit(D8, (500, 450))
screen.blit(D7, (600, 450))
screen.blit(D6, (700, 450))
screen.blit(D5, (800, 450))
screen.blit(D4, (900, 450))
screen.blit(D3, (1000, 450))
screen.blit(D2, (1100, 450))
screen.blit(D1, (1200, 450))
#Add a random picture
list = []
list.append(HK)
list.append(HQ)
list.append(HJ)
list.append(H10)
list.append(H9)
list.append(H8)
list.append(H7)
list.append(H6)
list.append(H5)
list.append(H4)
list.append(H3)
list.append(H2)
list.append(H1)
#rand = random.randrange(0, len(list))
random.shuffle(list)
screen.blit(list[0], (700,150))
if 200 > mouse[0] > 100 and 700 > mouse[1] > 450:
pygame.draw.rect(screen, lightblue2, [80, 705, 200, -60])
if click[0] == 1:
x = 12
score2 = font.render(str(x), True, (black))
screen.blit(score2, (135, 50))
pygame.display.update()
game()
What I want to do, is that I want a picture randomly out of this list to show up, then get "removed" from the list and then make the picture disappear from the screen. Because these pictures are cards, I want that if a person clicks on the king, the x value = 13 and so on.
Can someone please help, I'm stuck on this question. I'm trying to make the game Goofspiel and I can't!
Here's a point-form of how I would proceed:
Convert your H1, H2 ... images into PyGame Sprites.
This melds an image with a rectangle. It allows you to easily re-position and draw all the cards. The code an also use the mouse-click event to easily determine which of the cards (if any) was clicked.
Add the necessary card details into the Sprite Class too
This allows your Card to "know" its suit, number, and whether it's face-up.
Put your Card Sprites into a Pygame SpriteGroup
This facilitates easy drawing and mouse-click collision detection.
Once you know which card was clicked, it's pretty easy to remove it from the screen, turn it over, whatever because its state (location, suit, number, face-up, etc.) is all kept together in the sprite class. It no longer becomes a question of removing "#8 from the list".
Something like this:
class Card( pygame.sprite.Sprite ):
def __init__( self, front_image, back_image, suit, number, facing_up=True ):
pygame.sprite.Sprite.__init__(self)
self.front = pygame.image.load( front_image ).convert()
self.back = pygame.image.load( back_image ).convert()
self.rect = self.front.get_rect()
self.suit = suit
self.number = number
self.face_up = not facing_up
self.flip() # re-draw
def flip( self ):
self.face_up = not self.face_up
if ( self.face_up ):
self.image = self.front
else:
self.image = self.back
def moveTo( self, x, y ):
self.rect.x = x
self.rect.y = y
def isFaceUp( self ):
return self.face_up
# ... etc.
# make the cards
HK = Card( 'hearts_king.png', 'card_back.png', 'hearts', 13 )
HQ = Card( 'hearts_queen.png', 'card_back.png', 'hearts', 12 )
HJ = Card( 'hearts_jack.png', 'card_back.png', 'hearts', 11 )
H10 = Card( 'hearts_10.png', 'card_back.png', 'hearts', 10 )
# ... etc
all_hearts = [ HK, HQ, HJ, H10, H9, ... H1 ]
# TODO: call Card.moveTo() to position each card
cards_on_table = pygame.sprite.Group()
for card in all_hearts:
cards_on_table.add( card )
# Main Loop:
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.MOUSEBUTTONUP ):
# On mouse-click
mouse_pos = pygame.mouse.get_pos()
# Did we click on a card?
for card in cards_on_table:
if ( card.rect.collidepoint( mouse_pos ) ):
print( "Card [%s, %d] was clicked" % ( card.suit, card.number ) )
cards_on_table.remove( card ) # remove card from group
break
# Re-draw the window
window.fill( DARK_GREEN )
cards_on_table.draw( window )
pygame.display.flip()
# ... etc
I am trying to show that a tile on a chessboard is selected by outlining it whit a green colour.
I have managed to draw the outline when that specific tile area has been clicked, but the outline disappears after a few milliseconds.
So, I thought had something to do with updating the display (a function that I am already calling inside the highlight_tile() method).
here is the main loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# mouse handling
for unit in white_army:
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_position = pygame.mouse.get_pos()
if unit.tile_area.collidepoint(mouse_position):
highlight_tile(unit.tile_area)
else:
pygame.display.update()
and here the complete code
import pygame
import sys
from coordinator import coordinator
# set up the display
pygame.init()
window_size = (800, 800)
game_window = pygame.display.set_mode(size=window_size)
pygame.display.set_caption('My Game')
# defines classes and related methods
class WhiteSquare:
def __init__(self):
self.height = int(window_size[0] / 8)
self.width = int(window_size[1] / 8)
self.white_square = pygame.Surface((self.height, self.width))
self.white_square.fill((255, 255, 255))
class BlackSquare:
def __init__(self):
self.height = int(window_size[0] / 8)
self.width = int(window_size[1] / 8)
self.black_square = pygame.Surface((self.height, self.width))
self.black_square.fill((0, 0, 0))
class ChessBoard:
def __init__(self):
self.ws = ws
self.bs = bs
self.white_columns = white_columns
self.black_columns = black_columns
def draw(self):
for w_columns in self.white_columns:
game_window.blit(self.ws.white_square, w_columns)
for b_columns in self.black_columns:
game_window.blit(self.bs.black_square, b_columns)
# declare letters and numbers
letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
numbers = ['1', '2', '3', '4', '5', '6', '7', '8']
# create coordinates
coordinates = []
for item_letter in letters:
letter = item_letter
for item_number in numbers:
number = item_number
coordinates.append(letter + number)
# create coordinates values components
x_values = []
for number in range(0, 800, 100):
x = number
x_values.append(x)
y_values = []
for number in range(0, 800, 100):
y = number
y_values.append(y)
# create coordinate values
coordinate_values = []
for x in x_values:
for y in y_values:
coordinate_values.append((x, y))
# assign values to coordinates
squares_coordinates = dict(zip(coordinates, coordinate_values))
# Background for units
class CircleSurface:
def __init__(self):
self.circle_surface = pygame.Surface((100, 100), flags=pygame.SRCALPHA)
pygame.draw.circle(self.circle_surface, (255, 0, 0), (50, 50), 45)
# define colours
black = (0, 0, 0)
white = (255, 255, 255)
gold = (153, 153, 0)
green = (0, 255, 0)
dark_green = (0, 200, 0)
class Unit:
def __init__(self, colour, position):
# define Unit colour
self.colour = colour
# define Unit position
self.position = position
class Knight(Unit):
def __init__(self, colour, position):
# draw circle, inline, and outline
super().__init__(colour, position)
self.center_x = position[0]
self.center_y = position[1]
self.colour = colour
self.position = position
# define tile position
self.tile_area = pygame.Rect(position[0] - 50, position[1] - 50, 100, 100)
circle_radius = 40
self.circle = pygame.draw.circle(game_window, colour, self.position, circle_radius)
self.circle_outline = pygame.draw.circle(game_window, gold, self.position, circle_radius, 5)
self.circle_inline = pygame.draw.circle(game_window, gold, self.position, (circle_radius - 7), 2)
# draw letter
pygame.font.init()
my_font_size = 50
my_font = pygame.font.SysFont('Time New Roman', my_font_size)
text_surface = my_font.render('K', 1, gold)
center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
game_window.blit(text_surface, center_text)
class Archer(Unit):
def __init__(self, colour, first_point, second_point, third_point):
self.colour = colour
self.first_point = first_point
self.second_point = second_point
self.third_point = third_point
self.position = [self.first_point, self.second_point, self.third_point]
super().__init__(colour, self.position)
self.center_x = self.second_point[0]
self.center_y = (self.second_point[1] + ((self.first_point[1] - self.second_point[1]) / 2)) + 10
self.inline_position = [(self.first_point[0] + 10, self.first_point[1] - 5),
(self.second_point[0], self.second_point[1] + 10),
(self.third_point[0] - 10, self.third_point[1] - 5)]
# define_tile_centre
self.tile_area = pygame.Rect(self.first_point[0] - 10, self.second_point[1] - 10, 100, 100)
if self.colour == black:
self.first_point = self.first_point[0], self.first_point[1] - 80
self.second_point = self.second_point[0], self.second_point[1] + 80
self.third_point = self.third_point[0], self.third_point[1] - 80
self.position = [self.first_point, self.second_point, self.third_point]
self.center_y = (self.second_point[1] + ((self.first_point[1] - self.second_point[1])
/ 2)) - 10
self.inline_position = [(self.first_point[0] + 10, self.first_point[1] + 5),
(self.second_point[0], self.second_point[1] - 10),
(self.third_point[0] - 10, self.third_point[1] + 5)]
self.triangle = pygame.draw.polygon(game_window, colour, self.position)
self.triangle_outline = pygame.draw.polygon(game_window, gold, self.position, 5)
self.triangle_inline = pygame.draw.polygon(game_window, gold, self.inline_position, 2)
# draw letter
pygame.font.init()
my_font_size = 50
my_font = pygame.font.SysFont('Time New Roman', my_font_size)
text_surface = my_font.render('A', 1, gold)
center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
game_window.blit(text_surface, center_text)
class Pikeman(Unit):
def __init__(self, colour, position):
# draw circle, inline, and outline
super().__init__(colour, position)
self.dimension = (80, 80)
self.center_x = position[0] + self.dimension[0] / 2
self.center_y = position[1] + self.dimension[1] / 2
self.colour = colour
self.position = position
# define_tile_centre
self.tile_area = pygame.Rect(position[0] - 10, position[1] - 10, 100, 100)
self.position_and_dimension = self.position + self.dimension
self.inline_position_and_dimension = (self.position[0] + 5, self.position[1] + 5),\
(self.dimension[0] - 10, self.dimension[1] - 10)
self.square = pygame.draw.rect(game_window, colour, self.position_and_dimension, )
self.square_outline = pygame.draw.rect(game_window, gold, self.position_and_dimension, 5)
self.square_inline = pygame.draw.rect(game_window, gold, self.inline_position_and_dimension, 2)
# draw letter
pygame.font.init()
my_font_size = 50
my_font = pygame.font.SysFont('Time New Roman', my_font_size)
text_surface = my_font.render('P', 1, gold)
center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
game_window.blit(text_surface, center_text)
def highlight_tile(tile_area):
pygame.draw.rect(game_window, dark_green, tile_area, 5)
pygame.display.update()
# Sets and gets the coordinates for black and white squares
coordinator = coordinator()
black_columns = coordinator[2] + coordinator[3]
white_columns = coordinator[0] + coordinator[1]
# Creates needed objects
ws = WhiteSquare()
bs = BlackSquare()
cb = ChessBoard()
cs = CircleSurface()
# Event loop (outer)
while 1:
# Draws the chessboard
cb.draw()
# set up white units
white_knight_1 = Knight(white, (150, 650))
white_knight_2 = Knight(white, (650, 650))
white_archer_1 = Archer(white, (210, 790), (250, 710), (290, 790))
white_archer_2 = Archer(white, (310, 790), (350, 710), (390, 790))
white_archer_3 = Archer(white, (410, 790), (450, 710), (490, 790))
white_archer_4 = Archer(white, (510, 790), (550, 710), (590, 790))
white_pikeman_1 = Pikeman(white, (210, 610))
white_pikeman_2 = Pikeman(white, (310, 610))
white_pikeman_3 = Pikeman(white, (410, 610))
white_pikeman_4 = Pikeman(white, (510, 610))
white_army = [white_knight_1, white_knight_2,
white_archer_1, white_archer_2, white_archer_3, white_archer_4,
white_pikeman_1, white_pikeman_2, white_pikeman_3, white_pikeman_4]
# set up black units
black_knight_1 = Knight(black, (150, 150))
black_knight_2 = Knight(black, (650, 150))
black_archer_1 = Archer(black, (210, 90), (250, 10), (290, 90))
black_archer_2 = Archer(black, (310, 90), (350, 10), (390, 90))
black_archer_3 = Archer(black, (410, 90), (450, 10), (490, 90))
black_archer_4 = Archer(black, (510, 90), (550, 10), (590, 90))
black_pikeman_1 = Pikeman(black, (210, 110))
black_pikeman_2 = Pikeman(black, (310, 110))
black_pikeman_3 = Pikeman(black, (410, 110))
black_pikeman_4 = Pikeman(black, (510, 110))
# Event loop (inner)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# mouse handling
for unit in white_army:
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_position = pygame.mouse.get_pos()
if unit.tile_area.collidepoint(mouse_position):
highlight_tile(unit.tile_area)
else:
pygame.display.update()
pygame.display.update()
EDIT
I have managed to not make it disappear, but now I want it to disappear when the mouse is being clicked again.
here is the new block of code updated
if event.type == pygame.MOUSEBUTTONDOWN:
for unit in white_army:
mouse_position = pygame.mouse.get_pos()
if unit.tile_area.collidepoint(mouse_position):
print(mouse_position)
highlight_tile(unit.tile_area)
I tried with a while loop but it just makes the game crash, does not look like a good idea.
So, I thought had something to do with updating the display (a function that I am already calling inside the highlight_tile() method)
Of course.
Create a variable (selected_unit) which states the unit which is currently selected. If the mouse button is pressed the clear the state and set the new selection state according to the mouse position.
Once the variable is selected, then highlight the shape in the main application loop rather than in the event loop. e.g.:
selected_unit = None
while 1:
# Draws the chessboard
cb.draw()
# set up white units
# [...]
# set up black units
# [...]
# Event loop (inner)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# mouse handling
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_position = pygame.mouse.get_pos()
if selected_unit:
# reset the selection
selected_unit = None
else:
# select a new unit
for unit in white_army:
if unit.tile_area.collidepoint(mouse_position):
selected_unit = unit
# highlight the selected unit
if selected_unit:
highlight_tile(selected_unit.tile_area)
# update the dispaly
pygame.display.update()
I am trying to learn PyMunk and I used their basic example from the website:
import pymunk
space = pymunk.Space()
space.gravity = 0,-1000
body = pymunk.Body(1,1666)
body.position = 50,100
poly = pymunk.Poly.create_box(body)
space.add(body, poly)
while True:
space.step(0.02)
But it does not create a window, does not show anything. How to use PyGame to create the graphical window?
What that example does is create a simulation, add a box shaped object inside and then run the simulation infinitely. The code doesn't print or draw anything, so you will not actually see the output. To get a better understanding and something on screen I suggest you start with the tutorial: http://www.pymunk.org/en/latest/tutorials/SlideAndPinJoint.html
Pymunk is a 2d rigid body physics library, which means that what it does is simulate how objects move and interact with each other in 2 dimensions. Its not made for drawing to the screen or read input.
You can of course use it as is without anything else, and just print out the result of the simulation. But more common is that you want to draw to the screen, read input and so on. One way to do that is by using the game library Pygame that helps out with drawing to the screen, reading input, having a game loop and so on.
Pymunk itself does have some helper functions so that you can easily connect it with Pygame (and a couple of other libraries), but this is not the core part. Usually these helper functions are good for when you want something quick-n-dirty such as a prototype and you don't have need to customize the drawing.
Now, this said, if you want to see something you can add a print statement to the while loop, so it becomes like this:
while True:
space.step(0.02)
print(body.position)
Then it will print out the position of the ball each step of the simulation, and you can see that its changing all the time (because of the gravity that is set on the space).
There are more advanced examples included in Pymunk that are both interactive and show something on screen. These examples depends on mostly either Pygame or Pyglet, but the principle is the same in case you have a different library you want to use it with.
Here's an example that shows how I use Pymunk in combination with pygame. The Entity class is a pygame.sprite.Sprite subclass to which I attach a pymunk.Body and a pymunk.Shape as well as a reference to the pm.Space, so that the bodies and shapes can be added and removed from it. The position of the sprite's rect gets set to the self.body.position each frame, so that we get the correct blit position for the self.image and can simply draw all sprites by calling self.sprite_group.draw(self.screen).
import math
import pygame as pg
import pymunk as pm
from pymunk import Vec2d
def flipy(p):
"""Convert chipmunk coordinates to pygame coordinates."""
return Vec2d(p[0], -p[1]+600)
class Entity(pg.sprite.Sprite):
def __init__(self, pos, space):
super().__init__()
self.image = pg.Surface((46, 52), pg.SRCALPHA)
pg.draw.polygon(self.image, (0, 50, 200),
[(0, 0), (48, 0), (48, 54), (24, 54)])
self.orig_image = self.image
self.rect = self.image.get_rect(topleft=pos)
vs = [(-23, 26), (23, 26), (23, -26), (0, -26)]
mass = 1
moment = pm.moment_for_poly(mass, vs)
self.body = pm.Body(mass, moment)
self.shape = pm.Poly(self.body, vs)
self.shape.friction = .9
self.body.position = pos
self.space = space
self.space.add(self.body, self.shape)
def update(self, dt):
pos = flipy(self.body.position)
self.rect.center = pos
self.image = pg.transform.rotate(
self.orig_image, math.degrees(self.body.angle))
self.rect = self.image.get_rect(center=self.rect.center)
# Remove sprites that have left the screen.
if pos.x < 20 or pos.y > 560:
self.space.remove(self.body, self.shape)
self.kill()
def handle_event(self, event):
if event.type == pg.KEYDOWN:
if event.key == pg.K_a:
self.body.angular_velocity = 5.5
elif event.key == pg.K_w:
self.body.apply_impulse_at_local_point(Vec2d(0, 900))
class Game:
def __init__(self):
self.done = False
self.clock = pg.time.Clock()
self.screen = pg.display.set_mode((800, 600))
self.gray = pg.Color('gray68')
self.red = pg.Color('red')
# Pymunk stuff.
self.space = pm.Space()
self.space.gravity = Vec2d(0.0, -900.0)
self.static_lines = [
pm.Segment(self.space.static_body, (60, 100), (370, 100), 0),
pm.Segment(self.space.static_body, (370, 100), (600, 300), 0),
]
for lin in self.static_lines:
lin.friction = 0.8
self.space.add(self.static_lines)
# A sprite group which holds the pygame.sprite.Sprite objects.
self.sprite_group = pg.sprite.Group(Entity((150, 200), self.space))
def run(self):
while not self.done:
self.dt = self.clock.tick(30) / 1000
self.handle_events()
self.run_logic()
self.draw()
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
if event.type == pg.MOUSEBUTTONDOWN:
self.sprite_group.add(Entity(flipy(event.pos), self.space))
for sprite in self.sprite_group:
sprite.handle_event(event)
def run_logic(self):
self.space.step(1/60) # Update physics.
self.sprite_group.update(self.dt) # Update pygame sprites.
def draw(self):
self.screen.fill(pg.Color(140, 120, 110))
for line in self.static_lines:
body = line.body
p1 = flipy(body.position + line.a.rotated(body.angle))
p2 = flipy(body.position + line.b.rotated(body.angle))
pg.draw.line(self.screen, self.gray, p1, p2, 5)
self.sprite_group.draw(self.screen)
# Debug draw. Outlines of the Pymunk shapes.
for obj in self.sprite_group:
shape = obj.shape
ps = [pos.rotated(shape.body.angle) + shape.body.position
for pos in shape.get_vertices()]
ps = [flipy((pos)) for pos in ps]
ps += [ps[0]]
pg.draw.lines(self.screen, self.red, False, ps, 1)
pg.display.flip()
if __name__ == '__main__':
pg.init()
Game().run()
pg.quit()
I have some simple circles (boids) that move in a way that simulates birds; they should avoid getting too close to each other while maintaining the same general heading et cetera.
I'm using pygame but the circles don't move unless I press one of the buttons in the GUI, which seems kind of strange but I can't figure out where I messed up.
The most relevant part of the code is probably the gui function and the draw function inside the Boid class.
import pygame
import numpy as np
import sys
import math
class BoidWorld:
# Boid movement parameters
w_separation = 10
w_alignment = 1
w_cohesion = 1
w_avoidance = 0
w_flee = 50
dim = 0 # dim*dim = Size of world
neighbour_radius = 100
max_velocity = 100
# Objects in world
boids = []
predators = []
obstacles = []
def __init__(self, dim):
self.dim = dim
def update_boid_velocity(self, boid):
# Flee from predators, if any
predator = self.get_predator(boid)
flee_x, flee_y = self.calc_flee_force(boid, predator)
# Avoid obstacles, if any
obstacle = self.get_obstacle(boid)
avoid_x, avoid_y = self.calc_avoidance_force(boid, obstacle)
# Get neighbours within radius r
neighbours = self.get_neighbours(boid)
sep_x, sep_y = self.calc_separation_force(boid, neighbours)
align_x, align_y = self.calc_alignment_force(neighbours)
coh_x, coh_y = self.calc_cohesion_force(neighbours)
boid.velocity_x += self.w_separation * sep_x + self.w_alignment * align_x + self.w_cohesion * coh_x + \
self.w_avoidance * avoid_x + self.w_flee * flee_x
boid.velocity_y += self.w_separation * sep_y + self.w_alignment * align_y + self.w_cohesion * coh_y + \
self.w_avoidance * avoid_y + self.w_flee * flee_y
# Limit velocity by creating unit vectors and multiplying by max velocity
v = math.sqrt(boid.velocity_x**2 + boid.velocity_y**2)
if v > self.max_velocity:
boid.velocity_x = boid.velocity_x*self.max_velocity/v
boid.velocity_y = boid.velocity_y*self.max_velocity/v
boid.position_x += boid.velocity_x
boid.position_y += boid.velocity_y
print(boid.velocity_x, boid.velocity_y)
# Wrap around
if boid.position_x > self.dim or boid.position_x < 0:
boid.position_x %= self.dim
if boid.position_y > self.dim or boid.position_y < 0:
boid.position_y %= self.dim
def update_predator_velocity(self, predator):
pass
def calc_separation_force(self, boid, neighbours):
sep_x = 0.
sep_y = 0.
for b in neighbours:
sep_x = sep_x - (b.position_x - boid.position_x)
sep_y = sep_y - (b.position_y - boid.position_y)
return sep_x, sep_y
def calc_alignment_force(self, neighbours):
if not neighbours: return 0, 0
avg_heading_x = 0.
avg_heading_y = 0.
for b in neighbours:
avg_heading_x += b.velocity_x
avg_heading_y += b.velocity_y
return avg_heading_x/len(neighbours), avg_heading_y/len(neighbours)
def calc_cohesion_force(self, neighbours):
if not neighbours: return 0, 0
avg_pos_x = 0.
avg_pos_y = 0.
for b in neighbours:
avg_pos_x += b.position_x
avg_pos_y += b.position_y
return avg_pos_x/len(neighbours), avg_pos_y/len(neighbours)
# Flee straight away from predators
def calc_flee_force(self, boid, predator):
if not predator: return 0
return boid.position - predator.position
# Avoid obstacles
def calc_avoidance_force(self, boid, obstacle):
if not obstacle: return 0
return 0
# Predators chasing boids
def calc_chasing_force(self, predator, boids):
return 0
def get_predator(self, boid):
for predator in self.predators:
if self.is_neighbour(predator, boid):
return predator
return None
def get_obstacle(self, boid):
for obstacle in self.obstacles:
if self.is_neighbour(obstacle, boid):
return obstacle
return None
def is_neighbour(self, boid1, boid2):
if np.power(boid2.position_x - boid1.position_x, 2) + \
np.power(boid2.position_y - boid1.position_y, 2) \
< np.power(self.neighbour_radius, 2):
return True
return False
def get_neighbours(self, boid):
neighbours = []
for b in self.boids:
if b != boid and self.is_neighbour(b, boid):
neighbours.append(b)
return neighbours
def add_boid(self):
self.boids.append(Boid(
self.rand_position(), self.rand_position(),
self.rand_velocity(), self.rand_velocity()
))
def add_obstacle(self):
self.obstacles.append(Obstacle(
self.rand_position(), self.rand_position()))
def add_predator(self):
self.predators.append(Predator(
self.rand_position(), self.rand_position(),
self.rand_velocity(), self.rand_velocity()
))
def remove_boids(self):
self.boids = []
def remove_obstacles(self):
self.obstacles = []
def remove_predators(self):
self.predators = []
def rand_position(self):
return float(np.random.randint(0, self.dim))
def rand_velocity(self):
return float(np.random.randint(0, self.max_velocity))
class Boid(object):
color_circle = (100, 0, 0)
color_line = (100, 0, 100)
radius = 10
position_x = 0.
position_y = 0.
velocity_x = 0.
velocity_y = 0.
def __init__(self, position_x, position_y, velocity_x, velocity_y):
self.position_x = position_x
self.position_y = position_y
self.velocity_x = velocity_x
self.velocity_y = velocity_y
def draw(self, screen):
pygame.draw.circle(screen, self.color_circle, (int(round(self.position_x)), int(round(self.position_y))),
self.radius, 0)
# Velocity vector
pygame.draw.lines(screen, self.color_line, False, [
(int(round(self.position_x)), int(round(self.position_y))),
(int(round(self.position_x+self.velocity_x)), int(round(self.position_y+self.velocity_y)))
], 2)
class Predator(Boid):
color_circle = (100, 55, 0)
color_line = (100, 0, 100)
radius = 20
class Obstacle:
color = (0, 33, 50)
position_x = 0.
position_y = 0.
radius = 15
def __init__(self, position_x, position_y):
self.position_x = position_x
self.position_y = position_y
def draw(self, screen):
pygame.draw.circle(screen, self.color, (int(round(self.position_x)), int(round(self.position_y))),
self.radius, 0)
def main():
pygame.init()
boid_world = BoidWorld(800)
boid_world.add_boid()
gui(boid_world)
def gui(boid_world):
weight_inc = 0.1
btn_boid_add = Button('Add boid')
btn_boid_rem = Button('Remove boids')
btn_obst_add = Button('Add obstacle')
btn_obst_rem = Button('Remove obstacles')
btn_pred_add = Button('Add predator')
btn_pred_rem = Button('Remove predators')
btn_sep_p = Button('+')
btn_sep_m = Button('-')
btn_ali_p = Button('+')
btn_ali_m = Button('-')
btn_coh_p = Button('+')
btn_coh_m = Button('-')
pygame.font.init()
font = pygame.font.Font(None, 20)
font_color = (255, 255, 255)
screen = pygame.display.set_mode((1200, 800))
screen_half = screen.subsurface((400, 0, 800, 800))
pygame.display.set_caption('Boids')
clock = pygame.time.Clock()
run = True
while run:
screen.fill((0, 0, 0))
mouse = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if btn_boid_add.obj.collidepoint(mouse):
boid_world.add_boid()
elif btn_boid_rem.obj.collidepoint(mouse):
boid_world.remove_boids()
elif btn_obst_add.obj.collidepoint(mouse):
boid_world.add_obstacle()
elif btn_obst_rem.obj.collidepoint(mouse):
boid_world.remove_obstacles()
elif btn_pred_add.obj.collidepoint(mouse):
boid_world.add_predator()
elif btn_pred_rem.obj.collidepoint(mouse):
boid_world.remove_predators()
elif btn_sep_m.obj.collidepoint(mouse):
boid_world.w_separation -= weight_inc
elif btn_sep_p.obj.collidepoint(mouse):
boid_world.w_separation += weight_inc
elif btn_ali_p.obj.collidepoint(mouse):
boid_world.w_alignment -= weight_inc
elif btn_ali_m.obj.collidepoint(mouse):
boid_world.w_alignment += weight_inc
elif btn_coh_m.obj.collidepoint(mouse):
boid_world.w_cohesion -= weight_inc
elif btn_coh_p.obj.collidepoint(mouse):
boid_world.w_cohesion += weight_inc
btn_boid_add.draw(screen, mouse, (10, 10, 100, 20), (15, 15))
btn_boid_rem.draw(screen, mouse, (120, 10, 130, 20), (125, 15))
btn_obst_add.draw(screen, mouse, (10, 40, 100, 20), (15, 45))
btn_obst_rem.draw(screen, mouse, (120, 40, 130, 20), (125, 45))
btn_pred_add.draw(screen, mouse, (10, 70, 100, 20), (15, 75))
btn_pred_rem.draw(screen, mouse, (120, 70, 130, 20), (125, 75))
btn_sep_m.draw(screen, mouse, (120, 100, 20, 20), (125, 105))
btn_sep_p.draw(screen, mouse, (150, 100, 20, 20), (155, 105))
btn_ali_m.draw(screen, mouse, (120, 130, 20, 20), (125, 135))
btn_ali_p.draw(screen, mouse, (150, 130, 20, 20), (155, 135))
btn_coh_m.draw(screen, mouse, (120, 160, 20, 20), (125, 165))
btn_coh_p.draw(screen, mouse, (150, 160, 20, 20), (155, 165))
screen.blit(font.render('Separation', 1, font_color), (15, 105))
screen.blit(font.render('Alignment', 1, font_color), (15, 135))
screen.blit(font.render('Cohesion', 1, font_color), (15, 165))
for boid in boid_world.boids:
boid_world.update_boid_velocity(boid)
boid.draw(screen_half)
for obstacle in boid_world.obstacles:
obstacle.draw(screen_half)
for predator in boid_world.predators:
boid_world.update_predator_velocity(predator)
predator.draw(screen_half)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
class Button:
def __init__(self, text):
self.text = text
self.is_hover = False
self.default_color = (100, 100, 100)
self.hover_color = (255, 255, 255)
self.font_color = (100, 0, 0)
self.obj = None
def label(self):
font = pygame.font.Font(None, 20)
return font.render(self.text, 1, self.font_color)
def color(self):
if self.is_hover:
return self.hover_color
else:
return self.default_color
def draw(self, screen, mouse, rectcoord, labelcoord):
# create rect obj, draw, and change color based on input
self.obj = pygame.draw.rect(screen, self.color(), rectcoord)
screen.blit(self.label(), labelcoord)
# change color if mouse over button
self.check_hover(mouse)
def check_hover(self, mouse):
# adjust is_hover value based on mouse over button - to change hover color
if self.obj.collidepoint(mouse):
self.is_hover = True
else:
self.is_hover = False
if __name__ == "__main__":
main()
It happens because you do all calculation inside
elif event.type == pygame.MOUSEBUTTONDOWN:
MOUSEBUTTONDOWN means that button changes state from UP to DOWN (and it takes very short time). It doesn't means button is holding pressed all the time
If you need to check weather button is holding pressed then use pygame.mouse.get_pressed() but use it outside/after for event loop.
It is similar to key events: