I've been making a game where the player controls a ship and has to collect things around the screen. However, I found that the movement of the ship was unsatisfying and hard to control. To solve this, I want the ship to always be moving, where the player can change the direction of the ship using the arrow keys.
I thought I had found a solution, but c.bind_all won't call my function. I don't get any errors in the shell, and it will call other functions.
from tkinter import *
from random import randint
from time import sleep, time
# Window size
window_height = 500
window_width = 800
ship_speed = 10 # Sets how many pixels to move the ship when a key is pressed
min_bubble_r = 10 # Minimum size of each bubble
max_bubble_r = 30 # Maximum size of each bubble
max_bubble_speed = 4 # Maximum speed of each bubble
gap = 100 # How far across the screen to spawn the bubbles (higher is further right)
bubble_chance = 50 # The chance of a bubble being created
# Changes the direction of the ship depending on which key is pressed
def ship_direction(event):
global ship_direction # Takes the ship direction variable from outside the function
if event.keysym == "Up":
ship_direction = "Up"
elif event.keysym == "Down":
ship_direction = "Down"
elif event.keysym == "Right":
ship_direction = "Right"
elif event.keysym == "Left":
ship_direction = "Left"
# Creates a bubble and adds its info to the lists
def create_bubble():
# Sets the bubble position on the canvas
x = window_width + gap
y = randint(0, window_height)
r = randint(min_bubble_r, max_bubble_r) # Picks a random size for the bubble between the min and max
id1 = c.create_oval(x-r, y-r, x+r, y+r, outline="white") # Creates the bubble shape
# Adds the ID, radius and speed of the bubble to the lists
bubble_id.append(id1)
bubble_r.append(r)
bubble_speed.append(randint(1, max_bubble_speed))
# Moves the ship depending on its direction
ship_direction = str() # Creates an empty string for the ship's movement direction
def move_ship():
if ship_direction == "Up":
c.move(ship_part1, 0, -ship_speed)
c.move(ship_part2, 0, -ship_speed)
elif ship_direction == "Down":
c.move(ship_part1, 0, ship_speed)
c.move(ship_part2, 0, ship_speed)
elif ship_direction == "Right":
c.move(ship_part1, ship_speed, 0)
c.move(ship_part2, ship_speed, 0)
elif ship_direction == "Left":
c.move(ship_part1, -ship_speed, 0)
c.move(ship_part2, -ship_speed, 0)
# Goes through each existing bubble and moves them
def move_bubbles():
# Runs once for each existing bubble
for i in range(len(bubble_id)):
c.move(bubble_id[i], -bubble_speed[i], 0) # Moves the bubble depending on its speed
# Gets the co-ordinates of a bubble
def get_coordinates(id_number):
pos = c.coords(id_number) # Gets the co-ordinates
x = (pos[0] + pos[2])/2 # Works out the x co-ordinate of the middle of the bubble
y = (pos[1] + pos[3])/2 # Works out the y co-ordinate of the middle of the bubble
return x, y
window = Tk() # Creates a new window
window.title("Bubble Blaster") # Title in the top bar
c = Canvas(window, width=window_width, height=window_height, bg="#4269dd") # Creates a canvas that can be drawn on
c.pack()
ship_part1 = c.create_polygon(10, 10, 10, 50, 50, 10, 50, 50, fill="white") # Creates the centre part of the ship
ship_part2 = c.create_oval(3, 3, 57, 57, outline="white") # Creates the circle part of the ship
ship_r = 27 # Sets the ship's radius (for colisions
mid_x = window_width / 2 # Sets the page midway point on the X axis
mid_y = window_height / 2 # Sets the page midway point on the Y axis
c.move(ship_part1, mid_x-ship_r, mid_y-ship_r) # Moves part 1 of the ship to the centre of the page
c.move(ship_part2, mid_x-ship_r, mid_y-ship_r) # Moves part 2 of the ship to the centre of the page
c.bind_all("<Key>", ship_direction) # Runs the ship_direction function whenever a key is pressed
# Creates empty lists to store the ID, radius and speed of each bubble
bubble_id = list()
bubble_r = list()
bubble_speed = list()
# Main loop
while True:
if randint(1, bubble_chance) == 1:
create_bubble()
move_ship()
move_bubbles()
window.update() # Redraws the newly moved objects
sleep(0.01)
Your problem is simply that you have two global objects with the same name: a variable named ship_direction and a function named ship_direction. When you create the variable, it overwrites the function, so the function no longer exists.
If you rename your function from ship_direction to change_ship_direction (or any other name), and also change the binding, your code will work.
your problem was that you are trying to call a function that is not yet defined. So i did some replacing of your code but did not need to add a single character to it for it to work. here you go
from tkinter import *
from random import randint
from time import sleep, time
# Window size
window_height = 500
window_width = 800
ship_speed = 10 # Sets how many pixels to move the ship when a key is pressed
min_bubble_r = 10 # Minimum size of each bubble
max_bubble_r = 30 # Maximum size of each bubble
max_bubble_speed = 4 # Maximum speed of each bubble
gap = 100 # How far across the screen to spawn the bubbles (higher is further right)
bubble_chance = 50 # The chance of a bubble being created
window = Tk() # Creates a new window
window.title("Bubble Blaster") # Title in the top bar
c = Canvas(window, width=window_width, height=window_height, bg="#4269dd") # Creates a canvas that can be drawn on
c.pack()
ship_part1 = c.create_polygon(10, 10, 10, 50, 50, 10, 50, 50, fill="white") # Creates the centre part of the ship
ship_part2 = c.create_oval(3, 3, 57, 57, outline="white") # Creates the circle part of the ship
ship_r = 27 # Sets the ship's radius (for colisions
mid_x = window_width / 2 # Sets the page midway point on the X axis
mid_y = window_height / 2 # Sets the page midway point on the Y axis
c.move(ship_part1, mid_x-ship_r, mid_y-ship_r) # Moves part 1 of the ship to the centre of the page
c.move(ship_part2, mid_x-ship_r, mid_y-ship_r) # Moves part 2 of the ship to the centre of the page
# Changes the direction of the ship depending on which key is pressed
def ship_direction(event):
global ship_direction # Takes the ship direction variable from outside the function
if event.keysym == "Up":
ship_direction = "Up"
elif event.keysym == "Down":
ship_direction = "Down"
elif event.keysym == "Right":
ship_direction = "Right"
elif event.keysym == "Left":
ship_direction = "Left"
c.bind_all("<Key>", ship_direction) # Runs the ship_direction function whenever a key is pressed
# Creates a bubble and adds its info to the lists
def create_bubble():
# Sets the bubble position on the canvas
x = window_width + gap
y = randint(0, window_height)
r = randint(min_bubble_r, max_bubble_r) # Picks a random size for the bubble between the min and max
id1 = c.create_oval(x-r, y-r, x+r, y+r, outline="white") # Creates the bubble shape
# Adds the ID, radius and speed of the bubble to the lists
bubble_id.append(id1)
bubble_r.append(r)
bubble_speed.append(randint(1, max_bubble_speed))
# Moves the ship depending on its direction
ship_direction = str() # Creates an empty string for the ship's movement direction
def move_ship():
if ship_direction == "Up":
c.move(ship_part1, 0, -ship_speed)
c.move(ship_part2, 0, -ship_speed)
elif ship_direction == "Down":
c.move(ship_part1, 0, ship_speed)
c.move(ship_part2, 0, ship_speed)
elif ship_direction == "Right":
c.move(ship_part1, ship_speed, 0)
c.move(ship_part2, ship_speed, 0)
elif ship_direction == "Left":
c.move(ship_part1, -ship_speed, 0)
c.move(ship_part2, -ship_speed, 0)
# Goes through each existing bubble and moves them
def move_bubbles():
# Runs once for each existing bubble
for i in range(len(bubble_id)):
c.move(bubble_id[i], -bubble_speed[i], 0) # Moves the bubble depending on its speed
# Gets the co-ordinates of a bubble
def get_coordinates(id_number):
pos = c.coords(id_number) # Gets the co-ordinates
x = (pos[0] + pos[2])/2 # Works out the x co-ordinate of the middle of the bubble
y = (pos[1] + pos[3])/2 # Works out the y co-ordinate of the middle of the bubble
return x, y
# Creates empty lists to store the ID, radius and speed of each bubble
bubble_id = list()
bubble_r = list()
bubble_speed = list()
# Main loop
while True:
if randint(1, bubble_chance) == 1:
create_bubble()
move_ship()
move_bubbles()
window.update() # Redraws the newly moved objects
sleep(0.01)
What i did was moving the code that creates the window to the top so the window will be created before anything else is called, then i moved the canvas.bind_all function bellow your function that it is supposed to call
edit
Why is this answer downvoted as it provides the correct code and localises the fault in the code?
Related
This question already has answers here:
Problems with moving an enemy towards a character in pygame
(1 answer)
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 8 months ago.
So I have a functioning shooting mechanic in python pygame for a top down shooter, where I am using the mouse position to aim the bullets by working out the angles, however when I do this, the bullets are shooting slightly off where the mouse position is. for instance: the mouse would be where the red arrow is drawn and the bullets will be shooting by a small amount in the wrong direction
Any help would be appreciated
code below:
main.py:
#-------------Imports-------------#
import pygame,sys
#import globals
from background import*
from player import*
#-------------Constants-------------#
WIDTH,HEIGHT = 500,500
WINDOW = pygame.display.set_mode((WIDTH,HEIGHT))
CLOCK = pygame.time.Clock()
BLACK = (0, 0, 0)
#-------------Instances-------------#
bg = Background()
player = Player()
#-------------Functions-------------#
def draw():
WINDOW.fill(BLACK)
bg.update(WINDOW)
player.update(WINDOW)
pygame.display.update()
#-------------Main Game Loop-------------#
def main():
#globals.intialise()
while 1:
CLOCK.tick(1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
draw()
#globals.game_ticks += 1
if __name__ == "__main__":
main()
player.py
#-------------Imports-------------#
import pygame,math
#import globals
#-------------Constants-------------#
WIDTH,HEIGHT = 500,500
PLAYER_COLOUR = (255, 212, 112)
BLACK = (0,0,0)
PI = 3.14159265359
#-------------Classes-------------#
class Bullet:
def __init__(self,origin,angle):
self.speed = 20
self.x_speed,self.y_speed = self.speed*math.cos(math.radians(angle)),self.speed*math.sin(math.radians(angle))
self.rect = pygame.Rect(origin[0],origin[1],5,5)
def __del__(self):
pass
def update(self,window):
# move bullet
self.rect.x += self.x_speed
self.rect.y += self.y_speed
# draw bullet
pygame.draw.rect(window,BLACK,self.rect)
# check if bullet is out of the screen
if self.rect.x > WIDTH or self.rect.x < 0:
return -1
elif self.rect.y > HEIGHT or self.rect.y < 0:
return -1
class Player:
def __init__(self):
self.sprite = pygame.transform.scale(pygame.image.load("sprites/temp_player.png"),(50,50))
self.rect = pygame.Rect(250,250,50,50)
self.center = (self.rect.x,self.rect.y)
self.bullets = []
self.fire_rate = 12
def shoot(self,angle,window):
# update all bullets and delete if bullet is out of screen
for bullet in self.bullets:
if bullet.update(window) == -1:
self.bullets.remove(bullet)
del bullet
# instantiate bullet if mouse button pressed
#if pygame.mouse.get_pressed()[0] and globals.game_ticks % self.fire_rate == 0:
if pygame.mouse.get_pressed()[0]:
self.bullets.append(Bullet(self.rect.center,-angle))
def update(self,window):
mx,my = pygame.mouse.get_pos()
# find distance between mouse position and player position
diff_x,diff_y = mx-self.rect.x,my-self.rect.y
# word out angle between mouse and player
angle_rad = math.atan2(diff_y,diff_x)
angle = -360*angle_rad/(2*PI)
# adjust angle according to where we want to rotate the player
# when angle is bottom left
if abs(angle) > 90 and angle < 0:
a = 270-abs(angle)
# when angle is top left
elif abs(angle) > 90:
a = angle-90
# when angle is to the right
else:
a = angle - 90
# create new sprite that is rotated
rotated_image = pygame.transform.rotate(self.sprite,a)
# replace current rectangle with rotated sprite
self.rect = rotated_image.get_rect(center = self.center)
self.shoot(angle,window)
# add image to the screen
#window.blit(pygame.transform.rotate(self.sprite,a),self.rect)
background.py:
#-------------Imports-------------#
import pygame,random,ast,time,globals
#-------------Constants-------------#
WIDTH,HEIGHT = 500,500
TILE_DIMENSION = 9
TILE_SIZE = int(round(WIDTH/TILE_DIMENSION,0))
TO_EDGE = int((TILE_DIMENSION+1)/2)
#-------------Classes-------------#
class Background:
def __init__(self):
self.tiles = self.generate_screen()
self.centre = [2,2]
self.right = 0
self.up = 0
self.speed = 5
def generate_screen(self):
# generate original chunk of tiles
tiles = [[random.randint(100,200) for i in range(TILE_DIMENSION)] for j in range(TILE_DIMENSION)]
# eventually use image instead of random RGB value
return tiles
def movement(self,tile_rects):
keys = pygame.key.get_pressed()
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
# if player is on tile to the left of centre
if (self.right - self.speed) < -TILE_SIZE:
# reset movement and adjust centre
self.right = 0
self.centre[0] -= 1
else:
# add to movement if not on next tile
self.right -= self.speed
# move all rectangles in background to simulate player moving
for i in range(len(tile_rects)):
for j in range(len(tile_rects[0])):
tile_rects[i][j].x += self.speed
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
# if player is on tile to the right of centre
if (self.right + self.speed) > TILE_SIZE:
# reset movement and adjust centre
self.right = 0
self.centre[0] += 1
else:
# add to movement if not on next tile
self.right += self.speed
# move all rectangles in background to simulate player moving
for i in range(len(tile_rects)):
for j in range(len(tile_rects[0])):
tile_rects[i][j].x -= self.speed
if keys[pygame.K_w] or keys[pygame.K_UP]:
# if player is on tile above the centre
if (self.up + self.speed) > TILE_SIZE:
# reset movement and adjust centre
self.up = 0
self.centre[1] -= 1
else:
# add to movement if not on next tile
self.up += self.speed
# move all rectangles in background to simulate player moving
for i in range(len(tile_rects)):
for j in range(len(tile_rects[0])):
tile_rects[i][j].y += self.speed
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
# if player is on tile below the centre
if (self.up - self.speed) < -TILE_SIZE:
# reset movement and adjust centre
self.up = 0
self.centre[1] += 1
else:
# add to movement if not on next tile
self.up -= self.speed
# move all rectangles in background to simulate player moving
for i in range(len(tile_rects)):
for j in range(len(tile_rects[0])):
tile_rects[i][j].y -= self.speed
return tile_rects
def update(self,window):
# rendering in brand new map chunks
# if part of the chunk trying to be rendered in is non-existant in the 2D map array to the left
if self.centre[0]-TO_EDGE < 0:
# check how many tiles it is offset by
for i in range(0-(self.centre[0]-TO_EDGE)):
# add new column of values at the beginning of the 2D array
for i in range(len(self.tiles)):
self.tiles[i].insert(0,random.randint(120,230))
# due to whole array being shifted to the right, adjust the centre accordingly
self.centre[0] += 1
# if part of the chunk trying to be rendered is non-existant in the 2D map array to the right
if self.centre[0]+TO_EDGE >= len(self.tiles[0]):
# check how many tiles it is offset by
for i in range((self.centre[0]+TO_EDGE)-(len(self.tiles[0])-1)):
# add a new column of values at the end of the 2D array
for i in range(len(self.tiles)):
self.tiles[i].append(random.randint(120,230))
# if part of the chunk trying to be rendered in is non-existant in the 2D array at the top
if self.centre[1]-TO_EDGE < 0:
# check how many tiles it is offset by
for i in range(0-(self.centre[1]-TO_EDGE)):
# add a new row at the top of the 2D array
self.tiles.insert(0,[random.randint(120,230) for i in range(len(self.tiles[0]))])
# due to whole array shifting downwards, adjust the centre accordingly
self.centre[1] += 1
# if part of the chunk trying to be rendered in is non-existant in the 2D array at the bottom
if self.centre[1]+TO_EDGE >= len(self.tiles):
# check how many tiles it is offset by
for i in range((self.centre[1]+TO_EDGE)-(len(self.tiles)-1)):
# add a new row at the bottom of the 2D array
self.tiles.append([random.randint(120,230) for i in range(len(self.tiles[0]))])
# determining which tiles should be rendered in according to the centre(where player would be)
t = []
for i in range(TILE_DIMENSION+2):
t.append([])
for j in range(TILE_DIMENSION+2):
try:
t[i].append(self.tiles[i+(self.centre[1]-TO_EDGE)][j+(self.centre[0]-TO_EDGE)])
except:
pass
# create a rectangle for each tile that is rendered in
tile_rects = [[pygame.Rect((i-1)*TILE_SIZE-self.right,(j-1)*TILE_SIZE+self.up,TILE_SIZE,TILE_SIZE) for i in range(TILE_DIMENSION+2)] for j in range(TILE_DIMENSION+2)]
tile_rects = self.movement(tile_rects)
# draw all rectangles
for i in range(TILE_DIMENSION+2):
for j in range(TILE_DIMENSION+2):
try:
pygame.draw.rect(window,(0,int(t[i][j]),0),tile_rects[i][j])
except:
pass
the background script doesnt affect anything, its just there as a background to make it easier to see, and you may have to make your own temp_player.png image to make it compatible
I am making a one man pong game and I got everything except for moving the paddle up and down and I don't really understand how to do it can someone help. Is it something to do with the "pos" or is it something to do it with the syntax of the lines. The part where it controls the movements of the paddles is def move paddle
import tkinter
# steps
# ball diagonal
# paddle draw
# paddle animation with keyboard (right/left) -> challenge up/down
# collisions (if time)
# graphic parameters
canvas_width = 400
canvas_height = 500
ball_size = 30
timer_refresh = 20
paddle_width = 100
paddle_height = 20
# ball movement
y_move = 2
x_move = 2
# paddle movement
paddle_speed = 6
# game_state
game_running = True
def end_game():
global game_running
game_running = False
canvas.create_text(canvas_width/2, canvas_height/2, text="you lost!")
# move paddle when key is pressed
def move_paddle(event):
key_symbol = event.keysym
print(key_symbol)
pos = canvas.coords(paddle)
left = pos[0]
right = pos[2]
up = pos[1]
down = pos[3]
if key_symbol == "Left" and left > 0:
canvas.move(paddle, -paddle_speed, 0)
elif key_symbol == "Right" and right <= canvas_width:
canvas.move(paddle, paddle_speed, 0)
# move paddle up
elif key_symbol == "Up" and up >= 0:
canvas.move(paddle, paddle_speed, 0)
# move paddle down
elif key_symbol == "Down" and down <= canvas_width:
canvas.move(paddle, paddle_speed, 0)*
def collision(ball_pos):
overlap_result = canvas.find_overlapping(ball_pos[0],ball_pos[1],ball_pos[2],ball_pos[3])
if paddle in overlap_result:
return True;
else:
return False;
# draw/move ball
def draw():
global y_move, x_move
canvas.move(ball1, x_move, y_move)
pos = canvas.coords(ball1)
top_y = pos[1]
bottom_y = pos[3]
left = pos[0]
right = pos[2]
if top_y <= 0:
y_move = -y_move
elif bottom_y >= canvas_height-5:
y_move = -y_move
end_game()
# did I hit left or right wall?
elif left <= 0 or right >= canvas_width-5:
x_move = -x_move
# did I collide with the paddle? if so bounce vertically
if collision(pos):
y_move = -y_move
# animation timer
def master_timer():
# draw/move ball
draw()
# tkinter processing
tk.update_idletasks()
tk.update()
if game_running:
tk.after(timer_refresh, master_timer)
tk = tkinter.Tk()
tk.title("Simplified Pong")
# block resizing window
tk.resizable(0,0)
# drawing the canvasd
canvas = tkinter.Canvas(tk, width=canvas_width, height=canvas_height, bd=0, highlightthickness=0)
canvas.pack()
ball1 = canvas.create_oval(0, 0, ball_size, ball_size, fill="red")
canvas.move(ball1, canvas_width/2, canvas_height/2)
paddle = canvas.create_rectangle(0,0,paddle_width, paddle_height, fill="black")
canvas.move(paddle, canvas_width/2, canvas_height/1.2)
canvas.bind_all("<KeyPress-Right>", move_paddle)
canvas.bind_all("<KeyPress-Left>", move_paddle)
canvas.bind_all("<KeyPress-Up>", move_paddle)
canvas.bind_all("<KeyPress-Down>", move_paddle)
master_timer()
tk.mainloop()
The problem is quite simple if you refer to the docs of move method, for tkinter, or have an understanding of how to use it. From the docs:
.move(tagOrId, xAmount, yAmount)
Moves the items specified by tagOrId by adding xAmount to their x coordinates and yAmount to their y coordinates.
So if you notice, it takes the x coords first and then the y coords. So when you want to translate(move) upon the y axis(up and down), you want to alter the y axis argument, but instead you are doing it for the x axis. So you have to pass the variable onto the correct parameter only.
elif key_symbol == "Up" and up >= 0:
canvas.move(paddle, 0, -paddle_speed)
# move paddle down
elif key_symbol == "Down" and down <= canvas_width:
canvas.move(paddle, 0, paddle_speed)
Also note that you can get rid of all the key binds and just keep a single general bind, because inside your function, you are already getting the key that is pressed dynamically, so it doesn't make much sense to bind to all possible keys:
canvas.bind_all("<KeyPress>", move_paddle) # Instead of Up, Down, Left and Right
Also another tip is, you can take advantage of * to pass the contents of the iterable as the argument to the function:
canvas.find_overlapping(*ball_pos) # Instead of ball_pos[0] and so on
from tkinter import *
window = Tk()
canvas = Canvas(window, width=1280, height=720)
canvas.pack()
a = (0, 50), (50, 100) # coordinates of the rectangle
rect = canvas.create_rectangle(a, fill="red")
#rect = canvas.create_oval(a, fill="red")
speed = 5 # speed of the rectangle
jump = False
def keypress(event):
x = 0
y = 0
if event.char == "a": x-= speed
elif event.char == "d": x+= speed
elif event.char == "w": y-= speed
elif event.char == "s": y+= speed
elif event.char == " ":
y = 50
jump = True
while jump:
y = 0
jump = False
canvas.move(rect, x, y)
canvas.update()
canvas.after(1)
window.bind("<Key>", keypress)
window.mainloop()
This is my code, im trying to make the rectangle jump but whenever i add the jump code, everything stops working even the normal "asdw" movement that would work otherwise
In case you just want to do a simple jumping animation where the rectangle goes up and down again, just use physics: Define a gravitational pull and an initial vertical velocity and then loop until the ground is reached again:
elif event.char == " ":
diff = 0 ## Difference to initial level
y = -3 ## Initial speed in y direction
grav = .1 ## Gravitation
while diff >= 0: ## While it is still jumping (higher than initially)
canvas.move(rect, x, y)
canvas.update()
sleep(.01) ## Pause for 1/100 second
diff-=y ## Update current jumping height
y+=grav ## Update the speed in y direction
y = 0 ## Just so it is not moved again, afterwards
To make this code snippet work, you have to import time.sleep at the beginning of the program:
from time import sleep
I created this simple snake game a while ago and I wanted to try running it and for some reason the window is not starting on my machine. I am sure the code was working before. I tried debugging for a while but can't seem to figure out why I am stuck on a black screen. It seems to detect a key pressed but nothing is displayed on the screen.
import pygame
from pygame.locals import *
import random
# Global Color Variables
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
CYAN = (0,255, 255)
# Set the speed of the Snake --> lower = faster
timeDelaySpeed = 0
class App:
def __init__(self):
self._running = True
self._display_surf = None
self.size = self.weight, self.height = 600, 700
# create the boarder
self.boarder = self.generateBoard()
# Initial Snake array with 3 Snake Blocks starting at (50, 50) and going left
self.snake = [Snake(WHITE, 10, 10, 150, 260), Snake(WHITE, 10, 10, 140, 260), Snake(WHITE, 10, 10, 130, 260)]
def on_init(self):
pygame.init()
self._display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
self._display_surf.fill(BLACK)
self._running = True
# Create Score Board
self.score = 0
self.displayScore(self.score, 45)
# Create Initial Food
self.initFood = Food(RED, 10, 10)
self._display_surf.blit(self.initFood.image, self.initFood.rect)
# display the initial Snake array
for i in range(len(self.snake)):
self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
# display the board
for i in range(len(self.boarder)):
self._display_surf.blit(self.boarder[i].image, self.boarder[i].rect)
pygame.display.update()
"""
Helper Method that will run the events that are clicked on by the user
"""
def on_event(self):
# Checks if Snake crashes with itself - LOSE
for i in range(1, len(self.snake)):
if pygame.sprite.collide_rect(self.snake[0], self.snake[1]):
self.spaceToRestartText(20)
self.gameRestart()
if pygame.sprite.collide_rect(self.snake[0], self.snake[i]):
self.spaceToRestartText(20)
self.gameRestart()
# Check if Snake hits the boarder - LOSE
for i in range(len(self.boarder)):
if pygame.sprite.collide_rect(self.snake[0], self.boarder[i]):
self.spaceToRestartText(20)
self.gameRestart()
# Checks if Snake eats Food
if pygame.sprite.collide_rect(self.snake[0], self.initFood):
self.eatFood()
# set the direction based of key that is pressed
for event in pygame.event.get():
if event.type == pygame.QUIT:
self._running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
# check for self collision before eating any food - weird collision method error fix
if self.move == 'right':
self.spaceToRestartText(20)
self.gameRestart()
break
self.move = 'left'
if event.key == pygame.K_RIGHT:
# check for self collision before eating any food
if self.move == 'left':
self.spaceToRestartText(20)
self.gameRestart()
break
self.move = 'right'
if event.key == pygame.K_UP:
# check for self collision before eating any food
if self.move == 'down':
self.spaceToRestartText(20)
self.gameRestart()
break
self.move = 'up'
if event.key == pygame.K_DOWN:
# check for self collision before eating any food
if self.move == 'up':
self.spaceToRestartText(20)
self.gameRestart()
break
self.move = 'down'
# if stored current direction is right
if self.move == 'right':
print("RIGHT")
# Reset the Board
self.boardReset()
# Store the current head of the snake
snakeHead = self.snake[0]
# remove the last block of the snake
self.snake.pop()
# create a new head for the snake that is shifted toward the right
newHead = snakeHead.moveRight()
# add the newly created head to the front of the list - make head
self.snake.insert(0, newHead)
# displays moved snake
for i in range(len(self.snake)):
self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
pygame.display.update()
pygame.time.delay(timeDelaySpeed)
# if stored current direction is left
if self.move == 'left':
print("LEFT")
# Reset the Board
self.boardReset()
# Store the current head of the snake
snakeHead = self.snake[0]
# remove the last block of the snake
self.snake.pop()
# create a new head for the snake that is shifted toward the right
newHead = snakeHead.moveLeft()
# add the newly created head to the front of the list - make head
self.snake.insert(0, newHead)
# displays moved snake
for i in range(len(self.snake)):
self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
pygame.display.update()
pygame.time.delay(timeDelaySpeed)
# if stored current direction is up
if self.move == 'up':
print("UP")
# Reset the Board
self.boardReset()
# Store the current head of the snake
snakeHead = self.snake[0]
# remove the last block of the snake
self.snake.pop()
# create a new head for the snake that is shifted toward the right
newHead = snakeHead.moveUp()
# add the newly created head to the front of the list - make head
self.snake.insert(0, newHead)
# displays moved snake
for i in range(len(self.snake)):
self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
pygame.display.update()
pygame.time.delay(timeDelaySpeed)
# if stored current direction is down
if self.move == 'down':
print("DOWN")
# Reset the Board
self.boardReset()
# Store the current head of the snake
snakeHead = self.snake[0]
# remove the last block of the snake
self.snake.pop()
# create a new head for the snake that is shifted toward the right
newHead = snakeHead.moveDown()
# add the newly created head to the front of the list - make head
self.snake.insert(0, newHead)
# displays moved snake
for i in range(len(self.snake)):
self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
pygame.display.update()
pygame.time.delay(timeDelaySpeed)
"""
Helper method that displays the current score on the screen.
"""
def displayScore(self, score, size):
font = pygame.font.SysFont("Comic Sans MS", size)
ScoreBoard = font.render("SCORE: {}".format(score), False, (WHITE))
self._display_surf.blit(ScoreBoard, [90, 100])
pygame.display.update()
"""
Helper method that will reset the screen:
Make screen Black
Add the current Food block
Add the current Score
"""
def boardReset(self):
# Erases the current screen
self._display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
self._display_surf.fill(BLACK)
# Create Score Board
self.displayScore(self.score, 45)
# Add Food
self._display_surf.blit(self.initFood.image, self.initFood.rect)
# Add Boarder
for i in range(len(self.boarder)):
self._display_surf.blit(self.boarder[i].image, self.boarder[i].rect)
"""
Eating food helper method
"""
def eatFood(self):
# Create a new Food at random location and display it
self.initFood = Food(RED, 10, 10)
self._display_surf.blit(self.initFood.image, self.initFood.rect)
# Create Score Board
self.score += 1
self.displayScore(self.score, 45)
# for i in range(len(self.snake)):
# self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
#
# Store the last and second to last blocks of the snake
lastSnakeBlock = self.snake[-1]
secondToLastBlock = self.snake[-2]
# if the last two blocks are on the same horizontal line and the last block is to the left of the
# second to last block, add a block to the left side of the last block
if lastSnakeBlock.rect.y == secondToLastBlock.rect.y and lastSnakeBlock.rect.x < secondToLastBlock.rect.x:
newX = lastSnakeBlock.rect.x - 10
newSnakeBlock = Snake(lastSnakeBlock.color, lastSnakeBlock.width, lastSnakeBlock.height, newX,
lastSnakeBlock.rect.y)
self.snake.append(newSnakeBlock)
# if the last two blocks are on the same horizontal line and the last block is to the right of the
# second to last block, add a block to the right side of the last block
if lastSnakeBlock.rect.y == secondToLastBlock.rect.y and lastSnakeBlock.rect.x > secondToLastBlock.rect.x:
newX = lastSnakeBlock.rect.x + 10
newSnakeBlock = Snake(lastSnakeBlock.color, lastSnakeBlock.width, lastSnakeBlock.height, newX,
lastSnakeBlock.rect.y)
self.snake.append(newSnakeBlock)
# if the last two blocks are on the same vertical line and the last block is above the
# second to last block, add a block above the last block
if lastSnakeBlock.rect.x == secondToLastBlock.rect.x and lastSnakeBlock.rect.y < secondToLastBlock.rect.y:
newY = lastSnakeBlock.rect.y - 10
newSnakeBlock = Snake(lastSnakeBlock.color, lastSnakeBlock.width, lastSnakeBlock.height,
lastSnakeBlock.rect.x, newY)
self.snake.append(newSnakeBlock)
# if the last two blocks are on the same vertical line and the last block is below the
# second to last block, add a block below the last block
if lastSnakeBlock.rect.x == secondToLastBlock.rect.x and lastSnakeBlock.rect.y > secondToLastBlock.rect.y:
newY = lastSnakeBlock.rect.y + 10
newSnakeBlock = Snake(lastSnakeBlock.color, lastSnakeBlock.width, lastSnakeBlock.height,
lastSnakeBlock.rect.x, newY)
self.snake.append(newSnakeBlock)
for i in range(len(self.snake)):
self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
"""
Takes the player back to initial start state
"""
def gameRestart(self):
# Erase the Board
self._display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
self._display_surf.fill(BLACK)
self._running = True
# Recreate the Snake
self.snake = [Snake(WHITE, 10, 10, 150, 260), Snake(WHITE, 10, 10, 140, 260), Snake(WHITE, 10, 10, 130, 260)]
# Create Score Board
self.score = 0
self.displayScore(self.score, 45)
# Create Initial Food
self.initFood = Food(RED, 10, 10)
self._display_surf.blit(self.initFood.image, self.initFood.rect)
# set current move to nothing
self.move = ''
# draw in the boarder
for i in range(len(self.boarder)):
self._display_surf.blit(self.boarder[i].image, self.boarder[i].rect)
# display the initial Snake array
for i in range(len(self.snake)):
self._display_surf.blit(self.snake[i].image, self.snake[i].rect)
pygame.display.update()
"""
Creates a List of Blocks that outline the Boarder of the snake game
"""
def generateBoard(self):
boardCorners = []
boardTop = []
boardSide1 = []
boardSide2 = []
boardBottom = []
# Makes (0,0) of board = (100, 210)
# top left corner
boardCorners.append(Snake(CYAN, 10, 10, 90, 200))
# top right corner
boardCorners.append(Snake(CYAN, 10, 10, 500, 200))
# bottom left corner
boardCorners.append(Snake(CYAN, 10, 10, 90, 610))
# bottom right corner
boardCorners.append(Snake(CYAN, 10, 10, 500, 610))
# top and bottom sides
topCoord = 100
for i in range(40):
boardTop.append(Snake(CYAN, 10, 10, topCoord, 200))
boardBottom.append(Snake(CYAN, 10, 10, topCoord, 610))
topCoord += 10
# sides of board
sideCoord = 210
for i in range(40):
boardSide1.append(Snake(CYAN, 10, 10, 90, sideCoord))
boardSide2.append(Snake(CYAN, 10, 10, 500, sideCoord))
sideCoord += 10
# combine all parts
allBoarder = boardCorners + boardTop + boardSide1 + boardSide2 + boardBottom
# return list of blocks
return allBoarder
"""
Allows player to restart a game by pressing space bar - displays losing screen
"""
def spaceToRestartText(self, size):
self._display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
self._display_surf.fill(BLACK)
self.youLoseText(50)
self.yourScoreText(25)
font = pygame.font.SysFont("Comic Sans MS", size)
text_surface = font.render("Press space bar to play again", True, WHITE)
text_rect = text_surface.get_rect(center=(self.weight / 2, self.height / 2))
self._display_surf.blit(text_surface, text_rect)
pygame.display.flip()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
done = True
"""
Helper function that prints 'YOU LOSE!'
"""
def youLoseText(self, size):
font = pygame.font.SysFont("Comic Sans MS", size)
text_surface = font.render("YOU LOSE!", True, WHITE)
# Shift height up so no collision with space bar text
text_rect = text_surface.get_rect(center=(self.weight / 2, (self.height / 2) - 75))
self._display_surf.blit(text_surface, text_rect)
pygame.display.flip()
"""
Helper function that prints your score at loss
"""
def yourScoreText(self, size):
font = pygame.font.SysFont("Comic Sans MS", size)
text_surface = font.render("Your Score was: " + str(self.score), True, WHITE)
# Shift height up so no collision with space bar text
text_rect = text_surface.get_rect(center=(self.weight / 2, (self.height / 2) - 35))
self._display_surf.blit(text_surface, text_rect)
pygame.display.flip()
def on_loop(self):
pass
def on_render(self):
pass
def on_cleanup(self):
pygame.quit()
"""
Game Loop
"""
def on_execute(self):
if self.on_init() == False:
self._running = False
self.move = ''
while (self._running):
self.on_event()
self.on_loop()
self.on_render()
self.on_cleanup()
"""
Class to create a Food at a random coordinate
"""
class Food(pygame.sprite.Sprite):
def __init__(self, color, width, height):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image of the block, 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(color)
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
# set the position of the Food
# TODO: change values in randint to use the input width and height
randX = random.randint(10,49) * 10
randY = random.randint(21,60) * 10
self.rect.x = randX
self.rect.y = randY
class Snake(pygame.sprite.Sprite):
# TODO: Make Snake a chain of Blocks
def __init__(self, color, width, height, positionX, positionY):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image of the block, 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(color)
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
# set the position of the snake
self.rect.x = positionX
self.rect.y = positionY
# set the inputs the usable variables for later code
self.color = color
self.width = width
self.height = height
"""
Method the will change the direction of the Snake towards the left
"""
def moveLeft(self):
newX = self.rect.x - 10
return Snake(self.color, self.width, self.height, newX, self.rect.y)
"""
Method that will change the direction of the Snake toward the right
"""
def moveRight(self):
newX = self.rect.x + 10
return Snake(self.color, self.width, self.height, newX, self.rect.y)
"""
Method that will change the direction of the Snake to go upward
"""
def moveUp(self):
newY = self.rect.y - 10
return Snake(self.color, self.width, self.height, self.rect.x, newY)
"""
Method that will change the direction of the Snake to go downward
"""
def moveDown(self):
newY = self.rect.y + 10
return Snake(self.color, self.width, self.height, self.rect.x, newY)
if __name__ == "__main__":
theApp = App()
theApp.on_execute()
I can agree with the answers above. The only problem appears to be that you did not set the FPS i.e. clock.tick(fps) and the game is too fast so it is unplayable. 30 and under will do in my opinion. Please provide more details.
Code is okay, worked on my machine.
Can u add a screenshot of your command prompt?
I have an assignment that asks me to do the following:
Use Google's advanced image search to find a reasonably-sized image of a ball that is free to reuse and that includes transparency. Modify the sample code so that your ball slides back and forth across the bottom of the screen. It should take 2 seconds for the ball to go from the left side to the right.
Improve your animation for question 5 so that the ball rotates, accurately, as if it were rolling back and forth.
Modify your animation for question 6 so that the ball travels counterclockwise around the edge of the screen
I am at the last part. Trying to modify the animation for question 6 to do this: (1:24)
http://www.youtube.com/watch?v=CEiLc_UFNLI&feature=c4-overview&list=UUpbgjjXBL3hdTKDZ0gZvdWg
I'm stumped pretty bad. I just can't seem to understand how I will get the ball to slowly move from one point to another. The ball is an image. This is what I have so far, but it doesn't work.
"""Some simple skeleton code for a pygame game/animation
This skeleton sets up a basic 800x600 window, an event loop, and a
redraw timer to redraw at 30 frames per second.
"""
from __future__ import division
import math
import sys
import pygame
class MyGame(object):
def __init__(self):
"""Initialize a new game"""
pygame.mixer.init()
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.init()
# set up a 640 x 480 window
self.width = 800
self.height = 600
self.img = pygame.image.load('ball.png')
self.screen = pygame.display.set_mode((self.width, self.height))
self.x = 0
self.y = 0
self.angle = 0
self.rotate_right=True
self.first = True
#0: Move bottomleft to bottomright 1: Move from bottomright to topright 2:Move from topright to topleft 3:Move from topleft to bottomleft
self.mode = 0
# use a black background
self.bg_color = 0, 0, 0
# Setup a timer to refresh the display FPS times per second
self.FPS = 30
self.REFRESH = pygame.USEREVENT+1
pygame.time.set_timer(self.REFRESH, 1000//self.FPS)
def get_mode(self):
rect = self.img.get_rect()
if self.first == True:
self.first = False
return
if (self.x, self.y) == (0, self.height - rect.height):
#Our starting point, bottom left
self.mode = 0
elif (self.x, self.y) == (self.width-rect.width, self.height-rect.height):
#Bottom right
self.mode = 1
elif (self.x, self.y) == (self.width-rect.width, 0):
#Top Right
self.mode = 2
elif (self.x, self.y) == (0,0):
#Top Left
self.mode = 3
def get_target(self):
rect = self.img.get_rect()
if self.mode == 0:
targetPosition = (0, self.height - rect.height)
elif self.mode == 1:
targetPosition = (self.width-rect.width, self.height-rect.height)
elif self.mode == 2:
targetPosition = (self.width-rect.width, 0)
elif self.mode == 3:
targetPosition = (0,0)
return targetPosition
def get_angle(self):
if self.angle == 360:
self.rotate_right = False
elif self.angle == 0:
self.rotate_right = True
if self.rotate_right == True:
self.angle+=12
else:
self.angle-=12
def run(self):
"""Loop forever processing events"""
running = True
while running:
event = pygame.event.wait()
# player is asking to quit
if event.type == pygame.QUIT:
running = False
# time to draw a new frame
elif event.type == self.REFRESH:
self.draw()
else:
pass # an event type we don't handle
def draw(self):
"""Update the display"""
# everything we draw now is to a buffer that is not displayed
self.screen.fill(self.bg_color)
#Draw img
rect = self.img.get_rect()
#Note: this can be made dynamic, but right now since this is typically a poor structure, we will use static values.
#80 is the padding, so it hits right before.
#0,0 : top left
#self.width-rect.width, 0 : top right
#0, self.height-rect.height : bottom left
#self.width-rect.width, self.height-rect.height : bottom right
targetPosition = ()
#img = pygame.transform.rotate(self.img, self.angle)
img = self.img
self.get_angle()
self.get_mode()
targetPosition = self.get_target()
print targetPosition
print self.x, self.y
if self.x < targetPosition[0]:
self.x+= targetPosition[0]-self.x//self.FPS
elif self.x > targetPosition[0]:
self.x-= targetPosition[0]+self.x//self.FPS
if self.y < targetPosition[1]:
print "s"
self.y+= targetPosition[1]-self.y//self.FPS
elif self.y > targetPosition[1]:
self.y-= targetPosition[1]+self.y//self.FPS
rect = rect.move(self.x, self.y)
self.screen.blit(img, rect)
# flip buffers so that everything we have drawn gets displayed
pygame.display.flip()
MyGame().run()
pygame.quit()
sys.exit()
What's happening is that your ball is starting at (0,0) (top left) with a target of (0,550) (bottom left), discovers that it's at a lower y than its target, and promptly proceeds to increment its position by
targetPosition[1] - (self.y // self.FPS)
which is of course equal to 550, so it immediately snaps to the bottom of the screen.
Then during the next draw loop, get_mode() comes along and says 'okay, I'm at (0, 550), so I'll go ahead and set the mode to 0'. Then get_target() comes along and says 'okay, I'm in mode 0, let's go over to (0, 550).
And then this happens again during the next draw loop, and the next, and the next ... So of course your ball doesn't go anywhere.
You'll need to do a couple of things to fix your example:
Fix your target positions in get_target(). Right now they're targeting the same points where the transitions that trigger those modes happen, so your ball won't go anywhere.
Consider your velocity statements more carefully: right now they'll behave somewhat strangely. One way to do this properly is to determine (dx, dy) - that is, the absolute vector from you to your destination - and then normalize this vector such that it points in the same direction but has a magnitude equal to your desired speed. This approach will work for any target position you want.
To elaborate on the second point:
Suppose we're at (x, y) and we're trying to get to (target_x, target_y).
Let dx = target_x - x, dy = target_y - y. This should be uncontroversial: we're just taking the difference.
Then we remember the Pythagorean theorem: given a right triangle with sides a, b, c and hypotenuse c, we recall that len(c)**2 == len(a)**2 + len(b)**2. It's the same thing with vectors: the length of a vector (x, y) is the hypotenuse of a right triangle with side lengths x and y. You can draw this on a piece of paper if you want to prove this to yourself.
Given that, we can find the length of (dx, dy): it's just L(dx, dy) = sqrt(dx*dx + dy*dy). This lends itself to a curious observation: if we multiply both dx and dy by a scalar k, we also multiply the length by k, since sqrt(dx*k*dx*k + dy*k*dy*k) == sqrt(k*k*(dx*dx + dy*dy)) == k*sqrt(dx*dx + dy*dy).
It follows that we can find a vector parallel to (dx, dy), but of length 1, by dividing both dx and dy by L(dx, dy). Precompute L to avoid some potential issues. Multiply this new vector by whatever you want your speed to be: this is your desired velocity.