I am trying to find a way to get the rotation of the ship and apply it to the beam so that it will travel in the direction it was shot in.. I really have no idea how I would do this but here is my code: Please excuse the messiness of it, I put comments in so you know whats what.
import sys, pygame, math, time;
from pygame.locals import *;
spaceship = ('spaceship.png')
mouse_c = ('crosshair.png')
backg = ('background.jpg')
fire_beam = ('beams.png')
pygame.init()
screen = pygame.display.set_mode((800, 600))
bk = pygame.image.load(backg).convert_alpha()
mousec = pygame.image.load(mouse_c).convert_alpha()
space_ship = pygame.image.load(spaceship).convert_alpha()
f_beam = pygame.image.load(fire_beam).convert_alpha()
f_beam = pygame.transform.scale(f_beam, (50, 50))
f_beam_rect = f_beam.get_rect()
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
space_ship_rect = space_ship.get_rect()
space_ship_rect.centerx = 375
space_ship_rect.centery = 300
speed = 3.5
pressed_down = 0
while True:
clock.tick(60)
screen.blit(bk, (0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
pressed_down = 1
elif event.type == MOUSEBUTTONUP:
pressed_down = 0
if pressed_down == 1:
x, y = pygame.mouse.get_pos()
x1, y1 = x - space_ship_rect.x, y - space_ship_rect.y
angle = math.atan2(y1, x1)
dx = speed*math.cos(angle)
dy = speed*math.sin(angle)
movex = space_ship_rect.centerx = space_ship_rect.centerx + dx#ship x
movey = space_ship_rect.centery = space_ship_rect.centery + dy#ship y
if event.type == MOUSEMOTION:
x1, y1 = pygame.mouse.get_pos()
x2, y2 = space_ship_rect.x, space_ship_rect.y
dx, dy = x2 - x1, y2 - y1
rads = math.atan2(dx, dy)
degs = math.degrees(rads)
display_s = pygame.transform.rotate(space_ship, (degs))#rotation of ship
if event.type == MOUSEBUTTONDOWN and event.button == 1:
#Is it possible for me to get the degree rotation of the space_ship and apply it to here so the beam will travel in the direction it was shot in?
screen.blit(display_s, (space_ship_rect.centerx, space_ship_rect.centery))
pos = pygame.mouse.get_pos()
screen.blit(mousec, (pos))
pygame.display.update()
I see you have problem with ship rotation.
If you create rotated spaceship display_s you get image with different size than space_ship size so you have to get display_s rectangle and assign spaceship center to display_s center.
display_s_rect = display_s.get_rect( center=spaceship_rect.center)
Now you have to use display_s_rect to display display_s
screen.blit(display_s, display_s_rect)
By the way:
blit() expect position where to put left top corner of blited image on screen.
With
screen.blit(display_s, (space_ship_rect.centerx, space_ship_rect.centery))
left top corner of display_s will be put in (space_ship_rect.centerx, space_ship_rect.centery) but I think you want to put display_s center in (space_ship_rect.centerx, space_ship_rect.centery)
Assign center values (as before) to (space_ship_rect.centerx, space_ship_rect.centery) but use (space_ship_rect.x, space_ship_rect.y) in blit().
You can use space_ship_rect in place of (space_ship_rect.x, space_ship_rect.y) in blit() with the same result.
I think you have the same problem with mousec position in blit().
Get mousec rectangle, assign mouse position to rectangle center and than use rectangle x,y to blit.
mousec_rect = mousec.get_rect( center = pygame.mouse.get_pos() )
screen.blit(mousec, mousec_rect)
EDIT:
your mainloop after my modification - now ship is rotate as it should
display_s = space_ship # default value at start when ship wasn't rotate
while True:
clock.tick(60)
screen.blit(bk, (0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
pressed_down = 1
elif event.type == MOUSEBUTTONUP:
pressed_down = 0
if pressed_down == 1:
x, y = pygame.mouse.get_pos()
x1, y1 = x - space_ship_rect.x, y - space_ship_rect.y
angle = math.atan2(y1, x1)
dx = speed*math.cos(angle)
dy = speed*math.sin(angle)
movex = space_ship_rect.centerx = space_ship_rect.centerx + dx#ship x
movey = space_ship_rect.centery = space_ship_rect.centery + dy#ship y
if event.type == MOUSEMOTION:
x1, y1 = pygame.mouse.get_pos()
x2, y2 = space_ship_rect.centerx, space_ship_rect.centery
dx, dy = x2 - x1, y2 - y1
rads = math.atan2(dx, dy)
degs = math.degrees(rads)
display_s = pygame.transform.rotate(space_ship, (degs))#rotation of ship
display_s_rect = display_s.get_rect(center = space_ship_rect.center)
if event.type == MOUSEBUTTONDOWN and event.button == 1:
#Is it possible for me to get the degree rotation of the space_ship and apply it to here so the beam will travel in the direction it was shot in?
pass
screen.blit(display_s, display_s_rect)
#screen.blit(display_s, space_ship_rect)
pos = pygame.mouse.get_pos()
mousec_rect = mousec.get_rect(centerx=pos[0], centery=pos[1])
screen.blit(mousec, mousec_rect )
pygame.display.update()
Before the While Loop:
beam_speed=<somevalue>
f_beam_rect=space_ship_rect #initialise beam position
fired=False #check if beam is fired or not
Inside While Loop
##If beam is fired(right click) blit images through-out the path
if fired:
b_angle = math.atan2(by1, bx1)
bdx = beam_speed*math.cos(b_angle)
bdy = beam_speed*math.sin(b_angle)
f_beam_rect.centerx = f_beam_rect.centerx + bdx
f_beam_rect.centery = f_beam_rect.centery + bdy
screen.blit(f_beam,f_beam_rect)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
###If mouse is clicked then find the direction (mouse position and spaceship)
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
bx, by = pygame.mouse.get_pos()
bx1, by1 = bx - space_ship_rect.x, by - space_ship_rect.y
fired=True
Now you can make a function which checks that beam collides with enemy objects and if True then stop blitting both the beam and that object.
Also reset the variable fired=False and reset beam position to start from spaceship again f_beam_rect=space_ship_rect
Related
Im trying to make a game with pygame where I can click a sprite then click somewhere on the screen for the sprite to move towards. So far, I'm able to click the sprite and get a response but I'm not sure how to tell the sprite to go to a given location where I click. I've seen something online with sprite.goal but I can't seem to make it work.
This is what I have
if event.type==pygame.MOUSEBUTTONDOWN:
pos=pygame.mouse.get_pos()
#White is a rectangle
if White.collidepoint(pos):
Moving=True
elif Moving==True:
#This is where I would tell it to move to pos
I'll show you a very simple example. Write a function that moves a point 1 step to a target point and returns the new position:
def move_to_target(pos, target):
x, y = pos
if x < target[0]:
x += 1
elif x > target[0]:
x -= 1
if y < target[1]:
y += 1
elif y > target[1]:
y -= 1
return (x, y)
Set a new target when the mouse button is pressed and call the function at each frame:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
def move_to_target(pos, target):
x, y = pos
if x < target[0]:
x += 1
elif x > target[0]:
x -= 1
if y < target[1]:
y += 1
elif y > target[1]:
y -= 1
return (x, y)
my_sprite = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(my_sprite, (255, 255, 0), (10, 10), 10)
pos = (200, 200)
target = (200, 200)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
target = event.pos
pos = move_to_target(pos, target)
window.fill(0)
window.blit(my_sprite, pos)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()
For variable speed and a straighter and smoother movement, you need to tweak the function. See How to make smooth movement in pygame.
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
LERP_FACTOR = 0.05
minimum_distance = 0
maximum_distance = 100
def move_to_target(pos, target):
target_vector = pygame.math.Vector2(*target)
follower_vector = pygame.math.Vector2(*pos)
new_follower_vector = pygame.math.Vector2(*pos)
distance = follower_vector.distance_to(target_vector)
if distance > minimum_distance:
direction_vector = (target_vector - follower_vector) / distance
min_step = max(0, distance - maximum_distance)
max_step = distance - minimum_distance
step_distance = min_step + (max_step - min_step) * LERP_FACTOR
new_follower_vector = follower_vector + direction_vector * step_distance
return (new_follower_vector.x, new_follower_vector.y)
my_sprite = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(my_sprite, (255, 255, 0), (10, 10), 10)
pos = (200, 200)
target = (200, 200)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
target = event.pos
pos = move_to_target(pos, target)
window.fill(0)
window.blit(my_sprite, pos)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()
I was learning python and trying to make snake game using pygame library. I made a rectangle which moves using the pressing of keys but rather than moving one block it moves to the end of the block.
Whenever i press in any direction whether it be up , left, right, or down rather than moving 1 box size it moves to the end of the other direction
Could someone tell me why that happens.
import pygame
box_size = 50
num_box = 15
color1 = (169, 215, 81)
color2 = (169,250,81)
x = 0
y = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
break
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
if x < width - box_size:
x = x + box_size
if keys[pygame.K_LEFT]:
if x > 0:
x = x - box_size
if keys[pygame.K_UP]:
if y > 0:
y = y - box_size
if keys[pygame.K_DOWN]:
if y < height - box_size:
y = y + box_size
print(x,y)
for i in range(0,num_box):
if i %2 == 0:
for j in range(0,num_box):
if j%2 == 0:
pygame.draw.rect(win, color1, (box_size*j, box_size*i, box_size, box_size))
else:
pygame.draw.rect(win, color2, (box_size * j, box_size * i, box_size, box_size))
else:
for k in range(0,num_box):
if k%2 == 0:
pygame.draw.rect(win, color2, (box_size*k, box_size*i, box_size, box_size))
else:
pygame.draw.rect(win, color1, (box_size * k, box_size * i, box_size, box_size))
pygame.draw.rect(win, (0, 0, 250), (x, y, box_size, box_size))
# # rect(surface, color, (left, top, width, height))
pygame.display.update()
pass
See pygame.time.Clock.tick():
This method should be called once per frame.
Use pygame.time.Clock to control the frames per second and thus the game speed. The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time. e.g.:
clock = pygame.time.Clock()
while True:
clock.tick(100)
for event in pygame.event.get():
# [...]
If you want to move the object step by step you need to use KEYDOWN event instead of pygame.key.get_pressed():
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# INDENTATION
#-->|
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
if x < width - box_size:
x = x + box_size
if event.key == pygame.K_LEFT:
if x > 0:
x = x - box_size
if event.key == pygame.K_UP:
if y > 0:
y = y - box_size
if event.key == pygame.K_DOWN:
if y < height - box_size:
y = y + box_size
See How to get keyboard input in pygame? and How can I make a sprite move when key is held down.
pygame.key.get_pressed() returns a sequence with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action like jumping or spawning a bullet or a step-by-step movement.
I'm creating 2-player PONG game with pygame.I have my Racquets on both sides.
One of them move with W and S and another with UP and DOWN arrow.
I'm using this code to move Racquets:
chx = 0.051
chy = 0.051
def ychangeneg():
global y
if y <= 4:
return
else:
y -= chy
return
def ychangepos():
global y
if y >= 327:
return
else:
y += chy
return
def y1changeneg():
global y1
if y1 <= 4:
return
else:
y1 -= chy
return
def y1changepos():
global y1
if y1 >= 327:
return
else:
y1 += chy
return
while True:
for event in pygame.event.get():
keyQ = pygame.key.get_pressed()
if event.type == pygame.QUIT:
system("cls")
quit()
keyboard.add_hotkey("w",lambda:ychangeneg())
keyboard.add_hotkey("s",lambda:ychangepos())
keyboard.add_hotkey("up",lambda:y1changeneg())
keyboard.add_hotkey("down",lambda:y1changepos())
chy variable changes y of Racquet and moves it.But I have these problems:
When I start holding a key,Racquet starts moving with delay and then becomes faster
When I holding 2 key (W and UP arrow) at a same time, Racquets don't move
At first, I found some codes that using key= pygame.key.get_pressed() but when you hold a key with this code, It moves but not continuously.
Do not mix the pygame.key module with the python keyboard module.
See How can I make a sprite move when key is held down and that changes the y-coordinate, depending on the keys pressed:
def move_y(y, keys, up, down):
new_y = y + (keys[down] - keys[up]) * chy
return max(4, min(327, new_y))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
system("cls")
quit()
keyQ = pygame.key.get_pressed()
y = move_y(y, keyQ, pygame.K_w, pygame.K_s)
y1 = move_y(y1, keyQ, pygame.K_UP, pygame.K_DOWN)
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 350))
clock = pygame.time.Clock()
paddle1 = pygame.Rect(10, 0, 10, 20)
paddle1.centery = window.get_rect().centery
paddle2 = pygame.Rect(380, 0, 10, 20)
paddle2.centery = window.get_rect().centery
chy = 10
def move_y(y, keys, up, down):
new_y = y + (keys[down] - keys[up]) * chy
return max(4, min(327, new_y))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keyQ = pygame.key.get_pressed()
paddle1.y = move_y(paddle1.y, keyQ, pygame.K_w, pygame.K_s)
paddle2.y = move_y(paddle2.y, keyQ, pygame.K_UP, pygame.K_DOWN)
window.fill(0)
pygame.draw.rect(window, (255, 255, 255), paddle1)
pygame.draw.rect(window, (255, 255, 255), paddle2)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
I'd suggest not using the keyboard module. Instead look, which key is pressed by using pygame.key.get_pressed(). Check if the key is in there, and if so then change the coordinates.
somewhat like this:
pressed_keys = pygame.key.get_pressed()
if pressed_keys[pygame.UP]: # this evaluates to true if the up key is pressed
ychangeneg()
if pressed_keys[pygame.DOWN]:
ychangepos()
# and so on...
I made this code to make a circle follow my mouse but the movement is really mechanic and does not give the 360 degrees of movement I desired.
Here's the code:
mx, my = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
Running = False
mpos = (mx, my)
X = (playerX)
Y = (playerY)
if mpos[1] >= Y:
playerY += vel
if mpos[1] <= Y:
playerY -= vel
if mpos[0] <= X:
playerX -= vel
if mpos[0] >= X:
playerX += vel
You need to calculate the direction vector, normalize it and multiply it with the desired speed, then apply that to the circles position.
The easiest way is to use pygame's built-in Vector2 class for this:
import pygame
def main():
pygame.init()
screen = pygame.display.set_mode((600, 600))
pos = pygame.Vector2()
clock = pygame.time.Clock()
speed = 10
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
movement = pygame.mouse.get_pos() - pos
if movement.length() > 6: # some margin so we don't flicker between two coordinates
if movement.length() > 0: # when we move, we want to move at a constant speed
movement.normalize_ip()
pos += movement * speed # change the position
screen.fill((20, 20, 20))
pygame.draw.circle(screen, 'dodgerblue', pos, 30)
pygame.display.flip()
clock.tick(30)
main()
Im creating a snake Pygame with a menu and am fine tuning the bugs and such when I come across the error
IndexError: list index out of range
the error actually appears after I open the tab itself and move the cursor over it
I have a faint idea of what it actually means but I am quite new to python and coding in general so I would appreciate it if someone could explain and show a solution,
thank you very much and here is the code
import pygame
import sys
import random
import time
pygame.init()
WHITE = (255, 255, 255)
YELLOW = (255, 255, 102)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
DARKRED = (125, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
screenWidth = 800
screenHeight = 800
screen = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption('Snake Game')
clock = pygame.time.Clock()
snakeBlock = 10
snakeSpeed = 15
fontTitle = pygame.font.SysFont("arial",100)
fontStyle = pygame.font.SysFont("ariel", 50)
scoreFont = pygame.font.SysFont("ariel", 35)
def score(score):
value = scoreFont.render(" Score: " + str(score), True, BLACK)
screen.blit(value, [50, 50])
def snake(snakeBlock, snake_list):
for x in snake_list:
pygame.draw.rect(screen, GREEN, [x[0], x[1], snakeBlock, snakeBlock])
def message(msg, colour):
msg = fontStyle.render(msg, True, BLACK)
screen.blit(msg, [screenWidth / 20, screenHeight / 2])
def gameLoop():
gameOver = False
gameEnd = False
instructions = False
game = True
intro = True
main = True
x1 = screenWidth / 2
y1 = screenHeight / 2
dx = 0
dy = 0
snakeList = []
snakeLength = 2
foodx = round(random.randrange(0, screenWidth - snakeBlock) / 10.0) * 10.0
foody = round(random.randrange(0, screenHeight - snakeBlock) / 10.0) * 10.0
def menu(titles):
buttonTitleFont = pygame.font.SysFont("arial", 52)
selection = []
rectWidth = 400
rectHeight = 60
x = int(screen.get_width()/2 - rectWidth/2)
y = 450
length = len(titles)
num = 0
hover = False
# creates the Rects (containers) for the buttons
for i in range (0,length,1):
choiceRect = pygame.Rect(x,y,rectWidth,rectHeight)
selection.append(choiceRect)
y += 100
#main loop in menu
menu = True
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
menu = False
pygame.quit()
sys.exit()
if event.type ==pygame.MOUSEMOTION: # if mouse moved
hover = False
mx, my = pygame.mouse.get_pos() # get the mouse position
for i in range (length):
if selection[i].collidepoint((mx,my)): # check if x,y of mouse is in a button
num = i
hover = True
if event.type == pygame.MOUSEBUTTONDOWN and hover == True: #if mouse is in button
menu = False # and has been clicked
# draw all buttons
for choice in selection:
pygame.draw.rect(screen,WHITE,choice,0)
# redraw selected button in another colour
pygame.draw.rect(screen,GREEN,selection[num],0)
# draw all the titles on the buttons
x = int(screen.get_width()/2 - 150)
y = 450
for i in range(0,length,1):
buttonTitle = buttonTitleFont.render(titles[i],True,BLACK)
screen.blit(buttonTitle,(x,y))
y += 100
pygame.display.update()
return num
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type ==pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
intro = False
screen.fill(BLACK)
menuMain = ["Launch", "Instructions","QUIT"]
mainMenu = True
mainInt = True
while mainInt:
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
intro = False
mainInt = False
screen.fill(BLACK)
#Centers the rendered tiles
textTitle = fontTitle.render("Snake", True, GREEN )
textW = textTitle.get_width()
textH = textTitle.get_height()
xTitle = int(screenWidth/2 - textW/2)
yTitle = int(screenHeight/4 - textH/2)
screen.blit(textTitle, (xTitle,yTitle))
pygame.display.update()
# in the intro, this asks the user where they would like to go
if mainMenu ==True:
choose = menu(menuMain)
if choose == 0:
menu = False
intro = False
mainInt = False
mainMenu = False
game = True
screen.fill(BLACK)
elif choose ==1:
menu = False
instructions = True
mainMenu = False
screen.fill(BLACK)
pygame.display.update()
else:
menu = False
main = False
intro = False
mainInt = False
mainMenu = False
while game:
if gameOver == True:
game = False
while gameEnd == True:
screen.fill(DARKRED)
message("You Lost! Press C to Play Again or Q to Quit", RED)
score(snakeLength - 1)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
gameOver = True
gameEnd = False
if event.key == pygame.K_c:
gameLoop()
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameOver = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
dx = -snakeBlock
dy = 0
elif event.key == pygame.K_RIGHT:
dx = snakeBlock
dy = 0
elif event.key == pygame.K_UP:
dx = 0
dy = -snakeBlock
elif event.key == pygame.K_DOWN:
dx = 0
dy = snakeBlock
if x1 >= screenWidth or x1 < 0 or y1 >= screenHeight or y1 < 0:
gameEnd = True
x1 += dx
y1 += dy
screen.fill(WHITE)
pygame.draw.rect(screen, RED, [foodx, foody, snakeBlock, snakeBlock])
snakeHead = []
snakeHead.append(x1)
snakeHead.append(y1)
snakeList.append(snakeHead)
if len(snakeList) > snakeLength:
del snakeList[0]
for x in snakeList[:-1]:
if x == snakeHead:
gameEnd = True
snake(snakeBlock, snakeList)
score(snakeLength - 1)
pygame.display.update()
if x1 == foodx and y1 == foody:
foodx = round(random.randrange(0, screenWidth - snakeBlock) / 10.0) * 10.0
foody = round(random.randrange(0, screenHeight - snakeBlock) / 10.0) * 10.0
snakeLength += 1
clock.tick(snakeSpeed)
pygame.quit()
quit()
gameLoop()
I see that in your code, you're referencing selection[some_index] multiple times. If you look into what selection actually is, you'll find that it's an array with one rectangle object inside of it:
[<rect(200, 450, 400, 60)>]
This rectangle never changes, so I suggest referencing it directly by calling
selection[0]
For example, here is the snippet of code that's giving you the error.
for i in range (length):
if selection[i].collidepoint((mx,my)): # check if x,y of mouse is in a button
num = i
hover = True
Because selection is an array with one element that never changes, you get an Index out of range error after the first iteration of your for loop. Perhaps this looks familiar:
IndexError: list index out of range
What you can do to fix this is get rid of the loop (because it's unnecessary - you shouldn't be looping through indices if there's only one rectangle in the selection list!) and extracting the rectangle at the 0-th index.
if selection[0].collidepoint((mx,my)): # check if x,y of mouse is in a button
num = i
hover = True
This isn't the only bug I ran into in your code, but I believe this will help you on your way!