Related
import turtle
player = input('Pick square or arrow: ')
if player == "arrow":
turtle.shape("arrow")
turtle.pu()
turtle.goto(-400, 20)
elif player == "square":
turtle.shape("square")
turtle.pu()
turtle.goto(-400, 20)
else:
print ('You did not type square or arrow, so you have been given default.')
turtle.pu()
turtle.goto(-400, 20)
def up():
turtle.forward(45)
def right():
turtle.right(90)
def left():
turtle.left(90)
def down():
turtle.backward(45)
sc=turtle.Screen()
sc.setup(600, 600)
turtle.onkey(up,'w')
turtle.onkey(right,'d')
turtle.onkey(left,'a')
turtle.onkey(down,'s')
turtle.listen()
turtle.penup()
instructions = turtle.Turtle()
instructions.ht()
instructions.pu()
instructions.goto(-600, 200)
instructions.write("Maze Game", font=('Arial', 32, 'normal'))
instructions.goto(-600, 125)
instructions.write("Touch the Green", font=('Arial', 26, 'normal'))
instructions.goto(-580, 100)
instructions.write("And DIE!!!", font=('Arial', 26, 'normal'))
instructions.goto(-620, 50)
instructions.write("Make it Out to Win", font=('Arial', 26, 'normal'))
instructions.goto(-620, -75)
instructions.write("Use w/a/s/d to move", font=('Arial', 26, 'normal'))
#drawing the maze
maze = turtle.Turtle()
maze.ht()
maze.speed(0)
maze.penup()
maze.goto(-310, 300)
maze.pendown()
maze.pensize(40)
maze.color('green')
maze.forward(700)
maze.ht()
maze.speed(0)
maze.penup()
maze.goto(390, 300)
maze.pendown()
maze.right(90)
maze.pensize(40)
maze.color('green')
maze.forward(300)
maze.ht()
maze.speed(0)
maze.penup()
maze.goto(390, 0)
maze.pendown()
maze.left(90)
maze.pensize(40)
maze.color('green')
maze.forward(125)
maze.ht()
maze.speed(0)
maze.penup()
maze.goto(515, 0)
maze.pendown()
maze.right(90)
maze.pensize(40)
maze.color('green')
maze.forward(100)
I am making a maze game but I won't it so that when the player hits the maze it gets reset to the starting position but I don't know how.
I've tried one way with a distance method it did not work, but maybe because I did it wrong. I want it so that when the player hits the maze it gets reset to the starting position.
Generally, this is a difficult problem when using turtle. But since you're only using horizontal and vertical lines for your maze (and assuming you continue to do so) we can implement a simple wall collision test.
First, you need to explicitly store your lines in a data structure. Second, for the simple test to work, all your lines have to be described left to right or top to bottom:
from turtle import Screen, Turtle
LINES = [
((-310, 300), (390, 300)),
((390, 300), (390, 0)),
((390, 0), (515, 0)),
((515, 100), (515, 0)),
]
WALL_WIDTH = 40
PLAYER_WIDTH = 20
JUST_TOUCHING = WALL_WIDTH/2 + PLAYER_WIDTH/2
LARGE_FONT = ('Arial', 32, 'normal')
MEDIUM_FONT = ('Arial', 26, 'normal')
def up():
player.forward(PLAYER_WIDTH/2)
check_collision()
def right():
player.right(90)
check_collision()
def left():
player.left(90)
check_collision()
def down():
player.backward(PLAYER_WIDTH/2)
check_collision()
def check_collision():
x, y = player.position()
for ((a, b), (c, d)) in LINES:
if a == c: # vertical wall
if abs(x - a) < JUST_TOUCHING and d - JUST_TOUCHING < y < b + JUST_TOUCHING:
player.undo()
elif b == d: # horizontal wall
if abs(y - b) < JUST_TOUCHING and a - JUST_TOUCHING < x < c + JUST_TOUCHING:
player.undo()
screen = Screen()
screen.setup(1300, 650)
instructions = Turtle()
instructions.hideturtle()
instructions.penup()
instructions.goto(-600, 200)
instructions.write("Maze Game", font=LARGE_FONT)
instructions.goto(-600, 125)
instructions.write("Touch the Green", font=MEDIUM_FONT)
instructions.goto(-580, 100)
instructions.write("And DIE!!!", font=MEDIUM_FONT)
instructions.goto(-620, 50)
instructions.write("Make it Out to Win", font=MEDIUM_FONT)
instructions.goto(-620, -75)
instructions.write("Use w/a/s/d to move", font=MEDIUM_FONT)
maze = Turtle()
maze.hideturtle()
maze.speed('fastest')
maze.pensize(WALL_WIDTH)
maze.color('green')
for ((a, b), (c, d)) in LINES:
maze.penup()
maze.goto(a, b)
maze.pendown()
maze.goto(c, d)
player = Turtle()
player.shape("turtle")
player.penup()
player.goto(-400, 20)
screen.onkey(up, 'w')
screen.onkey(right, 'd')
screen.onkey(left, 'a')
screen.onkey(down, 's')
screen.listen()
screen.mainloop()
The above only prevents the player from crossing maze lines for testing purposes. You can change the code to end the game instead.
I needed to make the player's movements smaller as your original step distance is greater than the wall width so the player could "step over" a wall with my simple collision test!
This collision logic is fragile (and possibly incorrect in places), so keep your maze simple and spacious!
Here is the code, I have managed to position the square a little bit but the whole thing is just messy and fast when executed, it doesn't really resemble anything, let alone what I wanted to make.
What do I need to do in order to get these 4 squares to start in the middle and move to each corner, disappearing from the screen? I made an example here:
from tkinter import *
W, H = 500, 500
tk = Tk()
canvas = Canvas(tk,width=W,height=H)
canvas.pack()
class Square:
def __init__(self,size,speedx, speedy, color):
self.square = canvas.create_rectangle(50,50,100,100,fill=color)
self.speedx = speedx
self.speedy = speedy
self.movement()
def movement(self):
canvas.move(self.square,self.speedx,self.speedy)
pos = canvas.coords(self.square)
if pos[2]>=W or pos[0]<=0:
self.speedx *= -1
if pos[3]>=H or pos[1]<=0:
self.speedy *= -1
tk.after(40,self.movement)
square1 = Square(200,150,200,'brown')
square2 = Square(200,200,150,'yellow')
square3 = Square(200,200,200,'green')
square4 = Square(200,150,150,'blue')
tk.mainloop()
You need to define direction for each rectangle separately, so that each of them moves its own way.
import tkinter as tk
W, H = 500, 500
SPEED = 20
window = tk.Tk()
canvas = tk.Canvas(window, width=W, height=H)
canvas.pack()
class Square:
def __init__(self, x, y, color, speed_x, speed_y):
self.speed_x = speed_x
self.speed_y = speed_y
self.square = canvas.create_rectangle(x, y, x+50, y+50, fill=color)
self.movement()
def movement(self):
canvas.move(self.square, self.speed_x, self.speed_y)
window.after(200, self.movement)
Square(200, 200, 'brown', -SPEED, -SPEED)
Square(250, 200, 'yellow', SPEED, -SPEED)
Square(200, 250, 'green', -SPEED, SPEED)
Square(250, 250, 'blue', SPEED, SPEED)
window.mainloop()
Output:
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?
So I am trying to get 9 different balls to show up all with different color, size, and different movement. So far I have 9 balls, but they are sometimes the same color, all the same size, and all move almost the same trajectories.
not sure what I should change
from tkinter import *
import time
import random
WIDTH = 800
HEIGHT = 500
SIZE = random.randrange(10,100,10)
tk = Tk()
canvas = Canvas(tk, width=WIDTH, height=HEIGHT, bg="grey")
canvas.pack()
colors = ['black', 'blue', 'yellow','orange','green','purple', 'maroon', 'teal', 'brown']
balls = []
for _ in range (10):
balls.append(canvas.create_oval(0, 0, SIZE, SIZE, fill=random.choice(colors)))
class Ball:
def __init__(self):
for self.shape in balls:
self.speedx = 9 # changed from 3 to 9
self.speedy = 9 # changed from 3 to 9
self.active = True
self.move_active()
def ball_update(self):
for self.shape in balls:
canvas.move(self.shape, self.speedx, self.speedy)
pos = canvas.coords(self.shape)
if pos[2] >= WIDTH or pos[0] <= 0:
self.speedx *= -1
if pos[3] >= HEIGHT or pos[1] <= 0:
self.speedy *= -1
def move_active(self):
if self.active:
self.ball_update()
tk.after(40, self.move_active) # changed from 10ms to 30ms
ball = Ball()
tk.mainloop()
If you get random value always from the same list then items can repeate. And sometimes you can randomly get the same color for all items. Better get colors from list one-by-one, not randomly.
for color in COLORS:
ball_id = canvas.create_oval(..., fill=color)
You select SIZE only once - at start - and later you use the same value from SIZE. You should select random size inside loop for _ in range (10)
Every ball starts in the same place (0,0) and use the same speedx, speedy so it may move the same way. They should start in different places. And every ball should have own variable for speed.
for color in COLORS:
size = random.randrange(10, 50, 5)
x = random.randrange(0, WIDTH, 10)
y = random.randrange(0, HEIGHT, 10)
speedx = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
speedy = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
ball_id = canvas.create_oval(x, y, x+size, y+size, fill=color)
ball = [ball_id, speedx, speedy]
balls.append(ball)
from tkinter import *
import time
import random
# --- constants ---
WIDTH = 800
HEIGHT = 500
COLORS = ['black', 'blue', 'yellow','orange','green','purple', 'maroon', 'teal', 'brown']
# --- classes ---
class App:
def __init__(self, balls):
self.balls = balls
self.ball_update()
def ball_update(self):
for ball in self.balls:
#ball_id, speedx, speedy = ball
#canvas.move(ball_id, speedx, speedy)
canvas.move(ball[0], ball[1], ball[2])
pos = canvas.coords(ball[0])
if pos[2] >= WIDTH or pos[0] <= 0:
ball[1] *= -1
if pos[3] >= HEIGHT or pos[1] <= 0:
ball[2] *= -1
tk.after(50, self.ball_update) # changed from 10ms to 30ms
# --- functions ---
# empty
# --- main ---
tk = Tk()
canvas = Canvas(tk, width=WIDTH, height=HEIGHT, bg="grey")
canvas.pack()
balls = []
for color in COLORS:
size = random.randrange(10, 50, 5)
x = random.randrange(size, WIDTH-size, 10) # uses "size" to stop jamming the edge
y = random.randrange(size, HEIGHT-size, 10) # uses "size" to stop jamming the edge
speedx = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
speedy = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
ball_id = canvas.create_oval(x, y, x+size, y+size, fill=color)
ball = [ball_id, speedx, speedy]
balls.append(ball)
app = App(balls)
tk.mainloop()
EDIT: all in class App
import tkinter as tk # `import *` is not preferd
import time
import random
# --- constants ---
WIDTH = 800
HEIGHT = 500
COLORS = ['black', 'blue', 'yellow','orange','green','purple', 'maroon', 'teal', 'brown']
# --- classes ---
class App:
def __init__(self, root):
self.root = root
self.canvas = tk.Canvas(self.root, width=WIDTH, height=HEIGHT, bg="grey")
self.canvas.pack()
self.ball_create()
self.ball_update()
self.root.mainloop()
def ball_create(self):
self.balls = []
for color in COLORS:
size = random.randrange(10, 50, 5)
x = random.randrange(size, WIDTH-size, 10) # uses "size" to stop jamming the edge
y = random.randrange(size, HEIGHT-size, 10) # uses "size" to stop jamming the edge
speedx = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
speedy = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
ball_id = self.canvas.create_oval(x, y, x+size, y+size, fill=color)
ball = [ball_id, speedx, speedy]
self.balls.append(ball)
def ball_update(self):
for ball in self.balls:
#ball_id, speedx, speedy = ball
#self.canvas.move(ball_id, speedx, speedy)
self.canvas.move(ball[0], ball[1], ball[2])
pos = self.canvas.coords(ball[0])
if pos[2] >= WIDTH or pos[0] <= 0:
ball[1] *= -1
if pos[3] >= HEIGHT or pos[1] <= 0:
ball[2] *= -1
self.root.after(50, self.ball_update) # changed from 10ms to 30ms
# --- functions ---
# empty
# --- main ---
root = tk.Tk()
App(root)
root.mainloop()
BTW: class Ball should keep information only about one ball and move only this ball. Your class Ball worked rather like Application so I changed its name.
import tkinter as tk # `import *` is not preferd
import time
import random
# --- constants ---
WIDTH = 800
HEIGHT = 500
COLORS = ['black', 'blue', 'yellow','orange','green','purple', 'maroon', 'teal', 'brown']
# --- classes ---
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.color = color
self.size = random.randrange(10, 50, 5)
self.x = random.randrange(self.size, WIDTH-self.size, 10) # uses "size" to stop jamming the edge
self.y = random.randrange(self.size, HEIGHT-self.size, 10) # uses "size" to stop jamming the edge
self.speedx = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
self.speedy = random.choice([-9, -6, -3, 3, 6, 9]) # skip `0`
self.id = self.canvas.create_oval(self.x, self.y, self.x+self.size, self.y+self.size, fill=self.color)
def move(self):
self.canvas.move(self.id, self.speedx, self.speedy)
x1, y1, x2, y2 = self.canvas.coords(self.id)
if x1 <= 0 or x2 >= WIDTH:
self.speedx *= -1
if y1 <= 0 or y2 >= HEIGHT:
self.speedy *= -1
class App:
def __init__(self, root):
self.root = root
self.canvas = tk.Canvas(self.root, width=WIDTH, height=HEIGHT, bg="grey")
self.canvas.pack()
self.create()
self.move()
self.root.mainloop()
def create(self):
self.balls = []
for color in COLORS:
ball = Ball(self.canvas, color)
self.balls.append(ball)
def move(self):
for ball in self.balls:
ball.move()
self.root.after(50, self.move) # changed from 10ms to 30ms
# --- functions ---
# empty
# --- main ---
root = tk.Tk()
App(root)
root.mainloop()
I'm trying to build a game that involves a small red player (rectangle) being controlled by the arrow keys. There's a white grid on top of a black background, 2 different coloured objectives (rectangles), and several random red boxes (rectangles).
The part I need help with is moving the small red player. I can move it, but it seems to draw itself in the new position, but the version of the rectangle that was previously drawn stays there, forming a line. I want the entire rectangle to move and not leave any traces/previous versions of itself.
According to some other posts, I've heard that the only way to do this is to fill the screen with the background colour (in this case, black) and redraw the players on top of it; however, this is really hard in my case as I have the red boxes and objectives placed randomly, so every time I draw them again, they draw in a new random position, not in their old positions. I want the red boxes and objectives to stay in the same position, but have the player rectangles move around (while basically deleting the older versions of themselves).
Here's the code I currently have (I've excluded the basics, like defining colours, imports, and setting the screen height/width):
p1_velocity_x = 0
p1_velocity_y = 0
p2_velocity_x = 0
p2_velocity_y = 0
def grid():
for col in range(0, screen_width - 100, 10):
for row in range(0, screen_height, 10):
screen.set_at((row, col), white)
def red_boxes():
for i in range(100):
pygame.draw.rect(screen, red, (randrange(1, 1200), randrange(1, 470), 25, 25))
def blue_score_box():
pygame.draw.rect(screen, blue, (randrange(1, 1200), randrange(1, 470), 25, 25))
def yellow_score_box():
pygame.draw.rect(screen, yellow, (randrange(1, 1200), randrange(1, 470), 25, 25))
class Player:
color = (0, 0, 0)
def __init__(self, color):
self.x = 0
self.y = 0
self.color = color
p1 = Player(red)
p1.x = randrange(1, 1200, 10)
p1.y = randrange(1, 470, 10)
p2 = Player(yellow)
p2.x = randrange(1, 1200, 10)
p2.y = randrange(1, 470, 10)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
p1.x += 10
screen.fill(black)
grid()
red_boxes()
yellow_score_box()
blue_score_box()
pygame.draw.rect(screen, p1.color, (p1.x, p1.y, 10, 10))
pygame.draw.rect(screen, p2.color, (p2.x, p2.y, 10, 10))
pygame.display.update()
The result of the above code is that I'm able to move the red rectangle as I want, but since I call the red_boxes(), yellow_score_box(), and blue_score_box() methods in the while loop, they keep calling indefinitely, drawing random red, blue, and yellow rectangles all over the screen almost every second. I want them to stay in one place while having the functionality of moving the player as I do now.
Create a pygame.Surface with the same size than the red player rectangle:
bkP1 = pygame.Surface((10, 10))
Backup the background (pygame.Surface.blit) and store the position of the player, before the player is drawn:
prevPos = (p1.x, p1.y)
bkP1.blit(screen, (0, 0), (*prevPos, 10, 10))
Draw the background on top of the player when the player has to be erased:
screen.blit(bkP1, prevPos)
The process may work as follows:
prevPos = None
bkP1 = pygame.Surface((10, 10))
while True:
# [...]
if prevPos:
screen.blit(bkP1, prevPos)
prevPos = (p1.x, p1.y)
bkP1.blit(screen, (0, 0), (*prevPos, 10, 10))
pygame.draw.rect(screen, p1.color, (p1.x, p1.y, 10, 10))
Of course it is possible to add the mechanism to the class Player:
class Player:
def __init__(self, color):
self.x = 0
self.y = 0
self.color = color
self. prevPos = None
self.bk = pygame.Surface((10, 10))
def draw(self, screen):
if self.prevPos:
screen.blit(self.bk, self.prevPos)
self.prevPos = (self.x, self.y)
self.bk.blit(screen, (0, 0), (*self.prevPos, 10, 10))
pygame.draw.rect(screen, self.color, (self.x, self.y, 10, 10))
while True:
# [...]
p1.draw(screen)