Related
I'm trying to create a animation that runs around a shape. The method I came up with is similar to the snake game, drawing the desired part and refresh it with white screen.
For example, if I want to draw a linear line animation, with the max animated length of 3:
lst = [1, 2, 3]
dummy_lst = [1, 1, 1, 2, 3, 3, 3]
colored_position = [[1,1,1], [1,1,2], [1,2,3], [2,3,3], [3,3,3]] # if the numbers overlapped, it draws the same place which is fine
and then I iterate the colored_position to get the animation done.
The real code:
import pygame
import sys
SCREEN_WIDTH = 800
CELLSIZE = 10
x, y = 150, 200
position = [[x+CELLSIZE*i, y] for i in range(50)] +\
[[x+CELLSIZE*50, y+CELLSIZE*i] for i in range(50)] +\
[[x+CELLSIZE*50-CELLSIZE*i, y+CELLSIZE*50]for i in range(50)] +\
[[x, y+CELLSIZE*50-CELLSIZE*i]for i in range(50)]
# creates fake firsts and lasts
def dummy_list(list, length):
return [list[0]]*(length-1) + list + [list[-1]]*(length-1)
def main():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_WIDTH))
count = 0
while True:
clock.tick(10)
screen.fill((255, 255, 255))
dummy = dummy_list(position, 50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
count = 0
while count < 1: # I could chaget this to see many times it rotates
for i in range(len(dummy)-50+1):
for j in range(9): # change this to adjust how wide the colored part is
pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(
dummy[i+j][0], dummy[i+j][1], CELLSIZE, CELLSIZE), 0)
pygame.display.update()
screen.fill((255, 255, 255))
count += 1
pygame.display.update()
But it seems very hard-coding. since I have to know each part of the desired position's Xs and Ys. What's the smarter way to complete the task?
You want to move the object along a path. I don't see any problem in defining the path through a list of points.
However you can write a function that generates the list of points from a pygame.Rect object:
def generate_positions(rect, step):
return [[x, rect.top] for x in range(rect.left, rect.right, step)] +\
[[rect.right, y] for y in range(rect.top, rect.bottom, step)] +\
[[x, rect.bottom] for x in range(rect.right, rect.left, -step)] +\
[[rect.left, y] for y in range(rect.bottom, rect.top, -step)]
CELLSIZE = 10
x, y = 150, 200
position = generate_positions(pygame.Rect(x, y, 50*CELLSIZE, 50*CELLSIZE), CELLSIZE)
I recommend to use the application loop to draw the snake:
import pygame
import sys
SCREEN_WIDTH = 800
def generate_positions(rect, step):
return [[x, rect.top] for x in range(rect.left, rect.right, step)] +\
[[rect.right, y] for y in range(rect.top, rect.bottom, step)] +\
[[x, rect.bottom] for x in range(rect.right, rect.left, -step)] +\
[[rect.left, y] for y in range(rect.bottom, rect.top, -step)]
CELLSIZE = 10
x, y = 150, 200
position = generate_positions(pygame.Rect(x, y, 10*CELLSIZE, 10*CELLSIZE), CELLSIZE)
def main():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_WIDTH))
current_point = None
snake_len = 9
while True:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
current_point = -snake_len
screen.fill((255, 255, 255))
if current_point != None:
for i in range(max(0, current_point), min(len(position), current_point+snake_len)):
pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(*position[i], CELLSIZE, CELLSIZE), 0)
current_point += 1
if current_point >= len(position):
current_point = None
pygame.display.update()
main()
The problem is that I want to draw small square/rect in a circle from left to right. Somewhat like this:
In pygame, the rectangles number should be 120.
But whenever use this function:
x = int(math.cos(angle) * 50) + origin X
y = int(math.sin(angle) * 50) + origin Y
It starts from two different points, and if I adjust the angle it starts at five different points.
I want it to start from right in full circle.
import pygame, sys, math
run = True
white = (255, 255, 255)
black = (0, 0, 0)
angle = 0
size = widht, height = 800, 600
pygame.init()
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
run = True
while run:
msElapsed = clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(white)
i = 1
while i < 28:
x = int(math.cos(angle) * 100) + 300
y = int(math.sin(angle) * 100) + 200
pygame.draw.rect(screen, black, (x, y, 10, 10))
i += 1
angle += 5
print(x)
print(y)
pygame.display.flip()
pygame.quite()
and output are
The unit of the angle for Trigonometric functions (e.g. math.cos, math.sin) in the math module is Radian rather than degree. math.pi*2 Radian is 360 Degrees.
If you want to distribute 28 rectangles uniformly around a circle, then the angle from 1 rectangle to the next is math.pi*2 / 28. e.g.:
run = True
while run:
msElapsed = clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(white)
for i in range(28):
angle = i / 28 * math.pi * 2 # 2*pi radian == 360°
x = int(math.cos(angle) * 100) + 400
y = int(math.sin(angle) * 100) + 300
pygame.draw.rect(screen, black, (x-5, y-5, 10, 10))
pygame.display.flip()
What the title says, I have an image that I can control with W,S,A,D. But I want to make the player image that is movable rotate to face the position of my mouse. Here's the relevent code:
import pygame
import random
import time
import math
import sys
pygame.init()
#The size of the game window
display_width = 1280
display_height = 800
#Colors available
black = (0, 0, 0) #colours defined by RGB,
white = (255, 255, 255)
red = (200, 0, 0)
green = (0, 150, 0)
bright_red = (255, 0, 0)
bright_green =(0, 255, 0)
#This code opens up the Game window
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Blockslayer")
clock = pygame.time.Clock()
pygame.mouse.set_visible(True)
#player character info
slayerImg = pygame.image.load('squareslayer.png').convert_alpha()
slayerWidth = 84
slayerHeight = 84
#mouse info
mouse_c = pygame.image.load("crosshair.png ").convert_alpha()
def crosshair(mousex,mousey):
mousex, mousey = pygame.mouse.get_pos()
small_ch = pygame.transform.scale(mouse_c, (20, 20))
gameDisplay.blit(small_ch, (mousex, mousey,))
print(mousex,mousey)
#player character
def slayer(x,y,):
#small_slayer = pygame.transform.scale(slayerImg, (120, 80,))
pos = pygame.mouse.get_pos()
angle = 360 - math.atan2(pos[1] - 84, pos[0] - 84) * 180 / math.pi
rotimage = pygame.transform.rotate((slayerImg), angle,)
rect = rotimage.get_rect(center=(x, y))
gameDisplay.blit(rotimage, rect,)
pygame.display.update()
#Game Logic
def block_game_loop():
x = (display_width * 0.45)
y = (display_height * 0.8)
pos = pygame.mouse.get_pos()
angle = 360 - math.atan2(pos[1] + x - 84, pos[0] + y - 84) * 180 / math.pi
rotimage = pygame.transform.rotate((slayerImg), angle,)
mousex, mousey = pygame.mouse.get_pos()
#def blockguy(blockguyX, blockguyY, blockguyW, blockguyH, ):
#blockguyX = random.randrange(0, 785)
#blockguyY = random.randrange (0, 600)
#blockguyW = 166
#blockguyH = 110
#blockguy_speed = 5
#Event handler
exit_game = False
while not exit_game:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_s]: y += 7
if pressed[pygame.K_w]: y -= 7
if pressed[pygame.K_a]: x -= 7
if pressed[pygame.K_d]: x += 7
gameDisplay.fill(white)
slayer(x, y,)
#Boundaries
if x > display_width:
x = 1275
if x < 0:
x = 5
if y > display_height:
y = 795
if y < 0:
y = 5
crosshair(mousex,mousey)
#blockguy(blockguyX, blockguyY, blockguyW, blockguyH, )
pygame.display.update()
clock.tick(60)
block_game_loop()
pygame.quit()
quit()
Code is pretty janked together because I don't really know wtf im doing, but here's how the thing works so far
Youtube video: https://www.youtube.com/watch?v=zShWAm4pSx8&feature=youtu.be
Take a look at this rotate function (read the comments).
import math
import pygame
pygame.init()
gray = (30, 30, 30)
display_width, display_height = (1280, 800)
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
slayerImg = pygame.Surface((104, 84), pygame.SRCALPHA)
pygame.draw.polygon(slayerImg, (0, 120, 250), [(1, 1), (103, 42), (1, 83)])
def rotate(x, y, mouse_pos, image):
# Calculate x and y distances to the mouse pos.
run, rise = (mouse_pos[0]-x, mouse_pos[1]-y)
# Pass the rise and run to atan2 (in this order)
# and convert the angle to degrees.
angle = math.degrees(math.atan2(rise, run))
# Rotate the image (use the negative angle).
rotimage = pygame.transform.rotate(image, -angle)
rect = rotimage.get_rect(center=(x, y))
return rotimage, rect
def block_game_loop():
x = display_width * 0.45
y = display_height * 0.8
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
pressed = pygame.key.get_pressed()
if pressed[pygame.K_s]: y += 7
if pressed[pygame.K_w]: y -= 7
if pressed[pygame.K_a]: x -= 7
if pressed[pygame.K_d]: x += 7
mousex, mousey = pygame.mouse.get_pos()
# Boundaries
if x > display_width:
x = 1275
if x < 0:
x = 5
if y > display_height:
y = 795
if y < 0:
y = 5
gameDisplay.fill(gray)
rotimage, rect = rotate(x, y, (mousex, mousey), slayerImg)
gameDisplay.blit(rotimage, rect)
pygame.display.update()
clock.tick(60)
block_game_loop()
pygame.quit()
Addendum: Here's an example that shows you how you can shoot rotated bullets. You need some basic trigonometry knowledge, because you need to calculate the x- and y-velocity with math.cos and math.sin. That will give you a unit vector (with length 1) that you need to scale to the desired speed. Now you need to put the velocity list together with a rect, an extra position list and the rotated image into a list that represents a bullet object. To update the rect position you first have to add the velocity to the pos and then assign the pos to the rect center (you have to do it that way, because rects can only have ints as the x and y position).
I recommend to use pygame.math.Vector2s and pygame sprites and sprite groups instead of lists as you can see in the linked answer, because that's a lot nicer to read. You still need to add code to remove bullets, which would also be simpler to implement with sprites and sprite groups.
import math
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 24)
BLACK = pg.Color('black')
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(BULLET_IMAGE, pg.Color('grey11'), [(0, 0), (20, 5), (0, 11)])
def update_bullets(bullets):
"""Add the velocity to the pos then assign pos to the rect center."""
for bullet_rect, pos, velocity, _ in bullets:
pos[0] += velocity[0]
pos[1] += velocity[1]
bullet_rect.center = pos
def draw_bullets(bullets, screen):
for bullet_rect, pos, _, image in bullets:
screen.blit(image, bullet_rect)
pg.draw.rect(screen, (200, 140, 0), bullet_rect, 1)
def main():
clock = pg.time.Clock()
# The cannon image and rect.
cannon_img = pg.Surface((60, 22), pg.SRCALPHA)
pg.draw.rect(cannon_img, pg.Color('grey19'), [0, 0, 35, 22])
pg.draw.rect(cannon_img, pg.Color('grey19'), [35, 6, 35, 10])
orig_cannon_img = cannon_img # Store orig image to preserve quality.
cannon = cannon_img.get_rect(center=(320, 240))
angle = 0 # Angle of the cannon.
# Add bullets to this list. Bullets will also be lists
# consisting of a pygame.Rect, the velocity and the image.
bullets = []
bullet_speed = 5
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
elif event.type == pg.MOUSEBUTTONDOWN:
# Left button fires a bullet from cannon center with
# current angle. Add the bullet to the bullets list.
if event.button == 1:
# Use cosine and sine to calculate the x and y
# velocity. Scale them by the desired speed.
velocity = (math.cos(math.radians(angle)) * bullet_speed,
math.sin(math.radians(angle)) * bullet_speed)
img = pg.transform.rotate(BULLET_IMAGE, -angle)
bullet_rect = img.get_rect(center=cannon.center)
# The extra pos list is needed because the pygame.Rect
# can only have ints as the x and y value. We still
# need the rect for collision detection.
pos = list(bullet_rect.center)
bullet = [bullet_rect, pos, velocity, img]
bullets.append(bullet)
update_bullets(bullets)
# Find angle to target (mouse pos).
x, y = Vector2(pg.mouse.get_pos()) - cannon.center
angle = math.degrees(math.atan2(y, x))
# Rotate the cannon image.
cannon_img = pg.transform.rotate(orig_cannon_img, -angle)
cannon = cannon_img.get_rect(center=cannon.center)
# Draw
screen.fill(pg.Color('darkseagreen4'))
draw_bullets(bullets, screen)
screen.blit(cannon_img, cannon)
txt = FONT.render('angle {:.1f}'.format(angle), True, BLACK)
screen.blit(txt, (10, 10))
pg.draw.line(
screen, pg.Color(150, 60, 20),
cannon.center, pg.mouse.get_pos(), 2)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
Here Big Fish, this is my code for that problem:
import math
sprite = sprite_new("spr_plasma_blast")
targetx = 0
targety = 0
m = 0
time = 0
speed = 8
if time == 0:
m = (targety - y)/(targetx - x)
xSpeed = math.sqrt((speed*speed)/((m*m) + 1))
if targetx < x:
xSpeed = -xSpeed
x = x + xSpeed
y = y + xSpeed*m
time = time + 1
Apologies in advance if this is a simple fix but I couldn't find anything on it. I am relatively new to pygame but I can't understand why when I run this the first bar that is drawn is always half cut off. To me anyway I should start a 0,400 and draw from 0 across 40 and then up. If this is not the case please enlighten a curious mind
from pygame import *
import pygame, sys, random
pygame.init()
screen = pygame.display.set_mode((1000,400))
colour = (0, 255, 0)
array = []
x, y, z, b = -80, 0, 0, 0
flag = True
for c in range(5):
array.append(random.randint(100, 400));
for c in array:
print c
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if len(array) == z:
flag = False
if flag == True:
b = array[z]
x += 80
z += 1
pygame.draw.line(screen, colour, (x, 400), (x, (400-b)), 40)
pygame.display.update()
pygame is drawing a line from (0,400) to (0, 400-b), with line thickness 40.
Here is an way to shift the lines so each is fully visible:
for i, b in enumerate(array):
x = i*80 + 20 # <-- add 20 to shift by half the linewidth
pygame.draw.line(screen, colour, (x, 400), (x, (400-b)), 40)
import sys
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((1000,400))
colour = (0, 255, 0)
array = [random.randint(100, 400) for c in range(5)]
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
linewidth = 40
for i, b in enumerate(array):
x = i*linewidth*2 + linewidth//2
pygame.draw.line(screen, colour, (x, 400), (x, (400-b)), linewidth)
pygame.display.update()
I want to draw a parabola in pygame. I have made a pixelarray object and loop through it to determine if a pixel is on the parabola or not. I seem to get an image that has gaps between the points. How do I make it a single continuous line?
import pygame
import sys
from pygame.locals import *
import math
WIDTH = 640
HEIGHT = 480
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT), 0, 32)
pxarray = pygame.PixelArray(screen)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
for y, py in enumerate(pxarray):
for x, px in enumerate(py):
if int(x) == (int(y)*int(y)) - 30*int(y) + 450:
pxarray[y][x] = 0xFFFFFF
pygame.display.update()
color = 255, 0, 0
first = True
prev_x, prev_y = 0, 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
for y, py in enumerate(pxarray):
for x, px in enumerate(py):
if int(x) == (int(y)*int(y)) - 30*int(y) + 450:
pxarray[y][x] = 0xFFFFFF
if first:
first = False
prev_x, prev_y = x, y
continue
pygame.draw.line(screen, color, (prev_y, prev_x), (y, x))
prev_x, prev_y = x, y
first = True
pygame.display.flip()
You'll need to draw the parabola as a series of line segments. How many segments you use will determine how smooth it will look, so if you're computing the parabola x = y^2 + 30y + 450, you'd compute the x values for a number of y values and draw lines from (x0,y0) to (x1,y1) and so on.
If you don't have a line drawing primitive, you'll need to implement one using something like Bresenham's Algorithm.