I am a young Python programmer. I decided to create a 2D game using pygame Purpose of the game: Drive as much distance as possible on the car, without crashing into objects that will "spawn" during the game. The car will drive across the field.
I have problems with decoration sprites (during the game, trees will "fall" down on the edges of the window) Fig1.pic1
So, trees should spawn right after the previous ones reach the middle of the window, but when new trees spawn, this is what happens to me: Fig2. And the game starts to freeze pic2
Here is my code:
from superwires import games, color
from random import randrange
games.init(screen_width = 530, screen_height = 600, fps = 60)
#Car sprite
class Car(games.Sprite):
image = games.load_image("C:/python/car.bmp")
def __init__(self):
super(Car, self).__init__(image = Car.image,
x = games.mouse.x,
bottom = games.screen.height - 10)
self.score = games.Text(value = 0, size = 25, color = color.yellow,
top = 5, right = games.screen.width/2)
games.screen.add(self.score)
def update(self):
self.x = games.mouse.x
if self.left < 65:
self.left = 65
if self.right > games.screen.width - 65:
self.right = games.screen.width - 65
#Tree sprite
class Bush1(games.Sprite):
image = games.load_image("C:/python/bush.bmp")
speed = 1
def __init__(self, x = 20, y = 100):
super(Bush1, self).__init__(image = Bush1.image,
x = x, y = y,
dy = Bush1.speed)
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush1()
newbush.__init__(x = 20, y = -100)
games.screen.add(newbush)
class Bush2(games.Sprite):
image = games.load_image("C:/python/bush.bmp")
speed = 1
def __init__(self, x = 515, y = 100):
super(Bush2, self).__init__(image = Bush2.image,
x = x, y = y,
dy = Bush2.speed)
#Spawning new trees
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush2()
newbush.__init__(x = 515, y = -100)
games.screen.add(newbush)
#Start
def main():
road = games.load_image("road.jpg", transparent = False)
games.screen.background = road
bush1 = Bush1()
bush2 = Bush2()
car = Car()
games.screen.add(bush1)
games.screen.add(bush2)
games.screen.add(car)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main()
I'll be glad to know where I made a mistake.
Used: Python 3.9, superwires, games
Here's your problem. Once your first bush reaches the halfway point, you create two new bushes ON EVERY FRAME. That's what you're seeing in your pic2 -- you have hundreds of slightly overlapping bushes. You need to create a new bush only when the old one is EXACTLY at the halfway point, not AT OR BELOW the halfway point:
if self.bottom == games.screen.height//2:
You might also consider deleting bushes once they fall off the bottom
This may not address the specific problem you're running across, but it might help to unpack and zero in on what is happening.
Here you are creating a new Bush1 and Bush2 object:
bush1 = Bush1()
bush2 = Bush2()
Note that the code for these two classes are virtually identical. The differences are only in the init defaults. It is equivalent to doing this:
bush1 = Bush1()
bush2 = Bush1(515, 100)
It seems you have a misunderstanding, based on this, about how classes in Python work. This is reinforced by the two almost-equivelent-but-for-constants update blocks:
def update(self):
if self.bottom > games.screen.height/2:
newbush = Bush1()
newbush.__init__(x = 20, y = -100)
games.screen.add(newbush)
On the third line of these blocks you're creating a new object. Then you're re-calling that objects __init__ method. When you create a new object, a couple of hidden 'private' methods are run, the last of which is the __init__ method. Typically if you need instantiation of an object to change, you would alter that method. You should never need to re-call that method.
Finally, you are not, so far as I can tell, actually moving the bushes. I suspect when you say 'starts to freeze' what is happening is you have many instantiations of the bushes overlapping one another, and the game is taking longer and longer to process each loop. Putting a print statement in each init method would probably help identify this problem, but it's possible your frameworks might have a better way to do that. (I am not familiar with them.)
Refactoring a bit to clean up the usage of classes and scoping variables properly should yield something along the lines of this (though almost certainly there is more we could do):
from superwires import games, color
from random import randrange
games.init(screen_width = 530, screen_height = 600, fps = 60)
#Car sprite
class Car(games.Sprite):
def __init__(self, image: str):
self.__image = games.load_image(image)
# Is the car really instantiated at the point the mouse currently is at?
super().__init__(image = self.__image,
x = games.mouse.x,
bottom = games.screen.height - 10)
# Is 'score' really a field of 'car'?
self.score = games.Text(value = 0,
size = 25,
color = color.yellow,
top = 5,
right = games.screen.width/2)
games.screen.add(self.score)
def update(self):
self.x = games.mouse.x
self.left = 65 if self.left < 65 else self.left
# This should probably be defined in a broader scope
max_right = games.screen.width - 65
self.right = self.right if self.right <= max_right else max_right
class Bush(games.Sprite):
speed = 1
def __init__(self,
image: str
x: int,
y: int):
self.__image = games.load_image(image)
self.__start_x = x
self.__start_y = y
super().__init__(image = self.__image,
x = x,
y = y,
dy = Bush.speed)
def update(self):
if self.bottom > games.screen.height/2:
# uses the same initial values as this object
newbush = Bush(self.__image,
self.__start_x,
-100) # start vertically above the screen
games.screen.add(newbush)
#Start
def main():
road = games.load_image("road.jpg", transparent = False)
games.screen.background = road
bush1 = Bush("C:/python/bush.bmp", 20, 100)
bush2 = Bush("C:/python/bush.bmp", 515, 100)
car = Car("C:/python/car.bmp")
games.screen.add(bush1)
games.screen.add(bush2)
games.screen.add(car)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main()
Related
I'm currently taking a python course where we had to built a snake game. I did not follow the tutors instuctions and I tried to do it on my own. I could make it work for the most part, but I can't figure out why the purple food elements sometimes are not eaten by the snake, even though the snake directly strikes over the food. I would appreciate it, if someone with more experience than me could look over my code to find the bug, I certainly don't know where the mistake is. Here is the code:
from turtle import Turtle, Screen
import random
import time
class Snake():
def __init__(self):
screen.onkeypress(self.look_left, 'a')
screen.onkeypress(self.look_right, 'd')
screen.listen()
self.snake_squares = {
1: Turtle(),
2: Turtle(),
3: Turtle(),
}
for square in self.snake_squares:
self.design_square(self.snake_squares[square])
self.snake_head = self.snake_squares[1]
self.look_left()
def design_square(self, current_square):
current_square.penup()
current_square.shape('square')
current_square.color('white')
current_square.shapesize(1.00)
current_square.pensize(1)
def add_square(self, new_score):
square_amount = new_score + 3
self.snake_squares[square_amount] = Turtle()
self.design_square(self.snake_squares[square_amount])
self.snake_squares[square_amount].setposition(self.old_position)
def look_left(self):
self.snake_head.left(90)
def look_right(self):
self.snake_head.right(90)
def move(self):
self.old_position = self.snake_head.position()
self.snake_head.forward(10.00)
all_square_positions = []
for square in self.snake_squares:
if square != 1:
next_square = self.snake_squares[square]
new_position = self.old_position
self.old_position = next_square.position()
next_square.setposition(new_position)
all_square_positions.append(new_position)
return self.snake_head.position(), all_square_positions
def check_self_hit(self, head_position, tail_positions):
if head_position in tail_positions:
return False
else:
return True
class Board():
def __init__(self, height):
self.score_board = Turtle()
self.score_board.hideturtle()
self.score_board.setposition(0, height + 10)
self.score_board.color('white')
self.score = 0
def add_to_score(self, old_score):
self.score_board.color('black')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
self.score = old_score + 1
self.score_board.color('white')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
return self.score
class Border():
def __init__(self, width, height):
self.width = width
self.height = height
self.positive_borders = [self.width, self.height]
self.negative_borders = [- self.width, - self.height]
def check_borders(self, snake_position):
no_hit = True
for border in self.positive_borders:
if snake_position[0] >= border:
no_hit = False
elif snake_position[1] >= border:
no_hit = False
else:
pass
for border in self.negative_borders:
if snake_position[0] <= border:
no_hit = False
elif snake_position[1] <= border:
no_hit = False
else:
pass
return no_hit
def print_border(self, width, height):
line = Turtle()
line.hideturtle()
line.setposition(-float(width), -float(height))
line.shape("circle")
line.speed('fastest')
line.color('white')
for wall in range(4):
line.forward(800)
line.left(90)
class Food():
def __init__(self, width, height):
self.width = width
self.height = height
self.food = Turtle()
self.food.hideturtle()
self.food.speed('fastest')
self.food.penup()
self.food.pensize(3)
self.food.shape('circle')
self.food.color('purple')
def create_food(self):
self.food.showturtle()
self.food.clear()
return self.random_position()
def random_position(self):
x_min = -self.width + 10 #-390
x_max = self.width - 10 #390
y_min = -self.height + 10 #-390
y_max = self.height - 10 #390
x = 11
y = 11
while x % 10.00 != 0.00:
x = float(random.randint(x_min, x_max))
while y % 10.00 != 0.00:
y = float(random.randint(y_min, y_max))
self.food.setposition((x, y))
x += 10.00
y += 10.00
all_possible_hits = []
for hits in range(3):
all_possible_hits.append((x, y -10.00))
x -= 10.00
for hits in range(3):
all_possible_hits.append((x + 10.00, y))
y -= 10.00
return all_possible_hits
def erase_food(self):
self.food.hideturtle()
screen = Screen()
screen.tracer(0)
canvas_width = 400
canvas_height = 400
screen.bgcolor("black")
border = Border(canvas_width, canvas_height)
border.print_border(canvas_width, canvas_height)
snake = Snake()
food = Food(canvas_width, canvas_height)
board = Board(canvas_height)
current_score = board.add_to_score(-1)
current_food_positions = food.create_food()
snake_alive = True
screen.update()
snake_path = []
while snake_alive:
time.sleep(0.03)
screen.update()
snake_current_position, all_positions = snake.move()
snake_path.append(snake_current_position)
if snake.check_self_hit(snake_current_position, all_positions):
if border.check_borders(snake_current_position):
if snake_current_position in current_food_positions:
current_score = board.add_to_score(current_score)
snake.add_square(current_score)
food.erase_food()
current_food_positions = food.create_food()
screen.update()
else:
snake_alive = False
else:
snake_alive = False
screen.exitonclick()
I don't really know why that happens, especially because it only happens sometimes and at different score amounts.
A few things here:
As the first comment pointed out, generally, you would not come to stackoverflow for debugging advice like this, at least without trying to debug on your own and posting the results with your question
Something you will need to keep in mind always is that directly comparing floating point values for equality is a big no-no in any programming (that is only one of the hundreds of articles explaining why, for a much more in depth and possibly over your head look, check out this). I believe that is directly where your problem lies in this case as well. By using snake_current_position in current_food_positions, you are likely asking for that comparison to be made between a bunch of floats as your have it written, and therefor failing the equality check when it looks like it should be true.
To find this out and debug your next program on your own, think about how debugging could be done. There are many tools but the simplest is sometimes just to print things. When taking a look myself, the first thing I did was add
print("snake:")
print(snake_pos_int)
print("food")
print(current_food_positions)
as well as turned the game speed down (sleep time up) so I could watch the output. This allowed me to get an idea if the values were sane, and also notice that the values were printing as floating point numbers with different amounts of rounding (10.0 vs 10.00) which is another indicator you definitely should not be directly comparing them.
This brings me to the "solution"
from turtle import Turtle, Screen
import random
import time
class Snake():
def __init__(self):
screen.onkeypress(self.look_left, 'a')
screen.onkeypress(self.look_right, 'd')
screen.listen()
self.snake_squares = {
1: Turtle(),
2: Turtle(),
3: Turtle(),
}
for square in self.snake_squares:
self.design_square(self.snake_squares[square])
self.snake_head = self.snake_squares[1]
self.look_left()
def design_square(self, current_square):
current_square.penup()
current_square.shape('square')
current_square.color('white')
current_square.shapesize(1)
current_square.pensize(1)
def add_square(self, new_score):
square_amount = new_score + 3
self.snake_squares[square_amount] = Turtle()
self.design_square(self.snake_squares[square_amount])
self.snake_squares[square_amount].setposition(self.old_position)
def look_left(self):
self.snake_head.left(90)
def look_right(self):
self.snake_head.right(90)
def move(self):
self.old_position = self.snake_head.position()
self.snake_head.forward(10)
all_square_positions = []
for square in self.snake_squares:
if square != 1:
next_square = self.snake_squares[square]
new_position = self.old_position
self.old_position = next_square.position()
next_square.setposition(new_position)
all_square_positions.append(new_position)
return self.snake_head.position(), all_square_positions
def check_self_hit(self, head_position, tail_positions):
if head_position in tail_positions:
return False
else:
return True
class Board():
def __init__(self, height):
self.score_board = Turtle()
self.score_board.hideturtle()
self.score_board.setposition(0, height + 10)
self.score_board.color('white')
self.score = 0
def add_to_score(self, old_score):
self.score_board.color('black')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
self.score = old_score + 1
self.score_board.color('white')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
return self.score
class Border():
def __init__(self, width, height):
self.width = width
self.height = height
self.positive_borders = [self.width, self.height]
self.negative_borders = [- self.width, - self.height]
def check_borders(self, snake_position):
no_hit = True
for border in self.positive_borders:
if snake_position[0] >= border:
no_hit = False
elif snake_position[1] >= border:
no_hit = False
else:
pass
for border in self.negative_borders:
if snake_position[0] <= border:
no_hit = False
elif snake_position[1] <= border:
no_hit = False
else:
pass
return no_hit
def print_border(self, width, height):
line = Turtle()
line.hideturtle()
line.setposition(-float(width), -float(height))
line.shape("circle")
line.speed('fastest')
line.color('white')
for wall in range(4):
line.forward(800)
line.left(90)
class Food():
def __init__(self, width, height):
self.width = width
self.height = height
self.food = Turtle()
self.food.hideturtle()
self.food.speed('fastest')
self.food.penup()
self.food.pensize(3)
self.food.shape('circle')
self.food.color('purple')
def create_food(self):
self.food.showturtle()
self.food.clear()
return self.random_position()
def random_position(self):
x_min = -self.width + 10 #-390
x_max = self.width - 10 #390
y_min = -self.height + 10 #-390
y_max = self.height - 10 #390
x = 11
y = 11
while x % 10 != 0:
x = random.randint(x_min, x_max)
while y % 10 != 0:
y = random.randint(y_min, y_max)
self.food.setposition((x, y))
x += 10
y += 10
all_possible_hits = []
for hits in range(3):
all_possible_hits.append((x, y -10))
x -= 10
for hits in range(3):
all_possible_hits.append((x+10, y))
y -= 10
return all_possible_hits
def erase_food(self):
self.food.hideturtle()
screen = Screen()
screen.tracer(0)
canvas_width = 400
canvas_height = 400
screen.bgcolor("black")
border = Border(canvas_width, canvas_height)
border.print_border(canvas_width, canvas_height)
snake = Snake()
food = Food(canvas_width, canvas_height)
board = Board(canvas_height)
current_score = board.add_to_score(-1)
current_food_positions = food.create_food()
snake_alive = True
screen.update()
snake_path = []
while snake_alive:
time.sleep(0.1)
screen.update()
snake_current_position, all_positions = snake.move()
snake_path.append(snake_current_position)
if snake.check_self_hit(snake_current_position, all_positions):
if border.check_borders(snake_current_position):
snake_pos_int = (round(int(snake_current_position[0])/10)*10, round(int(snake_current_position[1])/10)*10)
if snake_pos_int in current_food_positions:
current_score = board.add_to_score(current_score)
snake.add_square(current_score)
food.erase_food()
current_food_positions = food.create_food()
screen.update()
else:
snake_alive = False
else:
snake_alive = False
screen.exitonclick()
You can note the main change: The previously floating point values are integers (I notice that you even cast the random ints for food to floats, do not do this if you do not need to, in your case this only hurt) and the snake head values are somewhat hacked to be integers (simply casting them to integers was not enough, as in another demonstration of why not to use floats here, at one point one got rounded down to 9, breaking the game, so I apply a rounding to the nearest 10 as well). THIS IS NOT AN IDEAL SOLUTION. It is meant to demonstrate that the problem is the use of floats and that you should be using integers for this all the way through.
With this hacky solution, I was able to run multiple games out to and past 25 score, so I believe that mostly fixes your main problem. I will leave it to you to fix the secondary but related issue that if your snake grows long enough to run into itself, the collision detection may not work consistently because of the same issue with comparing floats for equality as you do with if head_position in tail_positions:.
cant move the character at the same time the obstacle is moving, pirincipal is the name of the image that works as the player
def move():
def left(event):
x = -5
y = 0
canvasGame.update()
edgeReached()
canvasGame.move(principal,x, y)
collision()
def right(event):
x = 5
y = 0
canvasGame.update()
edgeReached()
canvasGame.move(principal,x, y)
collision()
canvasGame.bind_all("<Left>", left)
canvasGame.bind_all("<Right>", right
move()
class Barrel:
def __init__(self, canvas):
self.canvas = canvas
self.obs = canvasGame.create_image(125, 500, image=nuevoObs, anchor=tk.NW)
self.x = 16
self.y = 0
def movement(self):
while True:
coords = canvasGame.coords(self.obs)
if (coords[0] >= 1000):
self.x = -10
self.y = 0
elif (coords[0] < 0):
self.x = 10
self.y = 0
self.canvas.move(self.obs, self.x,self.y)
canvasGame.update()
time.sleep(0.05)
def createBarrel():
barrel = Barrel(canvasGame)
circle_thread = Thread(target=barrel.movement())
circle_thread.daemon = True
circle_thread.start()
createBarrel()
def plzmove():
moveplz = Thread(target=move())
moveplz.daemon = True
moveplz.start()
plzmove()
I tried creating threads but the problem continues, if there is a barrel moving, the player cant move (if there is nothing else moving, the player can move with freedom), also, the player movement is just an image being moved, any tip is appreciated, much love.
I want to change an image of the object worker each time when it stops.
The class Worker is created based on the answer of #sloth in this thread.
class Worker(pygame.sprite.Sprite):
def __init__(self, image_file, location, *groups):
# we set a _layer attribute before adding this sprite to the sprite groups
# we want the workers on top
self._layer = 1
pygame.sprite.Sprite.__init__(self, groups)
self.image = pygame.transform.scale(pygame.image.load(image_file).convert_alpha(), (40, 40))
self.rect = self.image.get_rect(topleft=location)
# let's call this handy function to set a random direction for the worker
self.change_direction()
# speed is also random
self.speed = random.randint(1, 3)
def change_direction(self):
# let's create a random vector as direction, so we can move in every direction
self.direction = pygame.math.Vector2(random.randint(-1,1), random.randint(-1,1))
# we don't want a vector of length 0, because we want to actually move
# it's not enough to account for rounding errors, but let's ignore that for now
while self.direction.length() == 0:
self.direction = pygame.math.Vector2(random.randint(-1,1), random.randint(-1,1))
# always normalize the vector, so we always move at a constant speed at all directions
self.direction = self.direction.normalize()
def update(self, screen):
# there is a less than 1% chance every time that direction is changed
if random.uniform(0,1)<0.005:
self.change_direction()
# now let's multiply our direction with our speed and move the rect
vec = [int(v) for v in self.direction * self.speed]
self.rect.move_ip(*vec)
# if we're going outside the screen, move back and change direction
if not screen.get_rect().contains(self.rect):
self.change_direction()
self.rect.clamp_ip(screen.get_rect())
I try to create a cache of pre-loaded images
image_cache = {}
def get_image(key):
if not key in image_cache:
image_cache[key] = pygame.image.load(key)
return image_cache[key]
Then I assume that it is necessary to add the following code into def __init__:
images = ["worker.png", "worker_stopped.png"]
for i in range(0,len(images)):
self.images[i] = get_image(images[i])
and the following code into def update(self):
if self.direction.length() == 0:
self.image = self.images[1]
else:
self.image = self.images[0]
However, it does not seem to work properly. The old image worker.png does not disappear and the whole animation gets locked.
I think you should introduce some kind of state to indicate that the worker is running or not. Here's an example. Note the comments:
class Worker(pygame.sprite.Sprite):
# we introduce to possible states: RUNNING and IDLE
RUNNING = 0
IDLE = 1
def __init__(self, location, *groups):
# each state has it's own image
self.images = {
Worker.RUNNING: pygame.transform.scale(get_image("worker.png"), (40, 40)),
Worker.IDLE: pygame.transform.scale(get_image("worker_stopped.png"), (40, 40))
}
self._layer = 1
pygame.sprite.Sprite.__init__(self, groups)
# let's keep track of the state and how long we are in this state already
self.state = Worker.IDLE
self.ticks_in_state = 0
self.image = self.images[self.state]
self.rect = self.image.get_rect(topleft=location)
self.direction = pygame.math.Vector2(0, 0)
self.speed = random.randint(2, 4)
self.set_random_direction()
def set_random_direction(self):
# random new direction or standing still
vec = pygame.math.Vector2(random.randint(-100,100), random.randint(-100,100)) if random.randint(0, 5) > 1 else pygame.math.Vector2(0, 0)
# check the new vector and decide if we are running or fooling around
length = vec.length()
speed = sum(abs(int(v)) for v in vec.normalize() * self.speed) if length > 0 else 0
if length == 0 or speed == 0:
new_state = Worker.IDLE
self.direction = pygame.math.Vector2(0, 0)
else:
new_state = Worker.RUNNING
self.direction = vec.normalize()
self.ticks_in_state = 0
self.state = new_state
# use the right image for the current state
self.image = self.images[self.state]
def update(self, screen):
self.ticks_in_state += 1
# the longer we are in a certain state, the more likely is we change direction
if random.randint(0, self.ticks_in_state) > 30:
self.set_random_direction()
# now let's multiply our direction with our speed and move the rect
vec = [int(v) for v in self.direction * self.speed]
self.rect.move_ip(*vec)
# if we're going outside the screen, change direction
if not screen.get_rect().contains(self.rect):
self.direction = self.direction * -1
self.rect.clamp_ip(screen.get_rect())
I have some problems and I cant figure out how to fix them...
It is really simple game. A bar moved by the mouse and one ball is (or some balls) bouncing.
The user just need to keep the ball bouncing.
The user can choose how many balls he wants (1,2,3), the size of the bar (small, medium,large) and the speed of the balls (slow, normal, fast).
Problems:
- sometimes everything works fine, and sometimes the ball (or balls) just goes through the bar. Like if the collision function does not work. Is there any other way I can do?
everytime there is a collision, the score should add 10 points for the total displayed on top of the screen, but this score is been overwriting all the time.
For this game be run, I just have to call the function (startGame) from other file (settings), where it also sends the value of number of balls, size of bar and speed of balls.
If anyone can help I appreciate.
Thanks
from livewires import games, color
from tkinter import*
import random
games.init(screen_width = 735, screen_height = 350, fps = 60)
class Bounce(games.Sprite):
global total_score
total_score = 0
def update(self):
global total_score
if self.bottom == 315 and self.overlapping_sprites:
self.dy = -self.dy
total_score += 10
the_score = games.Text(value = 0, size = 25,
color = color.gray,x = 700, y = 20)
games.screen.add(the_score)
if self.right > games.screen.width or self.left < 0:
self.dx = -self.dx
if self.top < 0:
self.dy = -self.dy
if self.bottom == 400:
lose_message = games.Message(value = " - GAME OVER -",
size = 50,
color = color.gray,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 300,
after_death = games.screen.quit)
games.screen.add(lose_message)
class Bar_moving(games.Sprite):
def update(self):
self.x = games.mouse.x
self.y = 315
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
class have_settings():
def __init__(self):
global the_ball_speed
global the_ball_number
global the_bar_size
if "the_ball_speed" not in globals():
the_ball_speed = "normal"
if "the_bar_size" not in globals():
the_bar_size = "medium"
if "the_ball_number" not in globals():
the_ball_number = 1
def set_all(self, number, size, speed):
global the_ball_speed
global the_bar_size
global the_ball_number
if speed in ("slow","normal","fast"):
the_ball_speed = speed
if size in ("small","medium","large"):
the_bar_size = size
if number in (1,2,3):
the_ball_number = number
def startGame():
call = have_settings()
background = games.load_image("BG.jpg", transparent = False)
games.screen.background = background
#-------------------------------------SPEED
sp_is = 0
if the_ball_speed == "slow":
sp_is = 2
elif the_ball_speed == "normal":
sp_is = 3
elif the_ball_speed == "fast":
sp_is = 4
#-------------------------------------BALL NUMBER
if the_ball_number in (1,2,3):
for n in range(the_ball_number):
position_x_list =(50,150,250,350,400,450,500,550)
position_x = random.choice(position_x_list)
position_y_list =(50,100,150,200,225,250)
position_y = random.choice(position_y_list)
vert_speed_list = (-2,2)
vert_speed = random.choice(vert_speed_list)
ball_img = games.load_image("ball.bmp")
ball = Bounce(image = ball_img,
x = position_x,
y = position_y,
dx = vert_speed,
dy = - sp_is)
games.screen.add(ball)
#-------------------------------------BAR SIZE
if the_bar_size in ("small","medium","large"):
if the_bar_size == "small":
bar_pic = "bar_small.jpg"
elif the_bar_size == "medium":
bar_pic = "bar_medium.jpg"
elif the_bar_size == "large":
bar_pic = "bar_large.jpg"
bar = games.load_image(bar_pic, transparent = False)
the_bar = Bar_moving(image = bar, x = games.mouse.x)
games.screen.add(the_bar)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
You should do a greater than check rather than an equals check as follows:
if self.bottom >= 315 and self.overlapping_sprites:
^^
instead of
if self.bottom == 315 and self.overlapping_sprites:
This is because rarely will the ball's y position ever perfectly line up with the bottom. In some cases it may go from y==314 to y==316. In such cases, your method above wouldn't work. Therefore, you should be a greater than test rather than an equality test.
You can apply similar changes everywhere else and it should work.
I've been programming a simple ping-pong game using Python's Pygame as run through Livewires. I've posted the question and a similar sample game code that runs perfectly as not everyone here will be versed in livewires and/or pygame.
Here's the code that has bugs on it. What happens is the window opens, and the "Ball" object works well and falls of the screen (as it should, it's incomplete), and the "Slab" object is gets stuck to wherever the mouse initially is. Here it is:
from livewires import games, color
games.init (screen_width = 640, screen_height = 480, fps = 50)
class Ball (games.Sprite):
def iMove (self):
self.dx = -self.dx
self.dy = -self.dy
class Slab (games.Sprite):
def mouse_moves (self):
self.x = games.mouse.x
self.y = games.mouse.y
self.iCollide()
def iCollide (self):
for Ball in overlapping_sprites:
Ball.iMove()
def main():
#Backgrounds
pingpongbackground = games.load_image ("pingpongbackground.jpg", transparent = False)
games.screen.background = pingpongbackground
#Ball: Initializing object and setting speed.
ballimage = games.load_image ("pingpongball.jpg")
theball = Ball (image = ballimage,
x = 320,
y = 240,
dx = 2,
dy = 2)
games.screen.add(theball)
#Paddle: Initializing ping pong object and setting initial poisition to the initial mouse position
slabimage = games.load_image ("pingpongpaddle.jpg")
theslab = Slab (image = slabimage,
x = games.mouse.x,
y = games.mouse.y)
games.screen.add(theslab)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main ()
And here's a piece of similar, functioning code:
# Slippery Pizza Program
# Demonstrates testing for sprite collisions
from livewires import games
import random
games.init(screen_width = 640, screen_height = 480, fps = 50)
class Pan(games.Sprite):
"""" A pan controlled by the mouse. """
def update(self):
""" Move to mouse position. """
self.x = games.mouse.x
self.y = games.mouse.y
self.check_collide()
def check_collide(self):
""" Check for collision with pizza. """
for pizza in self.overlapping_sprites:
pizza.handle_collide()
class Pizza(games.Sprite):
"""" A slippery pizza. """
def handle_collide(self):
""" Move to a random screen location. """
self.x = random.randrange(games.screen.width)
self.y = random.randrange(games.screen.height)
def main():
wall_image = games.load_image("wall.jpg", transparent = False)
games.screen.background = wall_image
pizza_image = games.load_image("pizza.bmp")
pizza_x = random.randrange(games.screen.width)
pizza_y = random.randrange(games.screen.height)
the_pizza = Pizza(image = pizza_image, x = pizza_x, y = pizza_y)
games.screen.add(the_pizza)
pan_image = games.load_image("pan.bmp")
the_pan = Pan(image = pan_image,
x = games.mouse.x,
y = games.mouse.y)
games.screen.add(the_pan)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
# kick it off!
main()
Any insight at all would be greatly appreciated!
I don't know your framework, but to keep the slab from "getting stuck" you need to update its location when the mouse is moved.
Here you initialize it:
theslab = Slab (image = slabimage,
x = games.mouse.x,
y = games.mouse.y)
Then here you add it to the game:
games.screen.add(theslab)
Presumably, the game would then call this function whenever the mouse moves:
def mouse_moves (self):
self.x = games.mouse.x
self.y = games.mouse.y
self.iCollide()
But that is either not happening, or the screen is not getting updated.
So you should find out, by doing this:
def mouse_moves (self):
print "mouse_moves: ", str(games.mouse.x), str(games.mouse.y)
self.x = games.mouse.x
self.y = games.mouse.y
self.iCollide()
If you see the output of the print statement happening when the mouse moves, you're probably not updating the screen, you'll need to check the framework docs. But I don't think that's the case. I think you are not updating the game when the mouse moves. I would imagine the framework has some sort of onMouseMove type event that you need to hook into, to allow you to update the game state (i.e. call mouse_moves()) when mouse movement occurs. Then, the next time the screen is updated, you should check for changes (invalidate the objects, mark them as dirty) and if they are dirty, update their part of the screen then mark them clean again.
Just put this update method in the slab class:
def update(self):
self.x=games.mouse.x
self.y=games.mouse.y
It will automatically run once every fiftieth of a second (your update speed). Currently, you just start the slab at the position of the mouse, and leave it there.