I've been working on this simple shooter game in python turtle for a few days and I've run into a error that I cannot find an answer to.
What I want to happen is every 5 kills the player gets, an extra enemy appears. But when I get 5 kills, infinite error messages start flooding in.
The error message is: isvisible() missing 1 required positional argument: 'self'
It points towards my enemy_attack function where I used the isvisible() command to test if the enemy had been shot or not in order to respawn it from another random position. Thanks for any help in advance.
I apologise for the lack of comments.
import turtle
import time
import random
from random import randint
game_over = False
kill_counter = 0
enemy_spawn_number = 5
def close():
turtle.bye()
speed = 10
enemy_speed = 0.5
lives = 3
wn = turtle.Screen()
wn.title("Defend.")
wn.bgcolor("black")
wn.setup(width=600, height=600)
wn.tracer(0)
player = turtle.Turtle()
player.speed(0)
player.shape("triangle")
player.color("white")
player.penup()
player.goto(0,0)
life1 = turtle.Turtle()
life1.speed(0)
life1.shape("triangle")
life1.color("yellow")
life1.penup()
life1.goto(265,-260)
life1.setheading(-90)
life2 = life1.clone()
life2.goto(240,-260)
life3 = life2.clone()
life3.goto(215,-260)
over_message = turtle.Turtle()
over_message.speed(0)
over_message.shape("square")
over_message.color("white")
over_message.penup()
over_message.goto(0,0)
over_message.ht()
killcount = over_message.clone()
killcount.goto(250,250)
killcount.ht()
killcount.write("Kill Count: 0", align="right", font=("Courier", 15, "normal"))
def go_up():
player.setheading(90)
player.forward(speed)
def go_right():
player.setheading(0)
player.forward(speed)
def go_left():
player.setheading(180)
player.forward(speed)
def go_down():
player.setheading(-90)
player.forward(speed)
wn.listen()
wn.onkeypress(go_up, "w")
wn.onkeypress(go_right, "d")
wn.onkeypress(go_left, "a")
wn.onkeypress(go_down, "s")
wn.onkeypress(close, "Escape")
collectible = turtle.Turtle()
collectible.speed(0)
collectible.shape("square")
collectible.color("blue")
collectible.penup()
collectible.goto(0,100)
bullet = turtle.Turtle()
bullet.shape("square")
bullet.color("grey")
bullet.shapesize(0.5,1)
bullet.hideturtle()
bullet.penup()
bullet.goto(500,500)
shooting = False
def shoot():
global shooting
if bullet.isvisible() == False:
player_x = player.xcor()
player_y = player.ycor()
player_facing = player.heading()
bullet.goto(player_x,player_y)
bullet.setheading(player_facing)
shooting = True
bullet.showturtle()
enemies = []
enemies.append(turtle.Turtle())
for enemy in enemies:
enemy.speed(0)
enemy.shape("circle")
enemy.color("red")
enemy.penup()
enemy.goto(0,350)
def enemy_attack():
global game_over
for enemy in enemies:
if enemy.isvisible() == True:
enemy.setheading(enemy.towards(player))
enemy.forward(enemy_speed)
wn.ontimer(enemy_attack, 10)
else:
if game_over == False:
rand_direction = randint(0,3)
if rand_direction == 0:
x = randint(-240, 200)
enemy.goto(x,450)
enemy.st()
enemy_attack()
elif rand_direction == 1:
x = randint(-240, 200)
enemy.goto(x,-450)
enemy.st()
enemy_attack()
elif rand_direction == 2:
y = randint(-210, 210)
enemy.goto(-450,y)
enemy.st()
enemy_attack()
elif rand_direction == 3:
y = randint(-210, 210)
enemy.goto(450,y)
enemy.st()
enemy_attack()
enemy_attack()
wn.onkey(shoot, "space")
while True:
wn.update()
if player.distance(collectible) < 20:
x = randint(-290, 290)
y = randint(-290, 290)
collectible.goto(x,y)
if bullet.xcor()>310 or bullet.xcor()<-310 or bullet.ycor()>310 or bullet.ycor()<-300:
shooting = False
bullet.hideturtle()
if shooting == True:
bullet.forward(0.2)
if player.xcor()>285:
playerX = player.xcor()
playerX = playerX-5
playerY = player.ycor()
player.goto(playerX,playerY)
elif player.xcor()<-285:
playerX = player.xcor()
playerX = playerX+5
playerY = player.ycor()
player.goto(playerX,playerY)
elif player.ycor()>285:
playerY = player.ycor()
playerY = playerY-5
playerX = player.xcor()
player.goto(playerX,playerY)
elif player.ycor()<-285:
playerY = player.ycor()
playerY = playerY+5
playerX = player.xcor()
player.goto(playerX,playerY)
for enemy in enemies:
if bullet.distance(enemy) < 20:
bullet.hideturtle()
enemy.hideturtle()
enemy.goto(0,350)
kill_counter = kill_counter+1
killcount.clear()
killcount.write("Kill Count: {}".format(kill_counter), align="right", font=("Courier", 15, "normal"))
if kill_counter == enemy_spawn_number:
enemies.append(turtle.Turtle)
for enemy in enemies:
enemy.speed(0)
enemy.shape("circle")
enemy.color("red")
enemy.penup()
enemy.goto(0,350)
enemy_spawn_number += 5
if enemy.distance(player) < 20:
enemy.ht()
player.goto(0,0)
lives = lives-1
if lives == 2:
life3.ht()
elif lives == 1:
life2.ht()
elif lives == 0:
life1.ht()
time.sleep(0.1)
if lives == 0:
game_over = True
collectible.ht()
player.ht()
for enemy in enemies:
enemy.ht()
over_message.write("Game Over! Press Esc to exit.", align="center", font=("Courier", 24, "normal"))```
The major problems I see are those that #furas refers to: enemy_attack() invokes itself both recursively and via a timer; turtle.Turtle is invoked without parentheses. I don't think invoking enemy_attack() either way is a good idea and instead it should be invoked in the main program loop.
One problem with using ontimer() is that you need to make sure the timeout you use is greater than the time it takes to execute the function. Unless it's designed to be running multiple instances of itself at the same time. Given it writes a global, then it probably should be only one at a time.
You shouldn't have while True: (nor time.sleep()) in an event-based environment like turtle. The ontimer() method should handle both.
Python-wise, you shouldn't do:
if bullet.isvisible() == False:
if enemy.isvisible() == True:
if game_over == False:
if shooting == True:
Instead do:
if not bullet.isvisible():
if enemy.isvisible():
if not game_over:
if shooting:
Below, I've taken apart your code and reassembled it the way I expect a turtle-based game to be designed. There may be bugs still, but it seems to be playable now:
from turtle import Screen, Turtle
from random import randint
ENEMY_SPEED = 1
PLAYER_SPEED = 4
BULLET_SPEED = 6
SMALL_FONT = ('Courier', 15, 'normal')
LARGE_FONT = ('Courier', 24, 'normal')
def go_up():
player.setheading(90)
player.forward(PLAYER_SPEED)
def go_right():
player.setheading(0)
player.forward(PLAYER_SPEED)
def go_left():
player.setheading(180)
player.forward(PLAYER_SPEED)
def go_down():
player.setheading(-90)
player.forward(PLAYER_SPEED)
def enemy_attack():
for enemy in enemies:
if enemy.isvisible():
enemy.setheading(enemy.towards(player))
enemy.forward(ENEMY_SPEED)
else:
rand_direction = randint(0, 3)
if rand_direction == 0:
x = randint(-240, 200)
enemy.goto(x, 325)
elif rand_direction == 1:
x = randint(-240, 200)
enemy.goto(x, -325)
elif rand_direction == 2:
y = randint(-210, 210)
enemy.goto(-325, y)
elif rand_direction == 3:
y = randint(-210, 210)
enemy.goto(325, y)
enemy.showturtle()
def shoot():
if not bullet.isvisible():
bullet.goto(player.position())
bullet.setheading(player.heading())
bullet.showturtle()
kill_counter = 0
enemy_spawn_number = 5
lives = 3
def move():
global kill_counter, enemy_spawn_number, lives
enemy_attack()
if player.distance(collectible) < 20:
collectible.goto(randint(-260, 260), randint(-260, 260))
if bullet.isvisible():
if not (-310 < bullet.xcor() < 310 and -310 < bullet.ycor() < 310):
bullet.hideturtle()
else:
bullet.forward(BULLET_SPEED)
playerX, playerY = player.position()
if playerX > 285:
player.setx(playerX - 5)
elif playerX < -285:
player.setx(playerX + 5)
elif playerY > 285:
player.sety(playerY - 5)
elif playerY < -285:
player.sety(playerY + 5)
for enemy in enemies:
if bullet.isvisible() and bullet.distance(enemy) < 15:
bullet.hideturtle()
enemy.hideturtle()
enemy.sety(325)
kill_counter += 1
killcount.clear()
killcount.write("Kill Count: {}".format(kill_counter), align='right', font=SMALL_FONT)
if kill_counter == enemy_spawn_number:
enemy = enemy_prototype.clone()
enemy.showturtle()
enemies.append(enemy)
for enemy in enemies:
enemy.sety(325)
enemy_spawn_number += 5
if enemy.distance(player) < 20:
enemy.hideturtle()
player.goto(0, 0)
lives -= 1
if lives == 2:
life3.hideturtle()
elif lives == 1:
life2.hideturtle()
elif lives == 0:
life1.hideturtle()
if lives == 0:
collectible.hideturtle()
player.hideturtle()
for enemy in enemies:
enemy.hideturtle()
over_message.write("Game Over! Press Esc to exit.", align='center', font=LARGE_FONT)
else:
screen.ontimer(move, 70)
screen.update()
screen = Screen()
screen.setup(width=600, height=600)
screen.title("Defend.")
screen.bgcolor('black')
screen.tracer(0)
player = Turtle()
player.shape('triangle')
player.color('white')
player.penup()
life1 = Turtle()
life1.shape('triangle')
life1.color('yellow')
life1.penup()
life1.goto(265, -260)
life1.setheading(-90)
life2 = life1.clone()
life2.goto(240, -260)
life3 = life2.clone()
life3.goto(215, -260)
over_message = Turtle()
over_message.hideturtle()
over_message.color('white')
over_message.penup()
killcount = over_message.clone()
killcount.goto(270, 270)
killcount.write("Kill Count: 0", align='right', font=SMALL_FONT)
collectible = Turtle()
collectible.shape('square')
collectible.color('blue')
collectible.penup()
collectible.goto(randint(-260, 260), randint(-260, 260))
bullet = Turtle()
bullet.hideturtle()
bullet.shape('square')
bullet.shapesize(0.5, 1)
bullet.color('grey')
bullet.penup()
bullet.goto(500, 500)
enemy_prototype = Turtle()
enemy_prototype.hideturtle()
enemy_prototype.shape('circle')
enemy_prototype.color('red')
enemy_prototype.penup()
enemy_prototype.sety(325)
enemy = enemy_prototype.clone()
enemy.showturtle()
enemies = [enemy]
screen.onkeypress(go_up, 'w')
screen.onkeypress(go_right, 'd')
screen.onkeypress(go_left, 'a')
screen.onkeypress(go_down, 's')
screen.onkeypress(screen.bye, 'Escape')
screen.onkeypress(shoot, 'space')
screen.listen()
move()
screen.mainloop()
The next issue I believe you should address are all the number constants in the code that prevent you from resizing the screen:
life1.goto(265,-260)
life2.goto(240,-260)
life3.goto(215,-260)
killcount.goto(250,250)
x = randint(-240, 200)
x = randint(-240, 200)
y = randint(-210, 210)
y = randint(-210, 210)
x = randint(-290, 290)
y = randint(-290, 290)
if bullet.xcor()>310 or bullet.xcor()<-310 or bullet.ycor()>310 or bullet.ycor()<-300:
if player.xcor()>285:
elif player.xcor()<-285:
elif player.ycor()>285:
elif player.ycor()<-285:
These should all be defined relative to the screen size:
screen.setup(width=600, height=600)
and the objects they manipulate. (I.e. no large numbers in the code!)
I run code and it worked most time without problem.
At some moment it got error
Traceback (most recent call last):
File "/home/furas/main.py", line 198, in <module>
enemy.speed(0)
File "/usr/lib/python3.7/turtle.py", line 2167, in speed
return self._speed
AttributeError: 'int' object has no attribute '_speed'
and I start thinking why enemy (which is turtle) doesn't have _speed ? Why it treats it as integer? All enemy are on list enemies so I start checking what you add to this list and I found that you forgot () in one of Turtle()
enemies.append(turtle.Turtle)
so it added class Turtle instead of instance of Turle.
After adding () I don't have this error but it seems there is other problem because after 5 killed enemies it freezes. It would need some debugging (or use of print()) to find what is a problem.
EDIT: I have the same problem if I add two enemies at start. Problem can be that it may run two times wn.ontimer which may run another two wn.ontimer so finally it may have no time to execute them.
EDIT: Code works better if I uses only wn.ontimer(enemy_attack, 10) at the end of function and remove other enemy_attack(). But this time enemies shows mostly at the same time and when I kill one of them then it remove all of them. It may need more changes.
def enemy_attack():
global game_over
for enemy in enemies:
if enemy.isvisible() == True:
enemy.setheading(enemy.towards(player))
enemy.forward(enemy_speed)
else:
if game_over == False:
rand_direction = randint(0,3)
if rand_direction == 0:
x = randint(-240, 200)
enemy.goto(x,450)
enemy.st()
#enemy_attack()
elif rand_direction == 1:
x = randint(-240, 200)
enemy.goto(x,-450)
enemy.st()
#enemy_attack()
elif rand_direction == 2:
y = randint(-210, 210)
enemy.goto(-450,y)
enemy.st()
#enemy_attack()
elif rand_direction == 3:
y = randint(-210, 210)
enemy.goto(450,y)
enemy.st()
#enemy_attack()
wn.ontimer(enemy_attack, 10)
Related
I'm making a game in pygame and I'm having some trouble with object collisions.
import pygame, sys
from pygame.math import Vector2
pygame.init()
screen = pygame.display.set_mode((500,500))
clock = pygame.time.Clock()
#Environment Variables
gravity = -1
jumpForce = 20
moveSpeed = 10
#Game Objects
playerPos = Vector2(230,230)
playerVelo = Vector2(0,0)
player = pygame.Rect(playerPos.x,playerPos.y, 20, 20)
boxPos = Vector2(350,480)
box = pygame.Rect(boxPos.x, boxPos.y, 20,20)
def Clamp(var, minClamp, maxClamp):
if minClamp > maxClamp:
raise Exception("minClamp must be less than maxClamp")
if var < minClamp:
var = minClamp
if var > maxClamp:
var = maxClamp
return var
def Draw():
global player,box
screen.fill((255,255,25))
player = pygame.Rect(playerPos.x,playerPos.y, 20, 20)
pygame.draw.rect(screen,(0,100,255),player)
box = pygame.Rect(boxPos.x, boxPos.y, 20,20)
pygame.draw.rect(screen,(10,200,20),box)
def PlayerVelocity():
global playerPos,playerVelo,player,box,boxPos
if player.colliderect(box):
playerPos = Vector2(boxPos.x,boxPos.y-20)
print("balls")
if playerPos.y < 499:
playerVelo.y += gravity
#if not pygame.Rect(playerPos.x+playerVelo.x,playerPos.y+playerVelo.y,20,20).colliderect(box):
playerPos -= playerVelo
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE or event.key == pygame.K_UP:
playerVelo.y = jumpForce
print(playerVelo)
keys = pygame.key.get_pressed()
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
playerVelo.x = 1*moveSpeed
elif keys[pygame.K_d] or keys[pygame.K_RIGHT]:
playerVelo.x = -1*moveSpeed
else:
playerVelo.x = 0
#Draw things to the screen
Draw()
PlayerVelocity()
playerPos.x = Clamp(playerPos.x, 0, 480)
playerPos.y = Clamp(playerPos.y,0,480)
pygame.display.update()
clock.tick(30)
I've tried looking this up and the other solutions either don't work with the movement system I've implemented or I just plain don't understand them.
It is not enough to change the position of the player when the player hits the obstacle. You need to constrain the player's box (pygame.Rect object) with the obstacle rectangle and update the player's position. Clamp and calculate the position of the player before checking for collision:
def PlayerVelocity():
global playerPos,playerVelo,player,box,boxPos
prev_y = playerPos.y
if playerPos.y < 499:
playerVelo.y += gravity
playerPos -= playerVelo
playerPos.x = Clamp(playerPos.x, 0, 480)
playerPos.y = Clamp(playerPos.y, 0, 480)
player = pygame.Rect(playerPos.x, playerPos.y, 20, 20)
box = pygame.Rect(boxPos.x, boxPos.y, 20,20)
if player.colliderect(box):
if prev_y+20 <= box.top:
player.bottom = box.top
elif player.left < box.left:
player.right = box.left
else:
player.left = box.right
playerPos = Vector2(player.topleft)
print("balls")
I'm trying to create an easier version of the snake game. Everything in the code looks ok for me but I can't make snake move as I want to. Can you help me why my code doesn't work? I don't understand why my code is not okay.
I searched for some similar games codes, but they all used time. and I couldn't understand the need for that.
Here is the code:
import turtle
import random
window = turtle.Screen()
window.screensize(600, 600)
window.title("Snake Eats Tomato Game")
window.bgcolor("skyblue")
window.tracer(0)
snake = turtle.Turtle()
snake.color("dark blue")
snake.shape("square")
snake.shapesize(1)
snake.speed(1)
snake.penup()
snake.goto(0, 100)
snake.direction = "stop"
def move():
if snake.direction == "up":
y = snake.ycor()
snake.sety(y + 20)
if snake.direction == "down":
y = snake.ycor()
snake.sety(y - 20)
if snake.direction == "left":
x = snake.xcor()
snake.setx(x + 20)
if snake.direction == "right":
x = snake.xcor()
snake.setx(x - 20)
point = 0
point_table = turtle.Turtle()
point_table.speed(0)
point_table.shape("square")
point_table.color("green")
point_table.penup()
point_table.hideturtle()
point_table.goto(-200, 200)
point_table.write(
"POİNT: {}".format(point), align="center", font=("Courier", 25, "normal")
)
def go_left():
if snake.direction != "right":
snake.direction = "left"
def go_right():
if snake.direction != "left":
snake.direction = "right"
def go_up():
if snake.direction != "down":
snake.direction = "up"
def go_down():
if snake.direction != "up":
snake.direction = "down"
window.listen()
window.onkey(go_left, "Left")
window.onkey(go_right, "Right")
window.onkey(go_up, "Up")
window.onkey(go_down, "Down")
tomato = turtle.Turtle()
tomato.penup()
tomato.color("tomato")
tomato.shape("circle")
tomato.speed(0)
tomato.setposition(random.randint(-300, 300), random.randint(-300, 300))
while True:
window.update()
snake.forward(3)
move()
if snake.xcor() < -330 or snake.xcor() > 330:
snake.right(90)
if snake.ycor() < -330 or snake.ycor() > 330:
snake.right(90)
if snake.distance(tomato) < 20:
point += 1
point_table.clear()
point_table.write(
"PUAN: {}".format(point), align="center", font=("Courier", 25, "normal")
)
tomato.setposition(random.randint(-300, 300), random.randint(-300, 300))
There were some 'errors' in your code, I'll try to adres most of them, one by one:
Instead of always calling snake.forward(3) in the while True, change snake.direction = "stop" to snake.direction = "right" as the starting movement. This will keep the while loop clean
The go_left (etc) functions had an if to check the reversed way, I've removed those, just overwrite the direction
snake.xcor() < -330 these won't work, not every screen is 330 pixels. You'll need to use window.window_width() and window.window_height() to get the current window size, and use those values to detect the edges
WHen you detect an edge, don't do snake.right(90), as this will instant move the snake. Just change the direction, to bounce it:
if (curX < window_max_left):
snake.direction = 'left'
if (curX > window_max_right):
snake.direction = 'right'
if (curY > window_max_up):
snake.direction = 'down'
if (curY < window_max_down):
snake.direction = 'up'
Applying those points, fixes the movement, and let the snake bounce of walls
* Please see the example recording below the code
The final code looks like:
import turtle
import random
window = turtle.Screen()
window.screensize(600, 600)
window.title("Snake Eats Tomato Game")
window.bgcolor("skyblue")
window.tracer(0)
snake = turtle.Turtle()
snake.color("dark blue")
snake.shape("square")
snake.shapesize(1)
snake.speed(1)
snake.penup()
snake.goto(0, 100)
snake.direction = "right"
def move():
if snake.direction == "up":
y = snake.ycor()
snake.sety(y + 10)
if snake.direction == "down":
y = snake.ycor()
snake.sety(y - 10)
if snake.direction == "left":
x = snake.xcor()
snake.setx(x + 10)
if snake.direction == "right":
x = snake.xcor()
snake.setx(x - 10)
point = 0
point_table = turtle.Turtle()
point_table.speed(0)
point_table.shape("square")
point_table.color("green")
point_table.penup()
point_table.hideturtle()
point_table.goto(-200, 200)
point_table.write(
"POİNT: {}".format(point), align="center", font=("Courier", 25, "normal")
)
def go_left():
snake.direction = "left"
def go_right():
snake.direction = "right"
def go_up():
snake.direction = "up"
def go_down():
snake.direction = "down"
window.listen()
window.onkey(go_left, "Left")
window.onkey(go_right, "Right")
window.onkey(go_up, "Up")
window.onkey(go_down, "Down")
tomato = turtle.Turtle()
tomato.penup()
tomato.color("tomato")
tomato.shape("circle")
tomato.speed(0)
tomato.setposition(random.randint(-300, 300), random.randint(-300, 300))
window_width = window.window_width()
window_height = window.window_height()
window_max_left = -abs(window_width / 2)
window_max_right = window_width / 2
window_max_down = -abs(window_height / 2)
window_max_up = window_height / 2
while True:
curX = snake.xcor()
curY = snake.ycor()
if (curX < window_max_left):
snake.direction = 'left'
if (curX > window_max_right):
snake.direction = 'right'
if (curY > window_max_up):
snake.direction = 'down'
if (curY < window_max_down):
snake.direction = 'up'
if snake.distance(tomato) < 20:
point += 1
point_table.clear()
point_table.write(
"PUAN: {}".format(point), align="center", font=("Courier", 25, "normal")
)
tomato.setposition(random.randint(-300, 300), random.randint(-300, 300))
move()
window.update()
How it looked:
I'm a beginner and have needed a lot of help to get this far. I'm trying to add more enemies when my score reaches certain values such as 10, 20, etc. Ive tried something along the lines of if score_value >= 10: num_of_enemies = num_of_enemies + 10 but have been failing to produce results. I feel like it should be simple but i am missing how to add values to this list.
import pygame
import random
import math
from pygame import mixer
# initialize game
pygame.init()
# create screen, set height and weight (())
screen = pygame.display.set_mode((800, 600))
# Background
background = pygame.image.load('background1.png')
pygame.display.set_icon(background)
# Background sound
mixer.music.load('troubador.wav')
mixer.music.play(-1)
# Title and Icon
pygame.display.set_caption("Beasts of Cthulu")
icon = pygame.image.load('knight.png')
pygame.display.set_icon(icon)
# Player
playerImg = pygame.image.load('wizard.png')
playerX = 370
playerY = 500
playerX_change = 0
# Enemy
enemyImg = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 5
max_enemies = 100
for i in range(num_of_enemies):
enemyImg.append(pygame.image.load('cthulhu.png'))
enemyX.append(random.randint(0, 735))
enemyY.append(random.randint(10, 150))
enemyX_change.append(3)
enemyY_change.append(40)
# Fireball
# ready - you cannot see fireball on screen
# fire - fireball is currently moving
fireballImg = pygame.image.load('fireball.png')
fireballX = 0
fireballY = 370
fireballX_change = 0
fireballY_change = 8
fireball_state = "ready"
# Score
score_value = 0
previous_score = score_value
font = pygame.font.Font('Enchanted_Land.otf', 32)
textX = 10
textY = 10
# Game over text
over_font = pygame.font.Font('Enchanted_Land.otf', 64)
def show_score(x, y):
score = font.render("Score :" + str(score_value), True, (255, 255, 255))
screen.blit(score, (x, y))
def game_over_text():
over_text = over_font.render("GAME OVER", True, (255, 255, 255))
screen.blit(over_text, (250, 200))
def player(x, y):
screen.blit(playerImg, (x, y))
def enemy(x, y, i):
screen.blit(enemyImg[i], (x, y))
def fire_fireball(x, y):
global fireball_state
fireball_state = "fire"
screen.blit(fireballImg, (x + 16, y + 10))
def isCollision(enemyX, enemyY, fireballX, fireballY):
distance = math.sqrt(math.pow(enemyX - fireballX, 2) + (math.pow(enemyY - fireballY, 2)))
if distance < 27:
return True
else:
return False
# Game Loop
running = True
while running:
screen.fill((0, 0, 0))
# Background image
screen.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Keyboard actions
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -4
if event.key == pygame.K_RIGHT:
playerX_change = 4
if event.key == pygame.K_SPACE:
if fireball_state == "ready":
fireball_Sound = mixer.Sound('fireball-1.wav')
fireball_Sound.play()
fireballX = playerX
fire_fireball(fireballX, fireballY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# checking for boundaries
playerX += playerX_change
if playerX <= 0:
playerX = 0
elif playerX >= 736:
playerX = 736
# Fireball movement
if fireballY <= 0:
fireballY = 480
fireball_state = "ready"
if fireball_state == "fire":
fire_fireball(fireballX, fireballY)
fireballY -= fireballY_change
# Enemy movement
enemyX += enemyX_change
for i in range(num_of_enemies):
# Game Over
if enemyY[1] > 370:
for j in range(num_of_enemies):
enemyY[j] = 2000
game_over_text()
break
enemyX[i] += enemyX_change[i]
if enemyX[i] <= 0:
enemyX_change[i] = 3
enemyY[i] += enemyY_change[i]
elif enemyX[i] >= 736:
enemyX_change[i] = -3
enemyY[i] += enemyY_change[i]
# Collision
collision = isCollision(enemyX[i], enemyY[i], fireballX, fireballY)
if collision:
explosion_Sound = mixer.Sound('fireball-explosion.wav')
explosion_Sound.play()
fireballY = 480
fireball_state = "ready"
score_value += 1
enemyX[i] = random.randint(0, 735)
enemyY[i] = random.randint(10, 150)
enemy(enemyX[i], enemyY[i], i)
player(playerX, playerY)
show_score(textX, textY)
pygame.display.update()
That's a lot of code and we can't run it, because we don't have the assets. What you need is a threshold, which increases once it was reached.
You can implement it like this:
import random
threshold = 10
score = 0
while score < 1000:
score += random.randint(1,4) # wherever points come from
print (f"Score {score}")
if score > threshold:
print (f"You have more than {threshold}. Adding enemies ...")
threshold += 10
See how the score can increase but enemies will only be added every 10 points and it needn't hit the value exactly.
The idea of #ThomasWeller is nice (+1). However, you will have difficulty adding it to your code.
Write a function that adds an enemy and use the function in the loop that creates the initial enemies:
enemySurf = pygame.image.load('cthulhu.png')
def addNewEnemy():
enemyImg.append(enemySurf)
enemyX.append(random.randint(0, 735))
enemyY.append(random.randint(10, 150))
enemyX_change.append(3)
enemyY_change.append(40)
for i in range(num_of_enemies):
addNewEnemy()
Use the idea of #ThomasWeller and add a new enemy when the scooter reaches a certain threshold:
threshold = 10
running = True
while running:
if score_value >= threshold:
addNewEnemy()
num_of_enemies += 1
threshold += 10
print(len(enemyX))
# [...]
I'm fairly new to programming, game programming especially. I'm trying to make pong using pygame, but have run into a slight issue. Essentially, the ball hits a paddle, stops, and then keeps going once the paddle is out of the way. Obviously I want the ball to bounce back, but I can't figure out why it won't, when I've coded (what I thought was) the appropriate logic for ball-paddle collisions. Here's my code:
# importing stuff
import sys, pygame
from pygame.locals import *
# starting pygame
pygame.init()
# defining basic colours
white = (255, 255, 255)
black = (0, 0, 0)
# set up the clock
clock = pygame.time.Clock()
# text and such
tFont = pygame.font.SysFont("monospace", 15)
# setting window res and setting display
winX, winY = 600, 300
window = pygame.display.set_mode((winX, winY))
# setting the speed for on screen stuff
playSpeed = 5 # player speed (p1 and p2)
# counts points for each player
# 1 2
points = [0, 0]
# tallies number of times FPS is counted and added to toal amount
fpsCount = 0
fpsTotal = 0
class Ball(object):
def __init__(self, speed):
# set default ball position in screen centre
self.ballX = winX / 2
self.ballY = winY / 2
self.ballSpeed = speed
def move(self):
if points[0] > points[1]: # if p1 has more points than p2
self.ballX -= self.ballSpeed # ball goes to p1's side
elif points[0] < points[1]: # only other condition could be p2 having more points
self.ballX += self.ballSpeed # ball goes to p2's side
elif points[0] == points[1]: # if points are equal
self.ballX -= self.ballSpeed # favour player 1 (change later)
pygame.draw.circle(window, black, (self.ballX, self.ballY), 3)
def collide(self, paddle): # unsure if 'paddle' necessary
# if ball hits top of paddle, bounce to top
# if hits bottom, bounce to bottom
# if ball hits midsection, bounce straight (middle could be about 10px?)
if paddle == playerOne:
self.ballX
self.ballX += self.ballSpeed
class Paddle(object):
# set the player number (1/2) and if it's an AI or real player
def __init__(self, player, aiornot):
if player == 1 and aiornot == False:
self.coords = [[40, 130], [40, 160]]
elif player == 2 and aiornot == False:
self.coords = [[560, 130], [560, 160]]
self.movement = 'stop' # sets default movement
def move(self):
if self.movement == 'down' and self.coords[1][1] < 300:
self.moveDown()
elif self.movement == 'up' and self.coords[0][1] > 0:
self.moveUp()
elif self.movement == 'stop':
self.stop()
# draw the paddle in new position
pygame.draw.line(window, black, self.coords[0], self.coords[1], 10)
# movement functions, for direction and such
def moveDown(self):
self.coords[0][1] += playSpeed
self.coords[1][1] += playSpeed
def moveUp(self):
self.coords[0][1] -= playSpeed
self.coords[1][1] -= playSpeed
def stop(self):
self.coords[0][1] = self.coords[0][1]
self.coords[1][1] = self.coords[1][1]
ball = Ball(playSpeed)
playerOne = Paddle(1, False)
playerTwo = Paddle(2, False)
# main loop
while True:
# event handling for exit
for event in pygame.event.get():
if event.type == QUIT:
# print the average FPS
print round(fpsTotal / fpsCount, 2), "fps (avg)"
pygame.quit()
sys.exit()
# setting direction upon arrow key press
elif event.type == KEYDOWN:
if event.key == K_DOWN:
playerOne.movement = 'down'
elif event.key == K_UP:
playerOne.movement = 'up'
elif event.key == K_s:
playerTwo.movement = 'down'
elif event.key == K_w:
playerTwo.movement = 'up'
# when the key is released, stop moving
elif event.type == KEYUP:
if event.key == K_DOWN or event.key == K_UP:
playerOne.movement = 'stop'
elif event.key == K_s or event.key == K_w:
playerTwo.movement = 'stop'
print "player1:", playerOne.coords
print "player2:", playerTwo.coords
# this is a mess... if the balls x coords = the paddles x coords, and the balls y
# coord is somewhere between the start and end point of the paddle, then do the balls
# collision function on the paddle
if ball.ballX >= playerOne.coords[0][0] and ball.ballX <= playerOne.coords[1][0]: # or ball.ballX == 40
if ball.ballY >= playerOne.coords[0][1] and ball.ballY <= playerOne.coords[1][1]:
ball.collide(playerOne)
print ball.ballX, ball.ballY
# fill the window
window.fill(white)
# redraw the bat with new position
playerOne.move()
playerTwo.move()
# redraw the ball with new position
ball.move()
# set FPS to 60
clock.tick(60)
# for working out average FPS
fpsCount += 1
fpsTotal += clock.get_fps()
# set window title
pygame.display.set_caption("Long Pong")
# render FPS to text, display text
text = tFont.render(str(round(clock.get_fps(), 2)), 8, black)
window.blit(text, (545, 5))
# update display
pygame.display.update()
And also a pastebin here if it's easier to look at/copy.
I appreciate any help with this, I've been able to work out any other problem on my own, but I can't tell what I'm missing here.
I hope the below code helps. Although my program was a bit different because every time the ball hit the paddle we had to generate a new ball.
import random
from livewires import games, color
games.init(screen_width = 640, screen_height = 480, fps = 50)
class Ball(games.Sprite):
quit_label = games.Text(value = "Press Q to Quit", size = 25, color = color.white, top = 5, right = 130,
is_collideable = False)
games.screen.add(quit_label)
def update(self):
if self.right > games.screen.width:
self.dx = -self.dx
if self.left < 0:
self.game_over()
if self.bottom > games.screen.height or self.top < 0:
self.dy = -self.dy
#pressing 'q' quits the game
if games.keyboard.is_pressed(games.K_q):
self.game_over()
def bounce(self):
self.dx = -self.dx
def game_over(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 = 3 * games.screen.fps,
after_death = games.screen.quit,
is_collideable = False)
games.screen.add(end_message)
self.destroy()
class Paddle(games.Sprite):
image = games.load_image("paddle.bmp")
score = games.Text(value = 0, size = 25, color = color.white, top = 15,
right = games.screen.width - 10, is_collideable = False)
games.screen.add(score)
def __init__(self):
super(Paddle, self).__init__(image = Paddle.image, x = games.mouse.x, bottom = games.screen.height)
def update(self):
""" Move to mouse y position. """
self.y = games.mouse.y
if self.left > 0:
self.left = 10
if self.right > games.screen.height:
self.right = games.screen.height
self.check_catch()
def check_catch(self):
for ball in self.overlapping_sprites:
Paddle.score.value += 1
ball_image2 = games.load_image("ball.bmp")
ball2 = Ball(image = ball_image2,
x = games.screen.width/2,
y = games.screen.height/2,
dx = 1,
dy = 1)
games.screen.add(ball2)
ball.bounce()
def main():
wall_image = games.load_image("background.bmp", transparent = False)
games.screen.background = wall_image
ball_image = games.load_image("ball.bmp")
the_ball = Ball(image = ball_image,
x = games.screen.width/2,
y = games.screen.height/2,
dx = 1,
dy = 1)
games.screen.add(the_ball)
the_paddle = Paddle()
games.screen.add(the_paddle)
games.mouse.is_visible = False
games.screen.mainloop()
# kick it off!
main()
I am trying to add in random drops from enemy deaths to a game I am making in Python and wondering how to implement it. the drops I am wanting to add currently are shield and health, with shield having a lower drop chance. The main code for Drops are here:
import pygame
class HealthDrop(pygame.sprite.Sprite):
def __init__(self, x, y):
self.image = pygame.image.load('images/Sprites/health.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.on_ground = False
self.gravity = 0.5
def update(self):
def render(self, surface):
surface.blit(self.image, self.rect)
class ShieldDrop(pygame.sprite.Sprite):
def __init__(self, x, y):
self.image = pygame.image.load('images/Sprites/shield.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.on_ground = False
self.gravity = 0.5
def update(self):
def render(self, surface):
surface.blit(self.image, self.rect)
Then the code for the main file is here:
import pygame, sys
import random
import pygame.mixer
import Funk
from time import sleep
from player import *
from zombie import *
from level import *
from bullet import *
from constants import *
from Drops import *
import menu as dm
class Game():
def __init__(self):
pygame.init()
pygame.mixer.init()
#pygame.mixer.music.load('sounds/menugame.ogg')
#pygame.mixer.music.play(-1)
# A few variables
self.gravity = .50
self.ground = pygame.Rect(0, 640, 1280, 80)
self.red = (255, 0, 0)
self.darkred = (200, 0, 0)
self.darkblue = (0, 0, 200)
self.darkgreen = (0, 200, 0)
self.gameover = pygame.image.load('images/gameover.png')
self.victory = pygame.image.load('images/victory.png')
# Bullets
self.bullets = []
# Screen
size = (1280, 720)
self.screen = pygame.display.set_mode(size)
pygame.display.set_caption('Moon Survival!')
# Moon / Background
self.moon = Background()
self.text1 = pygame.image.load('images/TextSlides/Text1.jpg')
self.text2 = pygame.image.load('images/TextSlides/Text2.jpg')
# Zombies
self.zombies = []
for i in range(15):
self.zombies.append( Zombie(random.randint(0,1280), random.randint(0,720)) )
self.zombieskilled = 0
# Player
self.player = Player(25, 320, self.gravity)
# Font for text
self.font = pygame.font.SysFont(None, 72)
# game over
self.gameover_text = self.font.render("The Aliens Are Too Good", -1, (255, 0, 0))
self.gameover_rect = self.gameover_text.get_rect(center=self.screen.get_rect().center)
# game state
self.game_state = STATE_INGAME
def run(self):
clock = pygame.time.Clock()
# "state machine"
RUNNING = True
PAUSED = False
GAME_OVER = False
# Game loop
while RUNNING:
# (all) Events
if self.game_state == STATE_INGAME:
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_s:
self.bullets.append(Bullet(self.player.rect.x + 30, self.player.rect.y + 30, self.player.direction))
if event.key == pygame.K_ESCAPE:
RUNNING = False
elif event.key == pygame.K_p:
# set state to paused
self.game_state = STATE_PAUSED
# Player/Zomies events
self.player.handle_events(event)
# (all) Movements / Updates
self.player_move()
self.player.update()
for z in self.zombies:
self.zombie_move(z)
z.update(self.screen.get_rect())
for b in self.bullets:
b.update()
for tile in self.moon.get_surrounding_blocks(b):
if tile is not None:
if pygame.sprite.collide_rect(b, tile):
# Destroy block
x = tile.rect.x / tile.rect.width
y = tile.rect.y / tile.rect.height
self.moon.levelStructure[x][y] = None
self.bullets.remove(b)
# (all) Display updating
self.moon.render(self.screen)
for z in self.zombies:
z.render(self.screen)
for b in self.bullets:
b.render(self.screen)
self.player.render(self.screen)
Funk.text_to_screen(self.screen, 'Level 1', 5, 675)
Funk.text_to_screen(self.screen, 'Health: {0}'.format(self.player.health), 5, 0)
Funk.text_to_screen(self.screen, 'Score: {0}'.format(self.player.score), 400, 0)
Funk.text_to_screen(self.screen, 'Time: {0}'.format(self.player.alivetime), 750, 0)
Funk.text_to_screen(self.screen, 'Kills: {0}'.format(self.zombieskilled), 5, 50)
Funk.text_to_screen(self.screen, 'Lives: {0}'.format(self.player.lives), 300, 50)
elif self.game_state == STATE_PAUSED:
# (all) Display updating
if self.game_state == STATE_INGAME:
if event.type == pygame.QUIT:
RUNNING = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
RUNNING = False
choose = dm.dumbmenu(self.screen, [
'Resume Game',
'Menu',
'Quit Game'], 200, 200,'orecrusherexpanded',100,0.75,self.darkred,self.red)
if choose == 0:
print "You choose 'Start Game'."
# set state to ingame
self.game_state = STATE_INGAME
elif choose == 1:
print "You choose 'Controls'."
if choose == 2:
print "You choose 'Quit Game'."
pygame.quit()
sys.exit()
#for event in pygame.event.get():
self.moon.render(self.screen)
for z in self.zombies:
z.render(self.screen)
for b in self.bullets:
b.render(self.screen)
self.player.render(self.screen)
Funk.text_to_screen(self.screen, 'Level 1', 5, 675)
Funk.text_to_screen(self.screen, 'Health: {0}'.format(self.player.health), 5, 0)
Funk.text_to_screen(self.screen, 'Score: {0}'.format(self.player.score), 400, 0)
Funk.text_to_screen(self.screen, 'Time: {0}'.format(self.player.alivetime), 750, 0)
Funk.text_to_screen(self.screen, 'Kills: {0}'.format(self.zombieskilled), 850, 0)
elif self.game_state == STATE_GAMEOVER:
self.screen.blit(self.gameover, (0, 0))
pygame.display.update()
choose = dm.dumbmenu(self.screen, [
'New Game',
' Menu ',
'Quit Game'], 200, 300,'orecrusherexpanded',100,0.75,self.darkred,self.red)
if choose == 0:
print "You choose 'Start Game'."
# set state to ingame
self.game_state = STATE_INGAME
execfile('MoonSurvival.py')
if choose == 1:
print "You choose 'Start Game'."
execfile('run_game.py')
if choose == 2:
print "You choose 'Start Game'."
pygame.quit()
sys.exit()
pygame.display.update()
# FTP
clock.tick(100)
# --- the end ---
pygame.quit()
def player_move(self):
# add gravity
self.player.do_jump()
# simulate gravity
self.player.on_ground = False
if not self.player.on_ground and not self.player.jumping:
self.player.velY = 4
# Health
for zombie in self.zombies:
if pygame.sprite.collide_rect(self.player, zombie):
self.player.health -= 5
# check if we die
if self.player.health <= 0:
self.player.lives -= 1
self.player.rect.x = 320
self.player.rect.y = 320
self.player.health += 200
if self.player.lives <= 0:
sleep(2)
self.game_state = STATE_GAMEOVER
# move player and check for collision at the same time
self.player.rect.x += self.player.velX
self.check_collision(self.player, self.player.velX, 0)
self.player.rect.y += self.player.velY
self.check_collision(self.player, 0, self.player.velY)
def zombie_move(self, zombie_sprite):
# add gravity
zombie_sprite.do_jump()
# simualte gravity
zombie_sprite.on_ground = False
if not zombie_sprite.on_ground and not zombie_sprite.jumping:
zombie_sprite.velY = 4
# Zombie damage
for zombie in self.zombies:
for b in self.bullets:
if pygame.sprite.collide_rect(b, zombie):
#The same bullet cannot be used to kill
#multiple zombies and as the bullet was
#no longer in Bullet.List error was raised
zombie.health -= 10
self.bullets.remove(b)
if zombie.health <= 0:
self.zombieskilled += 1
self.player.score += 20
self.zombies.remove(zombie)
break
# move zombie and check for collision
zombie_sprite.rect.x += zombie_sprite.velX
self.check_collision(zombie_sprite, zombie_sprite.velX, 0)
zombie_sprite.rect.y += zombie_sprite.velY
self.check_collision(zombie_sprite, 0, zombie_sprite.velY)
def check_collision(self, sprite, x_vel, y_vel):
# for every tile in Background.levelStructure, check for collision
for block in self.moon.get_surrounding_blocks(sprite):
if block is not None:
if pygame.sprite.collide_rect(sprite, block):
# we've collided! now we must move the collided sprite a step back
if x_vel < 0:
sprite.rect.x = block.rect.x + block.rect.w
if type(sprite) is Zombie:
# the sprite is a zombie, let's make it jump
if not sprite.jumping:
sprite.jumping = True
sprite.on_ground = False
if x_vel > 0:
sprite.rect.x = block.rect.x - sprite.rect.w
if type(sprite) is Zombie:
# the sprite is a zombie, let's make it jump
if not sprite.jumping:
sprite.jumping = True
sprite.on_ground = False
if y_vel < 0:
sprite.rect.y = block.rect.y + block.rect.h
if y_vel > 0 and not sprite.on_ground:
sprite.on_ground = True
sprite.rect.y = block.rect.y - sprite.rect.h
#---------------------------------------------------------------------
Game().run()
you need to edit this:
def zombie_move(self, zombie_sprite):
for zombie in self.zombies:
for b in self.bullets:
if pygame.sprite.collide_rect(b, zombie):
zombie.health -= 10
self.bullets.remove(b)
if zombie.health <= 0:
self.zombieskilled += 1
self.player.score += 20
#You need some code here (before removing the zombie)
self.zombies.remove(zombie)
break
I'm sorry I forgot how to do it in Python, but the logic is like that: In place of the comment inside the code add something like HealthDrop.append(x, y) or ShieldDrop.append(x, y) where x and y should be zombie's values (that's why you should do it before removing the zombie).
If you want random chance just add import random then do it like that:
percentage = random.randint(1, 100)
if (percentage >= 1) and (percentage < 10)
healthDrop.append(zombie.x, zombie.y)
else
if (percentage >= 10) and (percentage < 20)
shieldDrop.append(zombie.x, zombie.y)
In this example I set 10% for each "item" to drop (they can't both drop), it randomizes a number from 1 to 100 , if it's a number from 1 to 9 its healthDrop, if it's a number from 10 to 19 its shieldDrop, feel free to experiment with what percentages makes your game balanced
Also don't forget to add collision (I see you already have some code for collision so I guess you know how to do it). The rest of the code should be easy for you to do (like increasing health on pick up etc etc.
I'm sorry I don't remember python really well, but you can use them similar to the bullet class, I hope you have the idea, if there's anything troubling you please tell me and I'll help more :)