My RL Snake project doesn't seem to learn at all - python

I am using a DQL model with replay buffer to train the snake, where the state is represented by a vector containing [x-pos of the apple, y-pos of the apple, x-pos of snake_head, y-pos of snake_head .... ]
class DQL:
def __init__(self, model, actions, discount_factor=0.95, exploration_rate=0.2, memory_size=100000, batch_size=20, decay_rate=0.995, base_exploration_rate = 0.1):
#NN
self.model = model
self.actions = actions
#gamma
self.discount_factor = discount_factor
#for epsilon-greedy
self.exploration_rate = exploration_rate
#buffer
self.memory = deque(maxlen=memory_size)
self.batch_size = batch_size
#diminish exploration
self.decay_rate = decay_rate
self.base_exploration_rate = base_exploration_rate
def get_action(self, state, direction,length):
if np.random.rand() < self.base_exploration_rate + self.exploration_rate:
# Choose a random action
l = list(range(0,len(self.actions)))
l.remove(direction)
action = l[np.random.choice(len(self.actions)-1)]
else:
# Choose the best action according to the model
q_values = self.model.predict(state,verbose = 0)
sorted = q_values.argsort()
action = sorted[0][0]
if(action == direction and length>1):
action= sorted[0][1]
return action
def add_memory(self, state, action, reward, next_state, done):
x = np.random.rand()
self.memory.append((state, action, reward, next_state, done))
def train(self):
if len(self.memory) < self.batch_size:
# Not enough memories to train the model
return
# Randomly sample memories from the replay buffer
batch = random.sample(self.memory, self.batch_size)
states, actions, rewards, next_states, dones = [], [], [], [], []
for state, action, reward, next_state, done in batch:
states.append(state[0])
actions.append(action)
rewards.append(reward)
next_states.append(next_state[0])
dones.append(done)
states = np.array(states)
next_states = np.array(next_states)
# Decrease the exploration rate
self.exploration_rate *= self.decay_rate
# Calculate the target Q-values
#qw(st+1,a[0],a[1],a[2].. )
next_q_values = self.model.predict(next_states,verbose = 0)
target_q_values = np.zeros((self.batch_size,len(self.actions)))
for i in range(self.batch_size):
if dones[i]:
target_q_values[i][actions[i]] = rewards[i]
else:
target_q_values[i][actions[i]] = rewards[i] + self.discount_factor * max(next_q_values[i])
# Train the model with the target Q-values
# we want for qw(St,a) to become target_q[a]
self.model.fit(states, target_q_values, verbose=0)
And here is the game
# Snake block size
block_size = 10
# Set display width and height
width = 150
height = 150
pygame.init()
# Create display surface
screen = pygame.display.set_mode((width, height))
# Set title for the display window
pygame.display.set_caption("Snake Game")
# Define colors
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
# Set clock to control FPS
clock = pygame.time.Clock()
# Font for displaying score
font = pygame.font.Font(None, 30)
# FPS
fps = 10
def game_over():
# Display Game Over message
text = font.render("Game Over!", True, red)
screen.blit(text, [width/2 - text.get_width()/2, height/2 - text.get_height()/2])
'''pygame.display.update()
pygame.time.wait(3000)
pygame.quit()
sys.exit()'''
def display_score(score,gen,s):
# Display current score
text = font.render("Gen: " + str(gen) + " Length : " + str(score)+ " Score: " + str(s), True, black)
screen.blit(text, [0,0])
def draw_snake(snake_list):
# Draw the snake
for block in snake_list:
pygame.draw.rect(screen, black, [block[0], block[1], block_size, block_size])
def generate_food(snake_list):
# Generate food for the snake where there is no snake
food_x, food_y = None, None
while food_x is None or food_y is None or (food_x, food_y) in snake_list:
food_x = round(random.randrange(0, width - block_size) / 10.0) * 10.0
food_y = round(random.randrange(0, height - block_size) / 10.0) * 10.0
return food_x, food_y
#dictionary of possible actions
actions = {"up":(-1,0),"down":(1,0),"left":(0,-1),"right":(0,1)}
#(x,y) apple + snake body (which has at most width*height parts)
input_size = 2*width*height//block_size**2+2
def initNN():
#create a dense NN
model = Sequential()
model.add(Dense(256, input_shape=(input_size,), activation='sigmoid'))
model.add(Dense(128 , activation = 'tanh'))
model.add(Dense(len(actions), activation='linear'))
# Compile the model using categorical crossentropy loss and the Adam optimizer
model.compile(loss='mse', optimizer='adam', metrics=['accuracy'])
print(model.summary())
return model
#returns a vector that has the state of snake and apple ( the NN input vector )
def state(snake_list,apple):
s = np.array(snake_list)
#create an input vector starting from the apple, head .. the rest of the tail
input = np.zeros(input_size)
input[0],input[1] = apple[0]/width,apple[1]/height
for u in range(len(snake_list)):
if(2*u+2>=input_size):
break
input[2*u+2],input[2*u+3]= s[len(snake_list)-1-u][0]/width,s[len(snake_list)-1-u][1]/height
input=input.reshape(1,input_size)
return input
def normalized_distance(u,v,food_x,food_y):
return np.sqrt((((u-food_x)/width)**2+((v-food_y)/height)**2)/2)
def inBounds(u,v):
if(u>=0 and v>=0):
if(u<width and v<height):
return True
return False
def gaussian_aroundone(x,alpha):
return(np.exp(-alpha*(x-1)**2))
#reward function for each state and action
def reward(action, snake_list):
copy = deepcopy(snake_list)
p = copy[-1]
a = list(actions.values())
(u,v)=(a[action][1]*block_size+p[0],a[action][0]*block_size+p[1])
penalty_touch_self = 0
if [u,v] in snake_list:
penalty_touch_self=-1 # return a negative reward if the snake collides with itself
copy.append([u,v])
del copy[0]
global food_x, food_y
# reward the agent for getting closer to the food
reward_distance = 1-normalized_distance(u,v,food_x,food_y)
#if too far then the reward is very close to 0
gass_reward = gaussian_aroundone(reward_distance,10)
# reward the agent for eating the food
reward_eat = 1 if u == food_x and v == food_y else 0
# penalize the agent for moving away from the food
penalty_distance = -1 if normalized_distance(u,v,food_x,food_y) > normalized_distance(p[0], p[1], food_x, food_y) else 1
# penalize the agent for hitting a wall
penalty_wall = -1 if not (inBounds(u,v)) else 0
# combine all rewards and penalties
c = np.ones(5)
c[1] = 10
c[3]=5
total_reward =(c[0]* gass_reward + c[1]*reward_eat + c[2]*penalty_distance + c[3]*penalty_wall + c[4]*penalty_touch_self)/c.sum()
return total_reward
#initialize NN
filename = str(width)+" " + str(height)+" DeepQ.h5"
if(os.path.exists("./"+filename)):
print("model already exists ")
model = keras.models.load_model("./"+filename)
else:
model = initNN()
#initialize deepQ
dql = DQL(model,actions.values())
# Initialize pygame
food_x, food_y = generate_food([])
def main(gen,length):
# Initial snake position and food
snake_x = (width//block_size)//2 * block_size
snake_y = (height//block_size)//2 * block_size
snake_list = [[snake_x,snake_y]]
global food_x,food_y
#food_x, food_y = generate_food([])
episode_length =0
# Initial snake direction and length
direction = "right"
snake_length =length
prev_direction = "right"
#direction of the snake [0,1,2,3] each corresponding to one of up down left right
a=0
# = True if snake hits wall or itself
done = False
# number of collected fruit
score = 0
acts = list(actions.values())
# Game loop
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
# Start a new game
main()
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
St1 = state(snake_list,[food_x,food_y])
a = dql.get_action(St1,a,snake_length)
r = reward(a,snake_list)
#move snake
snake_x+=acts[a][1]*block_size
snake_y+=acts[a][0]*block_size
# Add new block of snake to the list
snake_list.append([snake_x, snake_y])
# Keep the length of snake same as snake_length
if len(snake_list) > snake_length:
del snake_list[0]
# Check if snake hits the boundaries
if snake_x >= width or snake_x < 0 or snake_y >= height or snake_y < 0:
done = True
# Check if snake hits itself
for block in snake_list[:-1]:
if block[0] == snake_x and block[1] == snake_y:
done = True
break
# Fill the screen with white color
screen.fill(white)
# Display food
pygame.draw.rect(screen, red, [food_x, food_y, block_size, block_size])
# Draw the snake
draw_snake(snake_list)
St2 = state(snake_list,[food_x,food_y])
#add to Buffer
dql.add_memory(St1,a,r,St2,done)
# Display score and other metrics
#display_score(snake_length-1,gen,score)
# Update the display
pygame.display.update()
# Check if snake hits the food
if snake_x == food_x and snake_y == food_y:
food_x, food_y = generate_food(snake_list)
snake_length += 1
score+=1
episode_length+=1
#if snake has hit something quit
if(done):
return [snake_length,episode_length,score]
# Set the FPS
clock.tick(fps)
#number of episodes
num_episodes =10000000
#maximum score reached
m =0
#initial max length for the snake at birth
max_length = 2
#maximum allowed length for a snake
max_max_length = width*height//block_size**2
#generation of episodes
for i in range(num_episodes):
#do a generation and see the outcome
a= main(i,np.random.randint(1,max_length+1))
#update maximum score
m = max(a[2],m)
#generate a new food position every 20 generations
if(i%200 ==0):
food_x, food_y = generate_food([])
#increase maximum birth length every 1000 generation
if((i+1)%1000==0):
model.save(str(width)+" " + str(height)+" DeepQ.h5")
max_length+=1
#train the DQL
dql.train()
I have been trying to run this RL project for over a week now, with many different NN architectures. Many other hyper parameters and reward functions. nothing seem to work. At the end, the snake either starts going hit the walls at each generation or would oscillate in place.
I am new to the subject and I have no Idea if my DQL is implemented correctly. I really could use some help.

Related

Pygame beginner, player animation not working despite doing it by the tutorial

First time ever working with Pygame for a school assignment and I'm following a tutorial for it with my project members (tutorial link https://youtu.be/AY9MnQ4x3zk).
Problem is that despite following the tutorial to the dot at the "animating the player", my character (named "Marko" in the game and code) doesn't have his animation playing. When I start the game the character is stuck on it's first frame of animation. I've created a 3-frame animation and have the frames as separate png-files. The game so far itself works (it's on very beginner level, just a couple spawning enemies, collision and intro screens done so far), but the animation has had me and my project group scratching our heads for days. So far haven't found a solution by Googling nor searching here.
Also at the "# Marko's animations" -part "marko" is darkened and when hovering mouse on it, it says ""(variable) marko: Surface - "marko" is not accessed Pylance""
Here's the entire code:
import sys
from pygame.locals import *
from random import randint
from pygame import mixer
pygame.init()
width = 1920
height = 1080
screen = pygame.display.set_mode((width,height))
pygame.display.set_caption("Putkimies Marko")
start_time = 0
score = 0
nopeus = [3,3]
clock = pygame.time.Clock()
game_active = False
marko_gravity = 0
# Score
def display_score():
current_time = int(pygame.time.get_ticks() / 1000) - start_time
score_surf = test_font.render(f'Score: {current_time}',False,(black))
score_rect = score_surf.get_rect(center = (900,50))
screen.blit(score_surf,score_rect)
return current_time
def obstacle_movement(obstacle_list):
if obstacle_list:
for obstacle_rect in obstacle_list:
obstacle_rect.x -= 5
if obstacle_rect.bottom == 955:
screen.blit(rat,obstacle_rect)
else:
screen.blit(fly,obstacle_rect)
return obstacle_list
else: return[]
def collisions(marko,obstacles):
if obstacles:
for obstacle_rect in obstacles:
if marko.colliderect(obstacle_rect): return False
return True
# Surfaces
background = pygame.image.load("marko_background.png").convert_alpha()
sewer = pygame.image.load("background_sewer.png").convert_alpha()
ground = pygame.image.load("ground.png").convert_alpha()
rat = pygame.image.load("rat.png").convert_alpha()
game_over = pygame.image.load("game_over.png").convert_alpha()
fly = pygame.image.load("fly.png").convert_alpha()
# Marko Surfaces
marko_run_1 = pygame.image.load("marko_run_1.png").convert_alpha()
marko_run_2 = pygame.image.load("marko_run_2.png").convert_alpha()
marko_run_3 = pygame.image.load("marko_run_3.png").convert_alpha()
marko_run = [marko_run_1,marko_run_2,marko_run_3]
marko_index = 0
marko_jump = pygame.image.load("marko_jump.png").convert_alpha()
marko = marko_run[marko_index]
# Fonts
test_font = pygame.font.Font("supermario.ttf", 50)
# Colors
black = (0,0,0)
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
light_blue = (94,129,162)
purple = (36,5,83)
# Rects
game_overSurface = game_over.get_rect(midtop = (970,-200))
platform = pygame.Surface((300,50))
groundSurface = ground.get_rect(midtop = (960,480))
markoSurface = marko.get_rect()
text_surface = test_font.render('Putkimies Marko', False, red)
markoSurface.left = 50
markoSurface.bottom = 714
# Obstacles
obstacle_rect_list = []
# Intro screen
player_stand = pygame.image.load("putkimies_marko_idle.png")
player_stand = pygame.transform.rotozoom(player_stand,0,2)
player_stand_rect = player_stand.get_rect(center = (950,500))
game_name = test_font.render("PUTKIMIES MARKO", False,"Black")
game_name_rect = game_name.get_rect(center = (950,350))
game_message = test_font.render('PRESS SPACE TO PLAY',False,"Black")
game_message_rect = game_message.get_rect(center = (950,650))
game_message_start_again = test_font.render('PRESS SPACE TO PLAY AGAIN',False,"Black")
game_message_start_again_rect = game_message.get_rect(center = (850,720))
# Marko's animations
def marko_animation():
global markoSurface, marko_index
if markoSurface.bottom < 955:
marko = marko_jump
else:
marko_index += 0.1
if marko_index >= len(marko_run):marko_index = 0
marko = marko_run[int(marko_index)]
# Timer
obstacle_timer = pygame.USEREVENT + 1
pygame.time.set_timer(obstacle_timer,1500)
# Background music
mixer.music.load('smb_stage_clear.wav')
mixer.music.play(0)
# -1 Makes the music loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if game_active:
if event.type == KEYDOWN:
if event.key == K_SPACE and markoSurface.bottom >= 955:
marko_gravity = -18
else:
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
game_active = True
start_time = int(pygame.time.get_ticks() / 1000)
if event.type == obstacle_timer and game_active:
if randint (0,2):
obstacle_rect_list.append(rat.get_rect(midbottom = (randint(2000,2200),955)))
else:
obstacle_rect_list.append(fly.get_rect(midbottom = (randint(2000,2200),855)))
if game_active:
# Draw
screen.blit(sewer, (0,0))
screen.blit(ground, (0,955))
marko_animation()
screen.blit(marko,markoSurface)
score = display_score()
# Key uses
key_use = pygame.key.get_pressed()
if key_use[K_LEFT]:
markoSurface.move_ip((-7,0))
if key_use[K_RIGHT]:
markoSurface.move_ip((7,0))
# Marko
marko_gravity += 1
markoSurface.y += marko_gravity
if markoSurface.bottom >= 955: markoSurface.bottom = 955
if markoSurface.left <= 0: markoSurface.left = 0
if markoSurface.right >= 1920: markoSurface.right = 1920
# Obstacle movement
obstacle_rect_list = obstacle_movement(obstacle_rect_list)
# Collision
game_active = collisions(markoSurface,obstacle_rect_list)
else: # Intro screen
screen.fill ("Light blue")
screen.blit(player_stand,player_stand_rect)
obstacle_rect_list.clear()
markoSurface.left = 80 # returns marko to 80
# Draws score message if score > 0
score_message = test_font.render(f'Your score: {score}',False,(red))
score_message_rect = score_message.get_rect(center = (950,650))
screen.blit(game_name,game_name_rect)
if score == 0: screen.blit(game_message,game_message_rect)
else:
screen.blit(score_message,score_message_rect)
screen.blit(game_over,game_overSurface)
screen.blit(game_message_start_again,game_message_start_again_rect)
pygame.display.update()
clock.tick(60)```
marko is a variable in global namespace. You must use the global statement to change a variable in the global namespace within a function:
def marko_animation():
global marko # <---
global marko_index
if markoSurface.bottom < 955:
marko = marko_jump
else:
marko_index += 0.1
if marko_index >= len(marko_run):
marko_index = 0
marko = marko_run[int(marko_index)]

TypeError: start_new_game() missing 1 required positional argument: 'max_health' [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
So I have been trying to figure out why I keep getting this error, even though everything is run from my game_view.py module. Everytime I press the 1 key to start a game and test to see if the players health bar and hit points works, I get this error message. TypeError: start_new_game() missing 1 required positional argument: 'max_health'
The sad part is, is that it reffers me to the start_view which doesn't have anything to do with anything else but running the start screen so players can choose to play solo or with another player. Below is the game_view and start_view so you guys can see where I'm going wrong. I wanted to test this out so this way I can add other enemies, bosses and power-ups later, but running into this error for the past several days is hindering progress. Anyway, code for both will be below. Thanks in advance for any and all help.
start_view:
import arcade
from game_view import GameView
class StartView(arcade.View):
def on_show(self):
# This is run once when we switch to this view
arcade.set_background_color(arcade.csscolor.BLACK)
# Reset the viewport, necessary if we have a scrolling game
arcade.set_viewport(0, self.window.width, 0, self.window.height)
def on_draw(self):
# Draw this view
arcade.start_render()
line_height = 70
line_location = self.window.height - line_height * 2
arcade.draw_text("Space Defense Force",
self.window.width / 2,
line_location,
arcade.color.WHITE,
font_size=50,
anchor_x="center",
font_name="SF Atarian System")
line_location -= line_height
line_location -= line_height
arcade.draw_text("1 - Start One Player Game",
self.window.width / 2,
line_location,
arcade.color.WHITE,
font_size=20,
anchor_x="center",
font_name="SF Atarian System")
# if len(self.window.joysticks) > 1:
# color = arcade.color.WHITE
# else:
# color = arcade.color.GRAY
color = arcade.color.GRAY
line_location -= line_height
arcade.draw_text("2 - Start Two Player Game",
self.window.width / 2,
line_location,
color,
font_size=20,
anchor_x="center",
font_name="SF Atarian System")
line_location -= line_height
line_location -= line_height
color = arcade.color.WHITE
arcade.draw_text("Use joysticks to play, or arrow keys to move and number keys to fire.",
self.window.width / 2,
line_location,
color,
font_size=20,
anchor_x="center",
font_name="SF Atarian System")
def on_key_press(self, symbol: int, modifiers: int):
if symbol == arcade.key.KEY_1:
game_view = GameView()
game_view.start_new_game(1)
self.window.show_view(game_view)
elif symbol == arcade.key.KEY_2:
game_view = GameView()
game_view.start_new_game(2)
self.window.show_view(game_view)
game_view:
import random
import math
import arcade
from health import Health
from game_over_view import GameOverView
from typing import cast
from arcade.experimental.shadertoy import Shadertoy
from constants import *
from asteroid_sprite import AsteroidSprite
from ship_sprite import ShipSprite
from bullet import Bullet
from glow_line import GlowLine
from glow_ball import GlowBall
from explosion import ExplosionMaker
from glow_image_sprite import GlowImageSprite
from window import Window as window
class GameView(arcade.View):
# Main application class
def __init__(self):
super().__init__()
# Sprite lists
self.player_sprite_list = arcade.SpriteList()
self.asteroid_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
self.ship_life_list = arcade.SpriteList()
self.health_list = arcade.SpriteList()
# Sounds
self.laser_sound = arcade.load_sound(":resources:sounds/hurt5.wav")
self.hit_sound1 = arcade.load_sound(":resources:sounds/explosion1.wav")
self.hit_sound2 = arcade.load_sound(":resources:sounds/explosion2.wav")
self.hit_sound3 = arcade.load_sound(":resources:sounds/hit1.wav")
self.hit_sound4 = arcade.load_sound(":resources:sounds/hit2.wav")
self.dead_sound = arcade.load_sound(":resources:sounds/gameover2.wav")
self.glowball_shadertoy = Shadertoy.create_from_file(self.window.get_size(), "glow_ball.glsl")
self.glowline_shadertoy = Shadertoy.create_from_file(self.window.get_size(), "glow_line.glsl")
self.explosion_list = []
# for joystick in self.window.joysticks:
# joystick.push_handlers(self)
def start_new_game(self, player_count, max_health):
#Set up the game and initialize the variables
self.game_over = False
arcade.set_background_color(arcade.csscolor.BLACK)
# Sprite lists
self.player_sprite_list = arcade.SpriteList()
self.asteroid_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
self.ship_life_list = arcade.SpriteList()
self.health_list = arcade.SpriteList()
# if len(self.window.joysticks) > 0:
# joystick = self.window.joysticks[0]
# else:
# joystick = None
joystick = None
player_sprite = ShipSprite(":resources:images/space_shooter/playerShip1_orange.png",
SCALE,
joystick,
player_no=1,
player_count=player_count,
max_health=5)
self.player_sprite_list.append(player_sprite)
self.health_list.append(max_health)
if player_count > 1:
joystick = None
# if len(self.window.joysticks) > 1:
# joystick = self.window.joysticks[1]
# else:
# joystick = None
player_sprite = ShipSprite(":resources:images/space_shooter/playerShip1_green.png",
SCALE,
joystick,
player_no=2,
player_count=player_count,
max_health=5
)
self.player_sprite_list.append(player_sprite)
self.health_list.append(max_health)
# Set up the player
for player in self.player_sprite_list:
player.score = 0
player.lives = 3
# Set up the little icons that represent the player lives.
cur_pos = 10
for i in range(self.player_sprite_list[0].lives):
life = arcade.Sprite(":resources:images/space_shooter/playerLife1_orange.png", SCALE)
life.center_x = cur_pos + life.width
life.center_y = life.height
cur_pos += life.width
self.ship_life_list.append(life)
if len(self.player_sprite_list) > 1:
cur_pos = 100
for i in range(self.player_sprite_list[1].lives):
life = arcade.Sprite(":resources:images/space_shooter/playerLife1_green.png", SCALE)
life.center_x = cur_pos + life.width
life.center_y = life.height
cur_pos += life.width
self.ship_life_list.append(life)
# Make the asteroids
image_list = (":resources:images/space_shooter/meteorGrey_big1.png",
":resources:images/space_shooter/meteorGrey_big2.png",
":resources:images/space_shooter/meteorGrey_big3.png",
":resources:images/space_shooter/meteorGrey_big4.png")
for i in range(STARTING_ASTEROID_COUNT):
image_no = random.randrange(4)
enemy_sprite = AsteroidSprite(image_list[image_no], SCALE)
enemy_sprite.guid = "Asteroid"
enemy_sprite.center_y = random.randrange(BOTTOM_LIMIT, TOP_LIMIT)
enemy_sprite.center_x = random.randrange(LEFT_LIMIT, RIGHT_LIMIT)
enemy_sprite.change_x = random.random() * 2 - 1
enemy_sprite.change_y = random.random() * 2 - 1
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 4
self.asteroid_list.append(enemy_sprite)
def on_draw(self):
# Render the screen.
# This command has to happen before we start drawing
arcade.start_render()
# Draw all the sprites.
self.asteroid_list.draw()
self.ship_life_list.draw()
for bullet in self.bullet_list:
bullet.draw()
self.bullet_list.draw()
for explosion in self.explosion_list:
explosion.render()
self.player_sprite_list.draw()
self.health_list.draw()
# Put the text on the screen.
output = f"Player 1 Score: {self.player_sprite_list[0].score}"
arcade.draw_text(output, 10, 40, arcade.color.AMBER,
font_size=15,
font_name="Arcade")
if len(self.player_sprite_list) > 1:
output = f"Player 2 Score: {self.player_sprite_list[1].score}"
arcade.draw_text(output, 500, 40, arcade.color.AMBER,
font_size=15,
font_name="Arcade")
output = f"Asteroid Count: {len(self.asteroid_list)}"
arcade.draw_text(output, 10, 80, arcade.color.AMBER,
font_size=15,
font_name="Arcade")
for player in health_list:
player.draw_health_number()
player.draw_health_bar()
# def on_joybutton_press(self, joystick, button):
# # What player is this?
# if joystick == self.window.joysticks[0]:
# player_sprite = self.player_sprite_list[0]
# else:
# player_sprite = self.player_sprite_list[1]
# if player_sprite.player_no == 1:
# color = 255, 128, 128
# else:
# color = 128, 255, 128
# if button == 0:
# self.fire_circle(color, player_sprite, player_no=player_sprite.player_no)
# elif button == 1:
# self.fire_line(color, player_sprite, player_no=player_sprite.player_no)
# elif button == 2:
# bullet_sprite = GlowImageSprite(":resources:images/space_shooter/laserBlue01.png",
# SCALE,
# glowcolor=arcade.color.WHITE,
# shadertoy=self.glowball_shadertoy,
# player_no=player_sprite.player_no)
# self.set_bullet_vector(bullet_sprite, 10, player_sprite)
# arcade.play_sound(self.laser_sound)
def on_key_press(self, symbol, modifiers):
# Shoot if the player hit the space bar and we aren't respawning.
if symbol == arcade.key.LEFT:
self.player_sprite_list[0].change_angle = 3
elif symbol == arcade.key.RIGHT:
self.player_sprite_list[0].change_angle = -3
elif symbol == arcade.key.UP:
self.player_sprite_list[0].thrust = 0.15
elif symbol == arcade.key.DOWN:
self.player_sprite_list[0].thrust = -.2
elif symbol == arcade.key.KEY_1:
color = (255, 128, 128)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_2:
color = (128, 255, 128)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_3:
color = (128, 128, 255)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_4:
color = (255, 128, 255)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_5:
color = (255, 255, 255)
self.fire_line(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_6:
color = (64, 255, 64)
self.fire_line(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_7:
bullet_sprite = GlowImageSprite(":resources:images/space_shooter/laserBlue01.png",
SCALE,
glowcolor=arcade.color.WHITE,
shadertoy=self.glowball_shadertoy,
player_no=0)
self.set_bullet_vector(bullet_sprite, 13, self.player_sprite_list[0])
arcade.play_sound(self.laser_sound)
def fire_circle(self, bullet_color, player_sprite, player_no):
bullet_sprite = GlowBall(glowcolor=bullet_color,
radius=5,
shadertoy=self.glowball_shadertoy,
player_no=player_no)
self.set_bullet_vector(bullet_sprite, 5, player_sprite)
arcade.play_sound(self.laser_sound)
def fire_line(self, bullet_color, player_sprite, player_no):
bullet_sprite = GlowLine(glowcolor=bullet_color,
shadertoy=self.glowline_shadertoy,
player=player_sprite,
player_no=player_no)
self.set_bullet_vector(bullet_sprite, 13, player_sprite)
arcade.play_sound(self.laser_sound)
def set_bullet_vector(self, bullet_sprite, bullet_speed, player_sprite):
bullet_sprite.change_y = \
math.cos(math.radians(player_sprite.angle)) * bullet_speed
bullet_sprite.change_x = \
-math.sin(math.radians(player_sprite.angle)) \
* bullet_speed
bullet_sprite.center_x = player_sprite.center_x
bullet_sprite.center_y = player_sprite.center_y
self.bullet_list.append(bullet_sprite)
def on_key_release(self, symbol, modifiers):
# Called whenever a key is released
if symbol == arcade.key.LEFT:
self.player_sprite_list[0].change_angle = 0
elif symbol == arcade.key.RIGHT:
self.player_sprite_list[0].change_angle = 0
elif symbol == arcade.key.UP:
self.player_sprite_list[0].thrust = 0
elif symbol == arcade.key.DOWN:
self.player_sprite_list[0].thrust = 0
def split_asteroid(self, asteroid: AsteroidSprite):
# Split an asteroid into chunks
x = asteroid.center_x
y = asteroid.center_y
if asteroid.size == 4:
for i in range(3):
image_no = random.randrange(2)
image_list = [":resources:images/space_shooter/meteorGrey_med1.png",
":resources:images/space_shooter/meteorGrey_med2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 2.5 - 1.25
enemy_sprite.change_y = random.random() * 2.5 - 1.25
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 3
self.asteroid_list.append(enemy_sprite)
self.hit_sound1.play()
elif asteroid.size == 3:
for i in range(3):
image_no = random.randrange(2)
image_list = [":resources:images/space_shooter/meteorGrey_small1.png",
":resources:images/space_shooter/meteorGrey_small2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 3 - 1.5
enemy_sprite.change_y = random.random() * 3 - 1.5
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 2
self.asteroid_list.append(enemy_sprite)
self.hit_sound2.play()
elif asteroid.size == 2:
for i in range(3):
image_no = random.randrange(2)
image_list = [":resources:images/space_shooter/meteorGrey_tiny1.png",
":resources:images/space_shooter/meteorGrey_tiny2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 3.5 - 1.75
enemy_sprite.change_y = random.random() * 3.5 - 1.75
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 1
self.asteroid_list.append(enemy_sprite)
self.hit_sound3.play()
elif asteroid.size == 1:
self.hit_sound4.play()
def on_update(self, x, delta_time):
# Move everything
self.asteroid_list.update()
self.bullet_list.update()
self.player_sprite_list.update()
self.health_list.update()
explosion_list_copy = self.explosion_list.copy()
for explosion in explosion_list_copy:
explosion.update(x)
if explosion.time > .9:
self.explosion_list.remove(explosion)
for bullet in self.bullet_list:
hit_list = arcade.check_for_collision_with_list(bullet, self.player_sprite_list)
# If it did hit, get rid of sprite
if len(hit_list) > 0:
bullet.remove_from_lists()
for player in hit_list:
if not isinstance(player, ShipSprite):
raise TypeError("List contents must be all ints")
# Remove one health point
player.cur_health -= 1
# Check Health
if player.cur_health <= 0:
arcade.play_sound(self.dead_sound)
view=GameOverView
self.window.show_view(view)
else:
# Not Dead
arcade.play_sound(self.hit_sound1)
assert isinstance(bullet, Bullet)
asteroids = arcade.check_for_collision_with_list(bullet, self.asteroid_list)
if len(asteroids) > 0:
explosion = ExplosionMaker(self.window.get_size(), bullet.position)
self.explosion_list.append(explosion)
for asteroid in asteroids:
assert isinstance(asteroid, AsteroidSprite)
self.player_sprite_list[bullet.player_no - 1].score += 1
self.split_asteroid(cast(AsteroidSprite, asteroid)) # expected AsteroidSprite, got Sprite instead
asteroid.remove_from_sprite_lists()
bullet.remove_from_sprite_lists()
# Remove bullet if it goes off-screen
size = max(bullet.width, bullet.height)
if bullet.center_x < 0 - size:
bullet.remove_from_sprite_lists()
if bullet.center_x > SCREEN_WIDTH + size:
bullet.remove_from_sprite_lists()
if bullet.center_y < 0 - size:
bullet.remove_from_sprite_lists()
if bullet.center_y > SCREEN_HEIGHT + size:
bullet.remove_from_sprite_lists()
for player in self.player_sprite_list:
assert isinstance(player, ShipSprite, max_health)
if not player.respawning:
asteroids = arcade.check_for_collision_with_list(player, self.asteroid_list)
if len(asteroids) > 0:
if player.lives > 0:
player.lives -= 1
player.respawn()
self.split_asteroid(cast(AsteroidSprite, asteroids[0]))
asteroids[0].remove_from_sprite_lists()
self.ship_life_list.pop().remove_from_sprite_lists()
elif len(asteroids) > 0:
if player.health > 0:
player.health -=1
player.respawn()
self.split_asteroid(cast(AsteroidSprite, asteroids[0]))
asteroids[0].remove_from_sprite_lists()
self.ship_list_list.pop().remove_from_sprite_lists()
else:
arcade.play_sound(self.dead_sound)
view = GameOverView()
self.window.show_view(view)
Sorry in advance if I have tons of code to go through. But like I said I am adding some features that wasn't in the Arcade Repository from github. I even set a separte module for the health bar and hit points so this way I can see what conflicts. But the error above has been a royal pain.Thanks again.
P.S. Remarked out joysticks because it was conflicting with another module I built.
As your title says, your start game function requires 2 arguments: player_count and max_healh. And in your code, when using start_new_game, you aren't supplying the max_health argument, thus raising an error.
(function) start_new_game(self, player_count, max_health)

How can I retrieve coordinates of a pymunk.Circle?

I'm new to python so please forgive me if I'm misnaming pygame objects. I've been tasked to build a virtual Pachinko gaming machine. I'm having trouble getting the coordinates of the ball as it falls through the window. I need the coordinates to keep track of score and reset the loops so the user can drop another ball once it bottoms out.
Here's my source code.
#Project specific libraries
import pygame #https://www.pygame.org/news
import pymunk #http://www.pymunk.org/en/latest/
import pymunk.util
import pymunk.pygame_util
import tkinter #https://docs.python.org/2/library/tkinter.html
#Standard libraries
import sys
import math
import random
import os
import time
#Import ALL tools from tkinter & pygame libraies
from tkinter import *
from pygame import *
#Constants for object to object interaction
COLLTYPE_FLOOR = 3
COLLTYPE_BOUNCER = 2
COLLTYPE_BALL = 1
#****************************************************************************
def goal_reached(arbiter, space1, data):
ball_i, floor_i = arbiter.shapes
space_i = space1
space_i.remove(ball_i, ball_i.body)
remove_from_ball_list(ball_i)
return True
#*************************************************************
main = Tk()
main.resizable(width=False, height=False)
main.title("Pachinko")
embed = Frame(main, width = 500, height = 500)
embed.pack() #packs window to the left
os.environ['SDL_WINDOWID'] = str(embed.winfo_id())
screen = pygame.display.set_mode((500,500))
screen.fill(pygame.Color(255,215,255))
clock = pygame.time.Clock()
#List of ball "objects"
balls = []
#might not need balls_to_remove List
balls_to_remove = []
#velocity, gravity
space = pymunk.Space()
space.gravity = 0, -200
#Floor boundaries
floor = pymunk.Segment(space.static_body, (0.0, 10.0), (500.0, 10.0), 1.0)
floor.collision_type = COLLTYPE_FLOOR
space.add(floor)
#Left wall boundaries
left_wall = pymunk.Segment(space.static_body, (0.0, 500.0), (0.0, 0.0), 1.0)
left_wall.friction = 1.0
left_wall.elasticity = 0.9
left_wall.collision_type = COLLTYPE_BOUNCER
space.add(left_wall)
#Right wall boundaries
right_wall = pymunk.Segment(space.static_body, (500.0, 500.0), (500.0, 0.0), 1.0)
right_wall.friction = 1.0
right_wall.elasticity = 0.9
right_wall.collision_type = COLLTYPE_BOUNCER
space.add(right_wall)
draw_options = pymunk.pygame_util.DrawOptions(screen)
space.debug_draw(draw_options)
#Generate a fixed field of pins
done = 0
x_shift = 45
y_shift = 150
step = 0
tier = 0
while(done == 0):
variance = random.randint(1, 15)
pin_radius = random.randint(14, 17)
newPin = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
x = x_shift + variance
y = y_shift + variance
newPin.position = x, y
shape = pymunk.Circle(newPin, pin_radius)
shape.collision_type = COLLTYPE_BOUNCER
space.add(newPin, shape)
x_shift += 85
step += 1
if(step == 5): #Tier one
x_shift = 100
y_shift += 60
if(step == 10): #Tier two
x_shift = 50
y_shift += 60
if(step == 15): #Tier three
x_shift = 100
y_shift += 60
if(step == 20): #Tier four
x_shift =50
y_shift += 60
if(step == 25): #Tier five
x_shift = 100
y_shift += 60
done = 1
#Generate the five poles (left to right)
step = 0
x_shift = 100
while(step < 4):
pole0 = pymunk.Segment(space.static_body, (x_shift, 100.0), (x_shift, 10.0), 5.0)
pole0.friction = 1.0
pole0.elasticity = 0.9
pole0.collision_type = COLLTYPE_BOUNCER
space.add(pole0)
step += 1
x_shift += 100
pygame.display.flip()
pygame.display.init()
pygame.display.update()
#https://stackoverflow.com/questions/23410161/pygame-collision-code
h = space.add_collision_handler(COLLTYPE_BALL, COLLTYPE_FLOOR)
h.begin = goal_reached
def remove_from_ball_list(temp1):
#print("where in list is it?")
for ball in balls:
if temp1 == ball:
#print("Time to remove from the list")
balls.remove(ball)
#Primary game loop
ticks = 50
play = True
while play == True:
mouseClick = pygame.event.get()
dropHeight = 440
for event in mouseClick:
if event.type == pygame.MOUSEBUTTONUP:
mouseX, mouseY = pygame.mouse.get_pos()
ticks = 0
#keep making new balls & pins
if ticks == 0:
step = 0
x_shift = 0
y_shift = 0
#Generate the new ball
mass = 1
inertia = pymunk.moment_for_circle(mass, 0, 14, (0, 0))
radius = 12
ball = pymunk.Body(mass, inertia)
#Keep the ball in bounds when user drops
if(mouseX < 25):
mouseX = 10
if(mouseX > 480):
mouseX = 490
ball.position = mouseX, dropHeight
shape = pymunk.Circle(ball, radius)
shape.collision_type = COLLTYPE_BALL
space.add(ball, shape)
balls.append(shape)
ticks = 50
pygame.draw.line(screen, (255,0,255), (20,60), (480,60), 2)
space.step(1/50.0)
space.debug_draw(draw_options)
pygame.display.update()
pygame.display.flip()
screen.fill(pygame.Color(255,215,255))
clock.tick(50)
#ticks -= 1
main.update()
And here is the specific piece of code I'd like to add the feature:
#Primary game loop
ticks = 50
play = True
while play == True:
mouseClick = pygame.event.get()
dropHeight = 440
for event in mouseClick:
if event.type == pygame.MOUSEBUTTONUP:
mouseX, mouseY = pygame.mouse.get_pos()
ticks = 0
#keep making new balls & pins
if ticks == 0:
step = 0
x_shift = 0
y_shift = 0
#Generate the new ball
mass = 1
inertia = pymunk.moment_for_circle(mass, 0, 14, (0, 0))
radius = 12
ball = pymunk.Body(mass, inertia)
#Keep the ball in bounds when user drops
if(mouseX < 25):
mouseX = 10
if(mouseX > 480):
mouseX = 490
ball.position = mouseX, dropHeight
shape = pymunk.Circle(ball, radius)
shape.collision_type = COLLTYPE_BALL
space.add(ball, shape)
balls.append(shape)
ticks = 50
pygame.draw.line(screen, (255,0,255), (20,60), (480,60), 2)
space.step(1/50.0)
space.debug_draw(draw_options)
pygame.display.update()
pygame.display.flip()
screen.fill(pygame.Color(255,215,255))
clock.tick(50)
#ticks -= 1
main.update()
I would like to print the ball coordinates to the terminal so I can add scoring and restart features. I feel like that I'm missing a very simple solution.
It seems like the balls are collected in a list, balls. So you can just loop through it to get all the info you need. For example, if you put the below code in the while loop it will print the location of each ball each frame.
for b in balls:
print("ball position", b.body.position)
However, maybe you do not need to check this yourself each frame? You already have a goal_reached function, that is called once the ball reached the goal if I understand it correctly. In there you can update the score and toggle something to allow the player to add another ball.

Jumping creates a distance between man and the floor Pygame [duplicate]

This question already has answers here:
How to simulate Jumping in Pygame for this particular code
(1 answer)
jumping too fast?
(1 answer)
How to make a circular object jump using pygame? [duplicate]
(2 answers)
Pygame Bouncy Ball Sinks Through Floor
(1 answer)
Closed 2 years ago.
The full code:
import glob
import pygame
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 40)
class Man(pygame.sprite.Sprite):
# just taking random image to get height and width after trans scale to be able to crop later (see lines 23/36)
idle_images_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Firing"+'\*')
random_image = pygame.image.load(idle_images_list[0])
new_img_width = int(random_image.get_width()/2.5)
new_img_height = int(random_image.get_height()/2.5)
random_image_after_crop = random_image.subsurface((70, 35, new_img_width-125, new_img_height-45))
# to be able to use needed images indexes
# getting idle animations list
right_idle_list = []
left_idle_list = []
for images in idle_images_list:
img = pygame.image.load(images)
wid = img.get_width()
hei = img.get_height()
img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5)))
img = img.subsurface((70, 35, new_img_width-125, new_img_height-45))
right_idle_list.append(img)
left_idle_list.append(pygame.transform.flip(img, True, False))
# getting movement animations list
walk_animation_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Run Firing"+'\*')
walk_right_list = []
walk_left_list = []
for files in walk_animation_list: # setting the animation list
img = pygame.image.load(files)
wid = img.get_width()
hei = img.get_height()
img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5))) # chaging scale
img = img.subsurface((70, 35, new_img_width-125, new_img_height-45))
walk_right_list.append(img)
walk_left_list.append(pygame.transform.flip(img, True, False)) # mirror list
def __init__(self, x, y,):
super(Man, self).__init__()
self.walk_left_list = Man.walk_left_list
self.walk_right_list = Man.walk_right_list
self.width = Man.new_img_width
self.height = Man.new_img_height
self.hitbox = (x+55, y+35, self.width-125, self.height-45) # nothing special on those num, just Trial and error
self.rect = Man.random_image_after_crop.get_rect()
self.rect.x = x
self.rect.y = y
def game_redraw(): # print things in end for main loop
global right
global left
screen.blit(bg_image, (0, 0))
if right:
screen.blit(man.walk_right_list[frame_count//3], (man.rect.x, man.rect.y))
elif left:
screen.blit(man.walk_left_list[frame_count//3], (man.rect.x, man.rect.y))
else:
if last_action == "right":
screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y))
if last_action == "left":
screen.blit(man.left_idle_list[frame_count//13], (man.rect.x, man.rect.y))
else: # just for the first move
screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y))
pygame.draw.rect(screen, RED, man.rect, 4)
pygame.display.flip()
right = False
left = False
def input_process(key):
global right
global left
global last_action
global man
global frame_count
global is_jump
global neg
global jump_count
#
if is_jump:
if jump_count >= -10: # check if jumping
if jump_count < 0:
neg = -1
man.rect.y -= (jump_count ** 2) * neg * 0.5
jump_count -= 1
else:
neg = 1
is_jump = False
#
if key[pygame.K_RIGHT] and man.rect.right + speed < w:
if left:
frame_count = 0
right = True
left = False
last_action = "right"
man.rect.x += speed
#
if key[pygame.K_LEFT] and man.rect.left + speed > 0:
if right:
frame_count = 0
right = False
left = True
last_action = "left"
man.rect.x -= speed
#
if not is_jump:
if key[pygame.K_UP]:
jump_count = 10
is_jump = True
w = 1728
h = 972
pygame.init()
RED = (255, 0, 0)
images_folder = r'C:\Users\aviro\Desktop\מחשבים\python projects\platform game\images'+'\\'
bg_image = images_folder+"background.png" # setting background image
ground_height = h-143 # the high of the image ground
man = Man(200, ground_height)
man.rect.bottom = ground_height
clock = pygame.time.Clock()
Refresh_Rate = 54
speed = 5
right = False
left = False
frame_count = 0
finish = False
last_action = ""
# jumping ver
is_jump = False
jump_count = 10
neg = 1
#
screen = pygame.display.set_mode((w, h))
bg_image = pygame.image.load(bg_image).convert() # can do convert only after setting surface.
# main loop
while not finish:
if frame_count >= 51:
frame_count = 0
for events in pygame.event.get():
if events.type == pygame.QUIT:
finish = True
key = pygame.key.get_pressed()
input_process(key)
game_redraw()
frame_count += 1
clock.tick(Refresh_Rate)
I'm trying to create a simple shooting-platform game, using pygame module. Everything works fine except the jumping. See in line 88. The player is jumping like an x^2 parabola. I added the *0.5 at the end of the line to make the player jump slower and lower, but when I do it this is what happened.
Before jumping:
After jumping:
Look in the second picture. There's a distance between the floor and the player. When I remove the *0.5 everything works fine. Why?

Pygame. How to make a rect change direction on collision (boundary check)

Part of an assignment I'm working on is making a ball bounce around the screen, I can make it move, but my boundary test doesn't seem to be working: the ball simply moves in direction instead of changing direction. So to clarify, what I want to ball to do is change direction as it hits the screen edge.
import sys
import pygame
SCREEN_SIZE = 750, 550
BALL_DIAMETER = 16
BALL_RADIUS = BALL_DIAMETER // 2
MAX_BALL_X = SCREEN_SIZE[0] - BALL_DIAMETER
MAX_BALL_Y = SCREEN_SIZE[1] - BALL_DIAMETER
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
LEFT = 11
RIGHT = 12
pygame.init()
clock = pygame.time.Clock()
pygame.display.init()
font = pygame.font.SysFont("impact", 20)
pygame.display.set_caption("Breakout")
screen = pygame.display.set_mode(SCREEN_SIZE)
class Ball:
def __init__(self):
''' '''
self.ball = pygame.Rect(300, 730 -
BALL_DIAMETER,
BALL_DIAMETER, BALL_DIAMETER)
# Draw ball
def draw_ball(self):
pygame.draw.circle(screen,
WHITE, (self.ball.left
+ BALL_RADIUS, self.ball.top +
BALL_RADIUS), BALL_RADIUS)
# Updates the coordinates by adding the speed components
def move_ball(self, x, y):
self.xspeed = x
self.yspeed = y
self.ball = self.ball.move(self.xspeed, self.yspeed)
# bounds check
if self.ball.left <= 0:
self.ball.left = 0
self.xspeed = -self.xspeed
elif self.ball.left >= MAX_BALL_X:
self.ball.left = MAX_BALL_X
self.xspeed = -self.xspeed
if self.ball.top < 0:
self.ball.top = 0
self.yspeed = -self.yspeed
elif self.ball.top >= MAX_BALL_Y:
self.ball.top = MAX_BALL_Y
self.yspeed = -self.yspeed
# shows a message on screen, for testing purposes
class Text:
def show_message(self, message):
self.font = pygame.font.SysFont("impact", 20)
font = self.font.render(message,False, WHITE)
screen.blit(font, (200, 400))
class Game:
def __init__(self):
''' '''
def run(self):
b = Ball()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
# fps lock, screen fill and method call for input
clock.tick(60)
screen.fill(BLACK)
b.draw_ball()
b.move_ball(5, -5)
# used to keep track of various elements
# Text().show_message("P: " + str(p))
pygame.display.flip()
# Creates instance of the game class, and runs it
if __name__ == "__main__":
Game().run()
Your only call to move_ball uses a constant vector.
Since you never change the call parameters, the ball moves only that way.
b.move_ball(5, -5)
Yes, you change the vector components within move_ball when you hit a wall. However, on the next call, you change them back to the original values and move the ball in the original direction.
You have to initialize the vector outside move_ball, and then let the routine access the existing vector when it's called.

Categories