I am working on a homework problem for my Python course that is supposed to display a car (got that) and then the car is supposed to simulate driving across the window until it reaches the end, and then restarting at the beginning. I have a race function that is supposed to be doing this but it's not doing anything. I'm having a hell of a time with classes and tkinter. I know that I need the value of x to be incremented each time it loops and updates so that the car looks like its moving, I'm just not sure how to implement that. Is my boolean not written correctly? I've tried changing things around a few different ways with the function but I can't seem to get it to do anything, so I must be missing something.
UPDATE: I have moved the race method into the class, and called it within the constructor. I put a print statement to get the value of x in the race method and its showing that x is being incremented correctly, but when I run the program my car disappears. So it's updating the value of x as it should, but its not displaying the graphic.
Code updated below
Any suggestions are appreciated!
# Import tkinter
from tkinter import *
# Set the height and Width of the window
width = 800
height = 800
# Create race car class with canvas as the argument
class RacingCar(Canvas):
# Constructor
def __init__(self, master, width, height):
# Constructor
Canvas.__init__(self, master, width = width, height = height)
# Create x and y variables for starting position
self.x = 10
self.y = 40
# Display the car
self.display_car()
self.race()
# Function to display car
def display_car(self):
# Delete original car
self.delete("car")
# Create first wheel
self.create_oval(self.x + 10, self.y - 10, self.x + 20,\
self.y, fill = "black", tags = "car")
# Create the second wheel
self.create_oval(self.x + 30, self.y - 10, self.x + 40,\
self.y, fill = "black", tags = "car")
# Create the body
self.create_rectangle(self.x, self.y - 20, self.x + 50,\
self.y - 10, fill = "green", tags = "car")
# Create the roof
self.create_polygon(self.x + 10, self.y - 20, self.x + 20,\
self.y - 30, self.x + 30, self.y - 30,\
self.x + 40, self.y - 20, fill = "green",\
tags = "car")
def race():
while True:
if self.x < width:
self.x += 2
else:
self.x = 0
self.after(88)
self.update()
window = Tk()
window.title("Racing Car")
racecar = RacingCar(window, width = 240, height = 50 )
racecar.pack()
window.mainloop()
This might not get it done (so I make this answer a community wiki), but I suggest you move the race method in the class:
class RaceCar(Canvas):
# Your current code
def race(self):
while True:
if self.x < width:
self.x += 2
else:
self.x = 0
self.after(88)
self.update()
Related
suppose I have code like the following. I want to black blob to end the game when it touched the frame, in other words, the wall. I actually am a beginner to Tkinter Canvas so if you give a code explanation too complex I can not understand it.
# Modules
from tkinter import *
# Classes
class Player:
def __init__(self, master=None):
self.master = master
self.x = 0 # To take care of movement in "x" direction.
self.y = 0 # To take care of movement in "y" direction.
self.canvas = Canvas(master) # Canvas object to create shape.
self.rectangle = self.canvas.create_rectangle(40, 40, 25, 25, fill="black") # Creating rectangle.
self.canvas.pack() # Packing the canvas.
self.movement() # Calling the movement method to move the rectangle.
def movement(self):
self.canvas.move(self.rectangle, self.x, self.y) # This is where the move() method is called.
"""
⬆ This moves the rectangle to x, y coordinates.
"""
self.canvas.after(100, self.movement) # Gives the character a cooldown time.
def left(self): # For motion in negative "x" direction.
self.x = -5
self.y = 0
def right(self): # For motion in positive "x" direction.
self.x = 5
self.y = 0
def down(self): # For motion in positive "y" direction.
self.x = 0
self.y = 5
def up(self): # For motion in negative "y" direction.
self.x = 0
self.y = -5
# Main Window
print("How To Play\nTry to not collid the frame.")
master = Tk()
master.title("Easy Game")
master.geometry("300x75")
master.resizable(True, True)
engine = Player(master)
master.bind("<KeyPress-Left>", lambda e: engine.left())
master.bind("<KeyPress-Right>", lambda e: engine.right())
master.bind("<KeyPress-Up>", lambda e: engine.up())
master.bind("<KeyPress-Down>", lambda e: engine.down())
# Mainloop
mainloop()
Thank you for further explanation.
I need to program a game in pygame similar to pong, but with 1 player. Although there is one paddle and one ball, I'm required to make a class for the paddle and for the ball. I created the paddle class, drawn it, but I have problem with implementing the if statements for movement. Here's what I tried:
class Paddle():
def __init__(self,x,y,width,height,color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
def Draw(self, screen,):
pygame.draw.rect(window, self.color, [self.x,self.y,self.width,self.height])
def Keys(self, y, height):
keys = pygame.key.get_pressed()
if keys [pygame.K_UP]:
self.y -= 1
if keys [pygame.K_DOWN]:
self.y += 1
and then I added a separate function for the objects from the classes:
def Objects():
paddle = Paddle(1150,250,20,100,black)
paddle.Draw(window)
paddle.Keys(250,100)
Again I want to add key movement in the class (since all paddles should have the same function). I should also mention that I'm not getting any error, but it doesn't work.
You must create the instance of Paddle before the application loop. Pass the object to the Objects function:
def Objects(surf, paddle):
paddle.Draw(surf)
paddle.Keys(250,100)
my_paddle = Paddle(1150,250,20,100,black)
while True:
# [...]
Objects(window, my_paddle)
# [...]
I'm just making a little game with Pygame. Objects should move across the screen. When I try to do this, a "track" is always dragged along (see picture). How can I move the apple without drawing the "course" of the movement?
from random import randint
import pygame
WIDTH = 800
HEIGHT = 800
apple = Actor("apple")
apple.pos = randint(0, 800), randint(800, 1600)
score = 0
def draw():
apple.draw()
screen.draw.text("Punkte: " + str(score), (700, 5), color = "white")
def update():
if apple.y > 0:
apple.y = apple.y - 4
else:
apple.x = randint(0, 800)
apple.y = randint(800, 1600)
This is not pure pygame, it is Pygame Zero. You've to call screen.clear() to clear the display in every frame:
def draw():
screen.clear()
apple.draw()
screen.draw.text("Punkte: " + str(score), (700, 5), color = "white")
Everytime you update, use pygame.display.flip(), this resets the screen.
I would also consider using a while loop, that would handle user input, draw the sprite, and then wipe the screen, and the just end the loop when the game is over.
What's happening is that the apple instead of being moved down is actually being redrawn many times over at the new coordinates. It seems that you are using an inbuilt class so idk what methods it has since I normally create my own class. What would fix it is if you had your apple object created before the main loop. Then in the main loop call a method to move the apple by how many pixels you want then update the position by using screen.blit()
For example, You could create a class for your apples, the class would take 4 parameters: which pygame window, x coordinate, y coordinate, and a path to the apple image.
class Apple():
def __init__(self, place, x, y, path,):
self.place = place
self.x = x
self.y = y
self.path = path
def load(self):
screen.blit(self.path, (self.x, self.y))
def move(self):
if self.y > 0:
self.y = self.y - 4
else:
self.x = randint(0, 800)
self.y = randint(800, 1600)
You would then create the apple object:
path = "path_to_the_image_of_the_apple"
apple_x = random.randint(0, 800)
apple_y = random.randint(0, 800)
apple = Apple(screen, apple_x, apple_y, path)
In the main loop then call a method to first move the apple, apple.move() then update the position apple.load()
Main loop:
#main game loop
while True:
#clear display
screen.fill(0)
#move call the function to move the apple
apple.move()
#updating the player
apple.load()
#update display
pygame.display.flip()
Note that in screen.blit(self.path, (self.x, self.y))
screen is just my variable in my code. replace it with whatever yours is.
I am trying to implement a similar program to the following in Python using Pymunk and Pyglet. My current implementation works well at low velocities however at high speeds the block can pass through the static wall. This because in 1/60s clock cycle the block moves further than the thickness of the wall. I have seen others solve this by having a limiting speed however in my case this would not work as the velocity is important to calculate the value for PI. I would like to know if there is any way of preventing this from happening.
import pyglet
import pymunk
class Block:
"""
The class for a block
Mass: the mass the block
X: Initial x position
Y: Initial y position
PhysSpace: The physics space to add items to
RenderBatch: Batch to add block to
"""
def __init__(self, Mass, X, Y, PhysSpace, RenderBatch):
# Create body with given mass and infinite moment of inertia
self.Body = pymunk.Body(Mass, pymunk.inf)
# Set Body's position
self.Body.position = X, Y
# Create shape for body
BodyShape = pymunk.Poly.create_box(self.Body, size=(50, 50))
# Define shapes elasticity
BodyShape.elasticity = 1
# Add block to the physics space
PhysSpace.add(self.Body, BodyShape)
# Import block image
BlockImg = pyglet.image.load('res/sqr.png')
# Set anchor point of image to be the centre
BlockImg.anchor_x = BlockImg.width // 2
BlockImg.anchor_y = BlockImg.height // 2
# Create sprite for block
self.BlockSprite = pyglet.sprite.Sprite(BlockImg, x=self.Body.position.x, y=self.Body.position.y,
batch=RenderBatch)
def update(self):
# Set the position of the sprite to be equal to the position of the physics body
self.BlockSprite.position = self.Body.position
def give_velocity(self, velocity):
# Set velocity of the body
self.Body.velocity = (velocity, 0)
class Simulation(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Set background to be clear
pyglet.gl.glClearColor(1, 1, 1, 1)
# Set clock speed
pyglet.clock.schedule_interval(self.update, 1/60)
# Create batch to draw all the graphics with
self.Batch = pyglet.graphics.Batch()
# Create Title Label
self.TitleLabel = pyglet.text.Label(text='Block Collision Simulator', x=self.width / 2, y=self.height - 20,
batch=self.Batch, anchor_x='center', anchor_y='center', font_size=24,
color=(0, 0, 0, 255))
self.Counter = -2
self.CounterLabel = pyglet.text.Label('Counter = 0'.format(self.Counter), x=self.width / 2, y=self.height - 60, anchor_x='center',
anchor_y='center', font_size=24, color=(0, 0, 0, 255), batch=self.Batch)
# Initiate space for Physics engine
self.Space = pymunk.Space()
self.Handler = self.Space.add_default_collision_handler()
self.Handler.begin = self.coll_begin
# Create the ground in physics engine
Ground = pymunk.Poly.create_box(self.Space.static_body, size=(self.width, 20))
Ground.body.position = self.width / 2, 10
self.Space.add(Ground)
# Create the sprite for the ground
GroundImg = pyglet.image.load('res/ground.png')
self.GroundSprite = pyglet.sprite.Sprite(GroundImg, x=0, y=0, batch=self.Batch)
# Create Wall in physics engine
Wall = pymunk.Poly.create_box(self.Space.static_body, size=(20, self.height))
Wall.body.position = 10, self.height / 2
Wall.elasticity = 1
self.Space.add(Wall)
# Create the sprite for the wall
WallImg = pyglet.image.load('res/wall.png')
self.WallSprite = pyglet.sprite.Sprite(WallImg, x=0, y=0, batch=self.Batch)
self.BlockRight = Block(10000, 2 * (self.width / 3), 45, self.Space, self.Batch)
self.BlockRight.give_velocity(-100)
self.BlockLeft = Block(1, self.width / 3, 45, self.Space, self.Batch)
pyglet.app.run()
def coll_begin(self, arbiter, space, data):
self.Counter += 1
if self.Counter > 0:
self.CounterLabel.text = 'Counter: {}'.format(self.Counter)
return True
def on_draw(self):
self.clear()
self.Batch.draw()
def update(self, dt):
self.Space.step(dt)
self.BlockRight.update()
self.BlockLeft.update()
One way is as you write to limit the velocity. Another way is to call the step function with a smaller dt. (On the same note, you should almost always use a fixed value for the dt, that will help to keep the simulation stable).
One way to use smaller dt is to call the step function multiple times for each call to the update function. So you can try something like this:
def update(self, dt):
for _ in range(10):
self.Space.step(dt/10)
#self.Space.step(dt)
self.BlockRight.update()
self.BlockLeft.update()
I'm learning python from a beginners book. Below is an extract of code from that book (Python Programming for the absolute beginner, 3rd edition) for a game.
My question is a fairly simple one. The update() methods appear never to be invoked, yet still function. How does this work?
I've pasted the whole block of code, so nothing is missing.
# Pizza Panic
# Player must catch falling pizzas before they hit the ground
from livewires import games, color
import random
games.init(screen_width = 640, screen_height = 480, fps = 50)
class Pan(games.Sprite):
"""
A pan controlled by player to catch falling pizzas.
"""
image = games.load_image("pan.bmp")
def __init__(self):
""" Initialize Pan object and create Text object for score. """
super(Pan, self).__init__(image = Pan.image,
x = games.mouse.x,
bottom = games.screen.height)
self.score = games.Text(value = 0, size = 25, color = color.black,
top = 5, right = games.screen.width - 10)
games.screen.add(self.score)
def update(self):
""" Move to mouse x position. """
self.x = games.mouse.x
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
self.check_catch()
def check_catch(self):
""" Check if catch pizzas. """
for pizza in self.overlapping_sprites:
self.score.value += 10
self.score.right = games.screen.width - 10
pizza.handle_caught()
class Pizza(games.Sprite):
"""
A pizza which falls to the ground.
"""
image = games.load_image("pizza.bmp")
speed = 1
def __init__(self, x, y = 90):
""" Initialize a Pizza object. """
super(Pizza, self).__init__(image = Pizza.image,
x = x, y = y,
dy = Pizza.speed)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.bottom > games.screen.height:
self.end_game()
self.destroy()
def handle_caught(self):
""" Destroy self if caught. """
self.destroy()
def end_game(self):
""" End the game. """
end_message = games.Message(value = "Game Over",
size = 90,
color = color.red,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 5 * games.screen.fps,
after_death = games.screen.quit)
games.screen.add(end_message)
class Chef(games.Sprite):
"""
A chef which moves left and right, dropping pizzas.
"""
image = games.load_image("chef.bmp")
def __init__(self, y = 55, speed = 2, odds_change = 200):
""" Initialize the Chef object. """
super(Chef, self).__init__(image = Chef.image,
x = games.screen.width / 2,
y = y,
dx = speed)
self.odds_change = odds_change
self.time_til_drop = 0
def update(self):
""" Determine if direction needs to be reversed. """
if self.left < 0 or self.right > games.screen.width:
self.dx = -self.dx
elif random.randrange(self.odds_change) == 0:
self.dx = -self.dx
self.check_drop()
def check_drop(self):
""" Decrease countdown or drop pizza and reset countdown. """
if self.time_til_drop > 0:
self.time_til_drop -= 1
else:
new_pizza = Pizza(x = self.x)
games.screen.add(new_pizza)
# set buffer to approx 30% of pizza height, regardless of pizza speed
self.time_til_drop = int(new_pizza.height * 1.3 / Pizza.speed) + 1
def main():
""" Play the game. """
wall_image = games.load_image("wall.jpg", transparent = False)
games.screen.background = wall_image
the_chef = Chef()
games.screen.add(the_chef)
the_pan = Pan()
games.screen.add(the_pan)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
# start it up!
main()
The update() methods of each objects are called by the game module you are importing at the top:
from livewires import games, color
The GUI that these modules are handling runs in a loop that manages events and callbacks.
If you are curious, you could open load and read these files, and find out how the code works. You will find that each object is, in turn, calling its own update() method.
You are using a framework livewires which you have imported at the top. That framework takes your objects and then invokes methods on them, instead of you calling the methods.
See this for a high level view of the difference between Frameworks and Libraries: Framework vs. Toolkit vs. Library