Related
I'll try to explain this to my best ability. Short story short, making a tic tac toe game for a school project. I have all the code necessary i think, but when i run my code, tkinter window opens no problem, but when i am to click in one of the squares where i can place a "x" or a "o" my image wont load into the grid.
When i click, this is one the errors that pops up:
line 2776, in _create
return self.tk.getint(self.tk.call(
_tkinter.TclError: unknown option "562.5"
If i was to guess, the error must be in both "def draw_o" and "def draw_x"
If you want to try the code here is the images to download: https://imgur.com/a/kKJPIop
If relevant image of assignment: https://imgur.com/a/3iu4MJy
from math import log
from tkinter import *
import numpy as np
size = 600
symbol_size = (size / 3 - size / 8) /2
symbol_thick = 50
symbol_x_color = '#EE4035'
symbol_o_color = '#0492CF'
green = '#7BC043'
class cell:
def __init__(self):
self.window = Tk()
self.window.title("Bondesjakk/Tic Tac Toe")
self.canvas = Canvas(self.window, width = size, height = size)
self.canvas.pack()
self.window.bind("<Button-1>", self.click)
self.initialize_board()
self.player_x_turns = True
self.board_status = np.zeros(shape=(3, 3) )
self.player_x_starts = True
self.reset_board = False
self.gameover = False
self.tie = False
self.x_wins = False
self.o_wins = False
self.x_score = False
self.o_score = False
self.tie_score = False
def mainloop(self):
self.window.mainloop()
def initialize_board(self):
for i in range(2):
self.canvas.create_line( (i + 1) * size / 3, 0, (i + 1) * size / 3, size)
for i in range(2):
self.canvas.create_line(0, (i + 1) * size / 3, size, (i + 1) * size / 3)
def play_again(self):
self.initialize_board()
self.player_x_starts = not self.player_x_starts
self.player_x_turns = self.player_x_starts
self.board_status = np.zeros(shape =(3, 3) )
def draw_o(self, logical_pos):
o_image = PhotoImage('book/pybook/image/o.gif')
logical_pos = np.array(logical_pos)
grid_pos = self.convert_logical_to_grid_pos(logical_pos)
self.canvas.create_image(grid_pos[0] - symbol_size, grid_pos[1] - symbol_size,
grid_pos[0] + symbol_size, grid_pos[1] + symbol_size, width = symbol_thick,
image = o_image)
def draw_x(self, logical_pos):
x_image = PhotoImage('book/pybook/image/x.gif')
grid_pos = self.convert_logical_to_grid_pos(logical_pos)
self.canvas.create_image(grid_pos[0] - symbol_size, grid_pos[1] - symbol_size,
grid_pos[0] + symbol_size, grid_pos[1] + symbol_size, width = symbol_thick,
image = x_image)
def display_gameover(self):
if self.x_wins:
self.x_score += 1
text = "Winner: Player 1(x)"
color = symbol_x_color
elif self.o_wins:
self.o_score += 1
text = "Winner: Player 2(o)"
color = symbol_o_color
else:
self.tie_score += 1
text = "It's a tie"
color = "gray"
self.canvas.delete("all")
self.canvas.create_text(size / 2, size / 3, font = "cmr 60 bold", fill = color, text = text)
score_text = 'Scores \n'
self.canvas.create_text(size / 2, 5 * size / 8, font = "cmr 40 bold", fill = green, text = score_text)
score_text = 'Player 1 (x) : ' + str(self.x_score) + '\n'
score_text = 'Player 2 (o) : ' + str(self.o_score) + '\n'
score_text = 'Tie : ' + str(self.tie_score) + '\n'
self.canvas.create_text(size / 2, 3 * size / 4, font = "cmr 30 bold", fill = green, text = score_text)
self.reset_board = True
score_text = "Click to play again \n"
self.canvas.create_text(size / 2, 15* size / 16, font = "cmr 20 bold", fill = "gray", text = score_text)
def convert_logical_to_grid_pos(self, logical_pos):
logical_pos = np.array(logical_pos, dtype = int)
return (size / 3) * logical_pos + size / 6
def convert_grid_to_logical_pos(self, grid_pos):
grid_pos = np.array(grid_pos)
return np.array(grid_pos // (size / 3), dtype = int)
def is_grid_occupied(self, logical_pos):
if self.board_status[logical_pos[0] ][logical_pos[1] ] == 0:
return False
else:
return True
def is_winner(self, player):
player = -1 if player == "x" else 1
#Three in a row
for i in range(3):
if self.board_status[i][1] == self.board_status[i][2] == player:
return True
if self.board_status[0][1] == self.board_status[2][i] == player:
return True
#Diagonals
if self.board_status[0][0] == self.board_status[1][1] == self.board_status[2][2] == player:
return True
if self.board_status[2][0] == self.board_status[1][1] == self.board_status[0][2] == player:
return True
return False
def is_tie(self):
r, c = np.where(self.board_status == 0)
tie = False
if len(r) == 0:
tie = True
return tie
def is_gameover(self):
self.x_wins = self.is_winner("x")
if not self.x_wins:
self.o_wins = self.is_winner("o")
if not self.o_wins:
self.tie = self.is_tie()
gameover = self.x.wins or self.o.wins or self.tie
if self.x_wins:
print("x wins!")
if self.o_wins:
print("o wins!")
if self.tie:
print("It's a tie!")
return gameover
def click(self, event):
grid_pos = [event.x, event.y]
logical_pos = self.convert_grid_to_logical_pos(grid_pos)
if not self.reset_board:
if self.player_x_turns:
if not self.is_grid_occupied(logical_pos):
self.draw_x(logical_pos)
self.board_status[logical_pos[0] ][logical_pos[1] ] = -1
self.player_x_turns = not self.player_x_turns
else:
if not self.is_grid_occupied(logical_pos):
self.draw_o(logical_pos)
self.board_status[logical_pos[0] ][logical_pos[1] ] = 1
self.player_x_turns = not self.player_x_turns
if self.gameover():
self.display_gameover()
else:
self.canvas.delete("all")
self.play_again()
self.reset_board = False
game_instance = cell()
game_instance.mainloop()
In create_image() you use x1, y1, x2, y2, width=... but it expects only position (x1,y1) as tuple (without x2,y2) and without width=...
x = grid_pos[0] - symbol_size
y = grid_pos[1] - symbol_size
self.canvas.create_image( (x, y), image=x_image)
EDIT:
You have other mistakes
In line if self.gameover() you have to remove ()
--
If you use PhotoImage('book/pybook/image/o.gif') then it treats it as string with image's data, not as path to file. You have to use file=
o_image = PhotoImage(file='book/pybook/image/o.gif')
--
But there is another problem. There is bug in PhotoImage which removes image from memory when it is assigned to local variable in function. You should assign to class variable using self.
self.o_image = PhotoImage(file='book/pybook/image/o.gif')
--
And here another problem. You load the same image again and again and you assign it to the same variable - so bug removes previous image from memory and it removes it from canvas.
You could load it only once in __init__ and later use all time the same self.o_image, self.x_image
def __init___(...):
self.o_image = PhotoImage(file='book/pybook/image/o.gif')
self.x_image = PhotoImage(file='book/pybook/image/x.gif')
Doc (effbot.org in archive.org): PhotoImage, Canvas
I'm making a game with pygame and pymunk as a physics engine. I'm trying to kill a bullet whenever it hits a player or goes past its lifetime.
When I tried to space.remove(self.shape) and the second bullet hits the player, it gives me an "AssertionError: shape not in space, already removed. I simply changed it to teleport the bullets away, and then learned of the real error.
When I have more than one bullet in the space and a bullet hits the enemy player, all the current bullets teleport away, which means that when I tried to remove one bullet, it called the remove on all the bullets and thats why I had the initial error.
However the problem still remains that one bullet is being treated as every bullet.
Why is something that should be a non-static variable being called as a static variable?
I even tried to use deepcopy to see if that fixed it, but to no avail
This is my chunk of code, apologies since I don't know what is needed to understand it.
The key parts are most likely the Bullet class, the shoot() function in the Player class, and the drawBulletCollision() function
# PyGame template.
# Import modules.
import sys, random, math, time, copy
from typing import List
import pygame
from pygame.locals import *
from pygame import mixer
import pymunk
import pymunk.pygame_util
from pymunk.shapes import Segment
from pymunk.vec2d import Vec2d
pygame.mixer.pre_init(44110, -16, 2, 512)
mixer.init()
# Set up the window.
width, height = 1440, 640
screen = pygame.display.set_mode((width, height))
bg = pygame.image.load("space.png")
def draw_bg():
screen.blit(bg, (0, 0))
#load sounds
#death_fx = pygame.mixer.Sound("")
#death_fx.set_volume(0.25)
shoot_fx = mixer.Sound("shot.wav")
shoot_fx.set_volume(0.25)
#mixer.music.load("video.mp3")
#mixer.music.play()
#time.sleep(2)
#mixer.music.stop()
#gun_mode_fx = pygame.mixer.Sound("")
#gun_mode_fx.set_volume(0.25)
#thrust_mode_fx = pygame.mixer.Sound("")
#thrust_mode_fx.set_volume(0.25)
collision_fx = mixer.Sound("thump.wav")
collision_fx.set_volume(0.25)
ship_group = pygame.sprite.Group()
space = pymunk.Space()
space.gravity = 0, 0
space.damping = 0.6
draw_options = pymunk.pygame_util.DrawOptions(screen)
bulletList = []
playerList = []
environmentList = []
arbiterList = []
b0 = space.static_body
segmentBot = pymunk.Segment(b0, (0,height), (width, height), 4)
segmentTop = pymunk.Segment(b0, (0,0), (width, 0), 4)
segmentLef = pymunk.Segment(b0, (width,0), (width, height), 4)
segmentRit = pymunk.Segment(b0, (0,0), (0, height), 4)
walls = [segmentBot,segmentLef,segmentRit,segmentTop]
for i in walls:
i.elasticity = 1
i.friction = 0.5
i.color = (255,255,255,255)
environmentList.append(i)
class Player(object):
radius = 30
def __init__(self, position, space, color):
self.body = pymunk.Body(mass=5,moment=10)
self.mode = 0 # 0 is gun, 1 is thrust, ? 2 is shield
self.body.position = position
self.shape = pymunk.Circle(self.body, radius = self.radius)
#self.image
#self.shape.friction = 0.9
self.shape.elasticity= 0.2
space.add(self.body,self.shape)
self.angleGun = 0
self.angleThrust = 0
self.health = 100
self.speed = 500
self.gearAngle = 0
self.turningSpeed = 5
self.shape.body.damping = 1000
self.cooldown = 0
self.fireRate = 30
self.shape.collision_type = 1
self.shape.color = color
playerList.append(self)
def force(self,force):
self.shape.body.apply_force_at_local_point(force,(0,0))
def rocketForce(self):
radians = self.angleThrust * math.pi/180
self.shape.body.apply_force_at_local_point((-self.speed * math.cos(radians),-self.speed * math.sin(radians)),(0,0))
def draw(self):
gear = pygame.image.load("gear.png")
gearBox = gear.get_rect(center=self.shape.body.position)
gearRotated = pygame.transform.rotate(gear, self.gearAngle)
#gearRotated.rect.center=self.shape.body.position
x,y = self.shape.body.position
radianGun = self.angleGun * math.pi/180
radianThrust = self.angleThrust * math.pi/180
radiyus = 30 *(100-self.health)/100
screen.blit(gearRotated,gearBox)
self.gearAngle += 1
if radiyus == 30:
radiyus = 32
pygame.draw.circle(screen,self.shape.color,self.shape.body.position,radiyus,0)
pygame.draw.circle(screen,(0,0,0),self.shape.body.position,radiyus,0)
pygame.draw.line(
screen,(0,255,0),
(self.radius * math.cos(radianGun) * 1.5 + x,self.radius * math.sin(radianGun) * 1.5 + y),
(x,y), 5
)
pygame.draw.line(
screen,(200,200,0),
(self.radius * math.cos(radianThrust) * 1.5 + x,self.radius * math.sin(radianThrust) * 1.5 + y),
(x,y), 5
)
#more
def targetAngleGun(self,tAngle):
tempTAngle = tAngle - self.angleGun
tempTAngle = tempTAngle % 360
if(tempTAngle < 180 and not tempTAngle == 0):
self.angleGun -= self.turningSpeed
elif(tempTAngle >= 180 and not tempTAngle == 0):
self.angleGun += self.turningSpeed
self.angleGun = self.angleGun % 360
#print(tAngle, "target Angle")
#print(self.angleGun, "selfangleGun")
#print(tempTAngle, "tempTAngle")
def targetAngleThrust(self,tAngle):
tempTAngle = tAngle - self.angleThrust
tempTAngle = tempTAngle % 360
if(tempTAngle < 180 and not tempTAngle == 0):
self.angleThrust -= self.turningSpeed
elif(tempTAngle >= 180 and not tempTAngle == 0):
self.angleThrust += self.turningSpeed
self.angleThrust = self.angleThrust % 360
#print(tAngle, "target Angle")
#print(self.angleThrust, "selfangleGun")
#print(tempTAngle, "tempTAngle")
def targetAngle(self,tAngle):
if(self.mode == 0):
self.targetAngleGun(tAngle)
elif(self.mode == 1):
self.targetAngleThrust(tAngle)
def shoot(self):
if(self.cooldown == self.fireRate):
x,y = self.shape.body.position
radianGun = self.angleGun * math.pi/180
spawnSpot = (self.radius * math.cos(radianGun) * 1.5 + x,self.radius * math.sin(radianGun)*1.5+y)
self.shape.body.apply_impulse_at_local_point((-20 * math.cos(radianGun),-20 * math.sin(radianGun)),(0,0))
print(spawnSpot)
bT = Bullet(spawnSpot, 5, 50,self.shape.color)
b = copy.deepcopy(bT)
bulletList.append(b)
space.add(b.shape,b.shape.body)
b.getShot(self.angleGun)
self.cooldown = 0
print('pew')
shoot_fx.play()
# HEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEREEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
def tick(self):
self.draw()
if(self.cooldown < self.fireRate):
self.cooldown += 1
#for o in playerList:
# c = self.shape.shapes_collide(o.shape)
# if(len(c.points)>0):
# self.damage(c.points[0].distance/10)
for o in bulletList:
c = self.shape.shapes_collide(o.shape)
#print(c)
for o in walls:
c = self.shape.shapes_collide(o)
if(len(c.points)>0):
self.damage(c.points[0].distance * 3)
def damage(self, damage):
self.health -= abs(damage)
if self.health < 0:
self.health = 0
#maybe make it part of the player class
def drawWallCollision(arbiter, space, data):
for c in arbiter.contact_point_set.points:
r = max(3, abs(c.distance * 5))
r = int(r)
p = tuple(map(int, c.point_a))
pygame.draw.circle(data["surface"], pygame.Color("red"), p, r, 0)
print('magnitude', math.sqrt(arbiter.total_impulse[0]**2 + arbiter.total_impulse[1]**2))
#print('position', p)
#print(data)
print("its all arbitrary")
s1, s2 = arbiter.shapes
collision_fx.play()
def drawBulletCollision(arbiter, space, data):
s1, s2 = arbiter.shapes
for c in arbiter.contact_point_set.points:
magnitude = math.sqrt(arbiter.total_impulse[0]**2 + arbiter.total_impulse[1]**2)
for p in playerList:
avr = ((c.point_a[0] + c.point_b[0])/2, (c.point_a[1] + c.point_b[1])/2)
distance = (math.sqrt((avr[0] - p.shape.body.position[0]) **2 + (avr[1] - p.shape.body.position[1]) **2 ))
if(distance < Bullet.explosionRadius + Player.radius):
if not(s1.color == s2.color):
p.damage(magnitude)
for b in bulletList:
avr = ((c.point_a[0] + c.point_b[0])/2, (c.point_a[1] + c.point_b[1])/2)
distance = (math.sqrt((avr[0] - p.shape.body.position[0]) **2 + (avr[1] - p.shape.body.position[1]) **2 ))
if(distance < Bullet.explosionRadius + Player.radius):
if not(s1.color == s2.color):
b.damage(magnitude)
pygame.draw.circle(data["surface"], pygame.Color("red"), tuple(map(int, c.point_a)), 10, 0)
print('magnitude', magnitude)
#print('position', p)
#print(data)
print("its all arbitrary")
def drawArbitraryCollision(arbiter, space, data):
collision_fx.play()
class Ship(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("gear.png")
self.rect = self.image.get_rect()
self.rect.center = [x, y]
def rotate(self):
self.image = pygame.transform.rotate(self.image,1)
class Bullet(object):
damage = 2
explosionRadius = 5
def __init__(self, position, size, speed,color):
pts = [(-size, -size), (size, -size), (size, size), (-size, size)]
self.body = copy.deepcopy(pymunk.Body(mass=0.1,moment=1))
self.shape = copy.deepcopy(pymunk.Poly(self.body, pts))
self.shape.body.position = position
self.shape.friction = 0.5
self.shape.elasticity = 1
self.shape.color = color
self.speed = speed
self.size = size
self.shape.collision_type = 2
#space.add(self.body,self.shape)
#bulletList.append(self)
self.lifetime = 0
def getShot(self,angle):
radians = angle * math.pi/180
self.shape.body.apply_impulse_at_local_point((self.speed * math.cos(radians),self.speed * math.sin(radians)),(0,0))
def tick(self):
self.lifetime += 1
if(self.lifetime > 300):
self.shape.body.position = (10000,30)
def damage(self, damage):
self.lifetime = 300
#VELOCITY OF BULLET STARTS WITH VELOCITY OF PLAYER
#MAKE VOLUME OF SOUND DEPEND ON THE IMPULSE FOR THE IMPACTS
#error on purpose so you notice this
#INSTANCES NOT WORKING????
def runPyGame():
# Initialise PyGame.
pygame.init()
# Set up the clock. This will tick every frame and thus maintain a relatively constant framerate. Hopefully.
fps = 60.0
fpsClock = pygame.time.Clock()
running = True
font = pygame.font.SysFont("Arial", 16)
p1 = Player((240,240),space,(132, 66, 245,255))
p2 = Player((1200,400),space,(47, 247, 184,255))
space.add(segmentBot,segmentTop,segmentLef,segmentRit)
# Main game loop.
ch = space.add_collision_handler(1, 0)
ch.data["surface"] = screen
ch.post_solve = drawWallCollision
ch = space.add_collision_handler(1, 2)
ch.data["surface"] = screen
ch.post_solve = drawBulletCollision
ch = space.add_collision_handler(0, 2)
ch.data["surface"] = screen
ch.post_solve = drawArbitraryCollision
dt = 1/fps # dt is the time since last frame.
while True: # Loop forever!
keys = pygame.key.get_pressed()
for event in pygame.event.get():
# We need to handle these events. Initially the only one you'll want to care
# about is the QUIT event, because if you don't handle it, your game will crash
# whenever someone tries to exit.
if event.type == QUIT:
pygame.quit() # Opposite of pygame.init
sys.exit() # Not including this line crashes the script on Windows.
if event.type == KEYDOWN:
if event.key == pygame.K_s:
p1.mode = -(p1.mode - 0.5) + 0.5
print(p1.mode)
if (event.key == pygame.K_k and p1.mode == 0):
p1.shoot()
if event.key == pygame.K_KP_5:
p2.mode = -(p2.mode - 0.5) + 0.5
print(p2.mode)
if (event.key == pygame.K_m and p2.mode == 0):
p2.shoot()
#b = Bullet((200,200),51,51)
if(keys[K_w]):
p1.targetAngle(90)
if(keys[K_q]):
p1.targetAngle(45)
if(keys[K_a]):
p1.targetAngle(0)
if(keys[K_z]):
p1.targetAngle(315)
if(keys[K_x]):
p1.targetAngle(270)
if(keys[K_c]):
p1.targetAngle(225)
if(keys[K_d]):
p1.targetAngle(180)
if(keys[K_e]):
p1.targetAngle(135)
if(keys[K_k] and p1.mode == 1):
p1.rocketForce()
if(keys[K_KP_8]):
p2.targetAngle(90)
if(keys[K_KP_7]):
p2.targetAngle(45)
if(keys[K_KP_4]):
p2.targetAngle(0)
if(keys[K_KP_1]):
p2.targetAngle(315)
if(keys[K_KP_2]):
p2.targetAngle(270)
if(keys[K_KP_3]):
p2.targetAngle(225)
if(keys[K_KP_6]):
p2.targetAngle(180)
if(keys[K_KP_9]):
p2.targetAngle(135)
if(keys[K_m] and p2.mode == 1):
p2.rocketForce()
# Handle other events as you wish.
screen.fill((250, 250, 250)) # Fill the screen with black.
# Redraw screen here.
### Draw stuff
draw_bg()
space.debug_draw(draw_options)
for i in playerList:
i.tick()
screen.blit(
font.render("P1 Health: " + str(p1.health), True, pygame.Color("white")),
(50, 10),
)
screen.blit(
font.render("P2 Health: " + str(p2.health), True, pygame.Color("white")),
(50, 30),
)
for i in bulletList:
i.tick()
ship_group.draw(screen)
# Flip the display so that the things we drew actually show up.
pygame.display.update()
dt = fpsClock.tick(fps)
space.step(0.01)
pygame.display.update()
runPyGame()
I cant point to the exact error since the code is quite long and depends on files I dont have. But here is a general advice for troubleshooting:
Try to give a name to each shape when you create them, and then print it out. Also print out the name of each shape that you add or remove from the space. This should show which shape you are actually removing and will probably make it easy to understand whats wrong.
For example:
...
self.shape = pymunk.Circle(self.body, radius = self.radius)
self.shape.name = "circle 1"
print("Created", self.shape.name)
...
print("Adding", self.shape.name)
space.add(self.body,self.shape)
...
(Note that you need to reset the name of shapes you copy, since otherwise the copy will have the same name.)
I'm having trouble deleting classes that are used in this code. This is a code that I found online, and it is a 2d car physics simulator. I tried just deleting the lines of code where it defines those classes, but it won't work because the class is used in code and I don't know how to get rid of it. Thank you in advance.
I would like to have this code work without using classes Car and Game.
import os
import pygame
from math import sin, radians, degrees, copysign
from pygame.math import Vector2
class Car:
def __init__(self, x, y, angle=0.0, length=4, max_steering=30, max_acceleration=5.0):
self.position = Vector2(x, y)
self.velocity = Vector2(0.0, 0.0)
self.angle = angle
self.length = length
self.max_acceleration = max_acceleration
self.max_steering = max_steering
self.max_velocity = 20
self.brake_deceleration = 10
self.free_deceleration = 2
self.acceleration = 0.0
self.steering = 0.0
def update(self, dt):
self.velocity += (self.acceleration * dt, 0)
self.velocity.x = max(-self.max_velocity, min(self.velocity.x, self.max_velocity))
if self.steering:
turning_radius = self.length / sin(radians(self.steering))
angular_velocity = self.velocity.x / turning_radius
else:
angular_velocity = 0
self.position += self.velocity.rotate(-self.angle) * dt
self.angle += degrees(angular_velocity) * dt
class Game:
def __init__(self):
pygame.init()
pygame.display.set_caption("Car tutorial")
width = 1280
height = 720
self.screen = pygame.display.set_mode((width, height))
self.clock = pygame.time.Clock()
self.ticks = 60
self.exit = False
def run(self):
current_dir = os.path.dirname(os.path.abspath(__file__))
image_path = os.path.join(current_dir, "car.png")
car_image = pygame.image.load(image_path)
car = Car(0, 0)
ppu = 32
while not self.exit:
dt = self.clock.get_time() / 1000
# Event queue
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.exit = True
# User input
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
if car.velocity.x < 0:
car.acceleration = car.brake_deceleration
else:
car.acceleration += 1 * dt
elif pressed[pygame.K_DOWN]:
if car.velocity.x > 0:
car.acceleration = -car.brake_deceleration
else:
car.acceleration -= 1 * dt
elif pressed[pygame.K_SPACE]:
if abs(car.velocity.x) > dt * car.brake_deceleration:
car.acceleration = -copysign(car.brake_deceleration, car.velocity.x)
else:
car.acceleration = -car.velocity.x / dt
else:
if abs(car.velocity.x) > dt * car.free_deceleration:
car.acceleration = -copysign(car.free_deceleration, car.velocity.x)
else:
if dt != 0:
car.acceleration = -car.velocity.x / dt
car.acceleration = max(-car.max_acceleration, min(car.acceleration, car.max_acceleration))
if pressed[pygame.K_RIGHT]:
car.steering -= 30 * dt
elif pressed[pygame.K_LEFT]:
car.steering += 30 * dt
else:
car.steering = 0
car.steering = max(-car.max_steering, min(car.steering, car.max_steering))
# Logic
car.update(dt)
# Drawing
self.screen.fill((0, 0, 0))
rotated = pygame.transform.rotate(car_image, car.angle)
rect = rotated.get_rect()
self.screen.blit(rotated, car.position * ppu - (rect.width / 2, rect.height / 2))
pygame.display.flip()
self.clock.tick(self.ticks)
pygame.quit()
if __name__ == '__main__':
game = Game()
game.run()
This is the link of the original code: http://rmgi.blog/pygame-2d-car-tutorial.html
Solely as an academic exercise, here is the same code without classes. A dictionary is used to hold the car properties. Note that the class syntax is cleaner than the dictionary syntax.
The important thing to note is that the car code is no longer encapsulated. The updatecar function is now part of the main code instead of being isolated to the car object. Since this code is small, encapsulation is not a major concern, but if the car had a dozen methods than the code could quickly become messy and difficult to maintain.
All this said, it's definitely important that you learn about classes and OOP. It will save you some pain in the future.
import os
import pygame
from math import sin, radians, degrees, copysign
from pygame.math import Vector2
def buildcar(x, y, angle=0.0, length=4, max_steering=30, max_acceleration=5.0):
car = {} # dictionary of properties
car["position"] = Vector2(x, y)
car["velocity"] = Vector2(0.0, 0.0)
car["angle"] = angle
car["length"] = length
car["max_acceleration"] = max_acceleration
car["max_steering"] = max_steering
car["max_velocity"] = 20
car["brake_deceleration"] = 10
car["free_deceleration"] = 2
car["acceleration"] = 0.0
car["steering"] = 0.0
return car
def updatecar(car, dt):
car["velocity"] += (car["acceleration"] * dt, 0)
car["velocity"].x = max(-car["max_velocity"], min(car["velocity"].x, car["max_velocity"]))
if car["steering"]:
turning_radius = car["length"] / sin(radians(car["steering"]))
angular_velocity = car["velocity"].x / turning_radius
else:
angular_velocity = 0
car["position"] += car["velocity"].rotate(-car["angle"]) * dt
car["angle"] += degrees(angular_velocity) * dt
pygame.init()
pygame.display.set_caption("Car tutorial")
width = 1280
height = 720
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
ticks = 60
exit = False
current_dir = os.path.dirname(os.path.abspath(__file__))
image_path = os.path.join(current_dir, "dotyellow.png")
car_image = pygame.image.load(image_path)
car = buildcar(0,0)
ppu = 32
while not exit:
dt = clock.get_time() / 1000
# Event queue
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit = True
# User input
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
if car["velocity"].x < 0:
car["acceleration"] = car["brake_deceleration"]
else:
car["acceleration"] += 1 * dt
elif pressed[pygame.K_DOWN]:
if car["velocity"].x > 0:
car["acceleration"] = -car["brake_deceleration"]
else:
car["acceleration"] -= 1 * dt
elif pressed[pygame.K_SPACE]:
if abs(car["velocity"].x) > dt * car["brake_deceleration"]:
car["acceleration"] = -copysign(car["brake_deceleration"], car["velocity"].x)
else:
car["acceleration"] = -car["velocity"].x / dt
else:
if abs(car["velocity"].x) > dt * car["free_deceleration"]:
car["acceleration"] = -copysign(car["free_deceleration"], car["velocity"].x)
else:
if dt != 0:
car["acceleration"] = -car["velocity"].x / dt
car["acceleration"] = max(-car["max_acceleration"], min(car["acceleration"], car["max_acceleration"]))
if pressed[pygame.K_RIGHT]:
car["steering"] -= 30 * dt
elif pressed[pygame.K_LEFT]:
car["steering"] += 30 * dt
else:
car["steering"] = 0
car["steering"] = max(-car["max_steering"], min(car["steering"], car["max_steering"]))
# Logic
updatecar(car, dt)
# Drawing
screen.fill((0, 0, 0))
rotated = pygame.transform.rotate(car_image, car["angle"])
rect = rotated.get_rect()
screen.blit(rotated, car["position"] * ppu - (rect.width / 2, rect.height / 2))
pygame.display.flip()
clock.tick(ticks)
pygame.quit()
I'm trying to write a collision detection, but I'm getting some weird sticking behaviour, when I run into the object, it goes through, then when I go back, it sticks. what is going on here? the
module setup
import tkinter as tk
import random
setting up the variables
groundheight = 700
playersize = 50
PVx = 5
PVy = 0
playermass = 25
groundmass = 1000
playerx = 0
playery = -5
isgrounded = False
enemysize = 25
the player behavior
def forever():
global PVx
global PVy
global playerx
global playery
global isgrounded
enemyarray = canv.find_withtag("enemy")
playerx = canv.coords("player")[2]-playersize/2
playery = canv.coords("player")[3]-playersize/2
canv.move("player", PVx, PVy)
distoground = math.sqrt(math.pow(playery - groundheight, 2))
if distoground <= playersize:
PVy = 0
else:
isgrounded = False
PVy += 0.1
root.after(16, forever)
the enemy behavior
def enemyupdate():
global PVx
global PVy
enemyarray = canv.find_withtag("enemy")
for i in enemyarray:
enemymass = 25
Evx = 0
EVy = 0
x = canv.coords("enemy")[2]-playersize
y = canv.coords("enemy")[3]-playersize
distoground = math.sqrt(math.pow(y - groundheight, 2))
distoplayer = math.sqrt(((x-playerx)**2)+((y-playery)**2))
if distoground <= playersize:
EVy = 0
else:
EVy += 5
here's where the collisions happen
if distoplayer <= playersize+enemysize:
tempx = Evx
Evx = (playermass*PVx + enemymass*Evx)/(playermass+enemymass)
PVx = (playermass*PVx + enemymass*Evx)/(playermass+enemymass)
canv.move(i, Evx, EVy)
root.after(16, enemyupdate)
receiving input
def getaxis(X):
global PVx
PVx = X
jump code for later
def jump(event):
global PVy
if isgrounded:
PVy -= 5
tkinter main loop
root = tk.Tk()
root.geometry("1920x1080")
root.state("zoomed")
canv = tk.Canvas(width=1920, height=1080, bg='light blue')
canv.create_rectangle(0, groundheight, 1920, 1080, fill="White")
player = canv.create_oval(0, 700, playersize, 700-playersize, fill="Red", tag="player")
canv.pack()
root.after(1, forever)
root.after(1, enemyupdate)
root.bind("<space>", jump)
root.bind("<Left>", lambda *args: getaxis(-5))
root.bind("<Right>", lambda *args: getaxis(5))
for i in range(1):
enemx = 500
player = canv.create_oval(enemx+enemysize, 700, enemx - enemysize, 700-enemysize*2, fill="Red", tag="enemy")
root.mainloop()
I have no clue what to do here, I want to make it so that a turtle in Python will shoot a bullet and if it collides with the other turtle, the score goes up. Except if I run this code, the game will crash when I shoot the bullet. So far, I only tried it for one of the eight move commands.
#imports turtles
import turtle
import random
#Sets screen, turtle program, and colour group
turtle.setup(1000, 800)
screen = turtle.Screen()
wn = turtle.Screen()
wn.bgcolor("springgreen")
amir = turtle.Turtle()
amir.shape("arrow")
amir.shapesize(1)
amir.speed(10)
amir.pensize (2)
amir.color("blue")
amir.setheading(90)
amir.penup()
#bullet 1
jonafat = turtle.Turtle()
jonafat.shape("circle")
jonafat.shapesize(0.5)
jonafat.speed(2)
jonafat.pensize (1)
jonafat.color("black")
jonafat.penup()
jonafat.ht()
hossein = turtle.Turtle()
hossein.shape("arrow")
hossein.shapesize(1)
hossein.speed(10)
hossein.pensize (2)
hossein.color("gold")
hossein.setheading(90)
hossein.penup()
#bullet
jonafat2 = turtle.Turtle()
jonafat2.shape("circle")
jonafat2.shapesize(0.5)
jonafat2.speed(2)
jonafat2.pensize (1)
jonafat2.color("black")
jonafat2.penup()
jonafat2.ht()
#scoreboard
TT = turtle.Turtle()
TT.ht()
TT.penup()
TT.goto(-500,200)
TT.color("black")
TT.write("0", move = False, align = "center", font = ("Arial", 20, "normal"))
#second scoreboard
TT = turtle.Turtle()
TT.ht()
TT.penup()
TT.goto(-500,200)
TT.color("black")
TT.write("0", move = False, align = "center", font = ("Arial", 20, "normal"))
x = 0
y = 0
amirs = 2
hosseins = 2
auto = 15
vanguard = 15
trump = 0
time = 1
score = 0
panda = 295
def up():
global amir
global x
global amirs
global hosseins
amir.seth(90)
n = 1
for i in range(0, n):
amir.sety(amir.ycor()+10)
n + 1
def down():
global amir
global x
global amirs
global hosseins
amir.seth(270)
n = 1
for i in range(0, n):
amir.sety(amir.ycor()-10)
n + 1
def left():
global amir
global x
global amirs
global hosseins
amir.seth(180)
n = 1
for i in range(0, n):
amir.setx(amir.xcor()-10)
n + 1
def right():
global amir
global x
global amirs
global hosseins
amir.seth(0)
n = 1
for i in range(0, n):
amir.setx(amir.xcor()+10)
n + 1
def up2():
global hossein
global y
global hosseins
hossein.seth(90)
n = 1
for i in range(0, n):
hossein.sety(hossein.ycor()+10)
n + 1
def down2():
global hossein
global y
global hosseins
hossein.seth(270)
n = 1
for i in range(0, n):
hossein.sety(hossein.ycor()-10)
n + 1
def left2():
global hossein
global y
global hosseins
hossein.seth(180)
n = 1
for i in range(0, n):
hossein.setx(hossein.xcor()-10)
n + 1
def right2():
global hossein
global y
global hosseins
hossein.seth(0)
n = 1
for i in range(0, n):
hossein.setx(hossein.xcor()+10)
n + 1
def collisionCheck(jonafat, hossein):
crash = True
jonafat1X = jonafat.xcor()
jonafat1Y = jonafat.ycor()
hossein2X = hossein.xcor()
hossein2Y = hossein.ycor()
jonafatPos = (int(jonafat1X), int(jonafat1Y))
hosseinPos = (int(hossein2X), int(hossein2Y))
if jonafatPos != hosseinPos:
crash = False
if jonafatPos == hosseinPos:
crash = True
return crash
def clock():
global time
time = time-1
def move():
global auto
global vanguard
global score
global time
wn.ontimer(clock,1000)
angle = amir.heading()
jonafat.ht()
jonafat.speed(10)
jonafat.setpos(amir.xcor(), amir.ycor())
if angle == 0:
trump = 1.
time = time-1
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor()+300, amir.ycor())
n + 1
infLoop = 1
while infLoop == 1:
if collisionCheck(jonafat, hossein) == True:
infLoop = 0
break
score = score + 1
TT.clear()
TT.write(score, move = False, align = "center", font = ("Arial", 20, "normal"))
if time == 0:
break
infLoop = 0
if angle == 90:
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor(),amir.ycor()+300)
n + 1
if angle == 180:
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor()-300, amir.ycor())
n + 1
if angle == 270:
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor(), amir.ycor()-300)
n + 1
def move2():
angle2 = hossein.heading()
jonafat2.ht()
jonafat2.speed(10)
jonafat2.setpos(hossein.xcor(), hossein.ycor())
if angle2 == 0:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor()+300, hossein.ycor())
n + 1
if angle2 == 90:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor(), hossein.ycor()+300)
n + 1
if angle2 == 180:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor()-300, hossein.ycor())
n + 1
if angle2 == 270:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor(), hossein.ycor()-300)
n + 1
wn.onkeypress(up, "w")
wn.onkeypress(down, "s")
wn.onkeypress(left, "a")
wn.onkeypress(right, "d")
wn.onkeypress(up2, "Up")
wn.onkeypress(down2, "Down")
wn.onkeypress(left2, "Left")
wn.onkeypress(right2, "Right")
wn.onkeypress(move2, "Shift_R")
wn.onkeypress(move, "space")
wn.listen()
This is the part I have trouble with
def collisionCheck(jonafat, hossein):
crash = True
jonafat1X = jonafat.xcor()
jonafat1Y = jonafat.ycor()
hossein2X = hossein.xcor()
hossein2Y = hossein.ycor()
jonafatPos = (int(jonafat1X), int(jonafat1Y))
hosseinPos = (int(hossein2X), int(hossein2Y))
if jonafatPos != hosseinPos:
crash = False
if jonafatPos == hosseinPos:
crash = True
return crash
def clock():
global time
time = time-1
def move():
global auto
global vanguard
global score
global time
wn.ontimer(clock,1000)
angle = amir.heading()
jonafat.ht()
jonafat.speed(10)
jonafat.setpos(amir.xcor(), amir.ycor())
if angle == 0:
trump = 1.
time = time-1
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor()+300, amir.ycor())
n + 1
infLoop = 1
while infLoop == 1:
if collisionCheck(jonafat, hossein) == True:
infLoop = 0
break
score = score + 1
TT.clear()
TT.write(score, move = False, align = "center", font = ("Arial", 20, "normal"))
if time == 0:
break
infLoop = 0
It appears your only checking if their position is the same, although two objects can pass close to each other so that they visually collide, but aren't on the exact same position as each other.
You'll want to take their dimensions into account to properly check for collisions. The easiest way to check if they collide is to take the distance between their positions and check if it's smaller than a certain value. Although better options exist.
Instead of all this:
def collisionCheck(jonafat, hossein):
crash = True
jonafat1X = jonafat.xcor()
jonafat1Y = jonafat.ycor()
hossein2X = hossein.xcor()
hossein2Y = hossein.ycor()
jonafatPos = (int(jonafat1X), int(jonafat1Y))
hosseinPos = (int(hossein2X), int(hossein2Y))
if jonafatPos != hosseinPos:
crash = False
if jonafatPos == hosseinPos:
crash = True
return crash
Let's take advantage of #JoshuaWaring's advice as well as turtle's distance() method and come up with something more like:
PERSONAL_SPACE = 10 # assign this to be how close is too close
def collisionCheck(jonafat, hossein):
return jonafat.distance(hossein) < PERSONAL_SPACE
Let's rework the rest of the code to produce a minimally playable game:
# imports turtles
from turtle import Turtle, Screen
PERSONAL_SPACE = 15 # assign this to be how close is too close
FIRE_DISTANCE = 300
FONT = ('Arial', 36, 'normal')
# Sets screen, turtle program, and color group
screen = Screen()
screen.setup(1000, 800)
screen.bgcolor('springgreen')
def collisionCheck(bullet, player):
return bullet.distance(player) < PERSONAL_SPACE
def up1():
player1.seth(90)
player1.forward(10)
def down1():
player1.seth(270)
player1.forward(10)
def left1():
player1.seth(180)
player1.forward(10)
def right1():
player1.seth(0)
player1.forward(10)
def clock1():
global time1, score1
if collisionCheck(bullet1, player2):
score1 += 1
TT1.undo()
TT1.write(score1, align='center', font=FONT)
time1 = 1
else:
bullet1.forward(1)
time1 -= 1
if time1:
screen.ontimer(clock1, 10)
else:
bullet1.hideturtle()
def fire1():
global time1
bullet1.setpos(player1.pos())
bullet1.setheading(player1.heading())
bullet1.showturtle()
time1 = FIRE_DISTANCE
screen.ontimer(clock1, 100)
screen.onkeypress(up1, 'w')
screen.onkeypress(down1, 's')
screen.onkeypress(left1, 'a')
screen.onkeypress(right1, 'd')
screen.onkeypress(fire1, 'space')
def up2():
player2.seth(90)
player2.forward(10)
def down2():
player2.seth(270)
player2.forward(10)
def left2():
player2.seth(180)
player2.forward(10)
def right2():
player2.seth(0)
player2.forward(10)
def clock2():
global time2, score2
if collisionCheck(bullet2, player1):
score2 += 1
TT2.undo()
TT2.write(score2, align='center', font=FONT)
time2 = 1
else:
bullet2.forward(1)
time2 -= 1
if time2:
screen.ontimer(clock2, 10)
else:
bullet2.hideturtle()
def fire2():
global time2
bullet2.setpos(player2.pos())
bullet2.setheading(player2.heading())
bullet2.showturtle()
time2 = FIRE_DISTANCE
screen.ontimer(clock2, 100)
screen.onkeypress(up2, 'Up')
screen.onkeypress(down2, 'Down')
screen.onkeypress(left2, 'Left')
screen.onkeypress(right2, 'Right')
screen.onkeypress(fire2, '0')
player1 = Turtle('arrow')
player1.speed('fastest')
player1.color('blue')
player1.setheading(180)
player1.penup()
player1.forward(10)
# bullet 1
bullet1 = Turtle('circle', visible=False)
bullet1.shapesize(0.5)
bullet1.speed('fastest')
bullet1.color('black', 'blue')
bullet1.penup()
player2 = Turtle('arrow')
player2.speed('fastest')
player2.color('gold')
player2.setheading(0)
player2.penup()
player2.forward(10)
# bullet 2
bullet2 = Turtle('circle', visible=False)
bullet2.shapesize(0.5)
bullet2.speed('fastest')
bullet2.color('black', 'gold')
bullet2.penup()
# scoreboard
TT1 = Turtle(visible=False)
TT1.penup()
TT1.goto(-450, 300)
TT1.color('blue')
TT1.write('0', align='center', font=FONT)
# second scoreboard
TT2 = Turtle(visible=False)
TT2.penup()
TT2.goto(450, 300)
TT2.color('gold')
TT2.write('0', align='center', font=FONT)
time1 = 0
score1 = 0
time2 = 0
score2 = 0
screen.listen()
screen.mainloop()
Still needs additional work, i.e. doesn't handle (or disable) multiple shots correctly, etc.