I am working to create a version of asteroids using Python and Tkinter. When the left or right arrow key is pressed the ship needs to rotate. The ship is a triangle on the Tkint
First of all, you need to rotate around a center of the triangle. The centroid would probably work best for that. To find that, you can use the formula C = (1/3*(x0 + x1 + x2), 1/3*(y0 + y1 + y2))
, as it's the average of all points in the triangle. Then you have to apply the rotation with that point as the center. So it'd be something like this...
import math
class Ship:
def centroid(self):
return 1 / 3 * (self.x0 + self.x1 + self.x2), 1 / 3 * (self.y0 + self.y1 + self.y2)
def __init__(self, canvas, x, y, width, height, turnspeed, acceleration=1):
self._d = {'Up':1, 'Down':-1, 'Left':1, 'Right':-1}
self.canvas = canvas
self.width = width
self.height = height
self.speed = 0
self.turnspeed = turnspeed
self.acceleration = acceleration
self.x0, self.y0 = x, y
self.bearing = -math.pi / 2
self.x1 = self.x0 + self.width / 2
self.y1 = self.y0 - self.height
self.x2 = self.x0 + self.width
self.y2 = self.y0
self.x, self.y = self.centroid()
self.ship = self.canvas.create_polygon((self.x0, self.y0, self.x1, self.y1, self.x2, self.y2), outline="white", width=3)
def changeCoords(self):
self.canvas.coords(self.ship,self.x0, self.y0, self.x1, self.y1, self.x2, self.y2)
def rotate(self, event=None):
t = self._d[event.keysym] * self.turnspeed * math.pi / 180 # the trig functions generally take radians as their arguments rather than degrees; pi/180 radians is equal to 1 degree
self.bearing -= t
def _rot(x, y):
#note: the rotation is done in the opposite fashion from for a right-handed coordinate system due to the left-handedness of computer coordinates
x -= self.x
y -= self.y
_x = x * math.cos(t) + y * math.sin(t)
_y = -x * math.sin(t) + y * math.cos(t)
return _x + self.x, _y + self.y
self.x0, self.y0 = _rot(self.x0, self.y0)
self.x1, self.y1 = _rot(self.x1, self.y1)
self.x2, self.y2 = _rot(self.x2, self.y2)
self.x, self.y = self.centroid()
self.changeCoords()
def accel(self, event=None):
mh = int(self.canvas['height'])
mw = int(self.canvas['width'])
self.speed += self.acceleration * self._d[event.keysym]
self.x0 += self.speed * math.cos(self.bearing)
self.x1 += self.speed * math.cos(self.bearing)
self.x2 += self.speed * math.cos(self.bearing)
self.y0 += self.speed * math.sin(self.bearing)
self.y1 += self.speed * math.sin(self.bearing)
self.y2 += self.speed * math.sin(self.bearing)
self.x, self.y = self.centroid()
if self.y < - self.height / 2:
self.y0 += mh
self.y1 += mh
self.y2 += mh
elif self.y > mh + self.height / 2:
self.y0 += mh
self.y1 += mh
self.y2 += mh
if self.x < -self.width / 2:
self.x0 += mw
self.x1 += mw
self.x2 += mw
elif self.x > mw + self.width / 2:
self.x0 -= mw
self.x1 -= mw
self.x2 -= mw
self.x, self.y = self.centroid()
self.changeCoords()
I made some changes to the controls that make the game a bit more like Asteroids, by the way. (Didn't implement firing, though. I may have gotten more into this than I expected, but I'm not going to do everything. Also, there's a bit of a problem when you try to use multiple movement keys at once, but that's due to the way Tk does event handling. It wasn't designed for gaming, so you'd have to fiddle around a fair bit to get that working properly with Tk/Tkinter.)
from tkinter import *
from ship import *
class Game:
def __init__(self, gameWidth, gameHeight):
self.root = Tk()
self.gameWidth = gameWidth
self.gameHeight = gameHeight
self.gameWindow()
self.ship = Ship(self.canvas, x=self.gameWidth / 2,y=self.gameHeight / 2, width=50, height=50, turnspeed=10, acceleration=5)
self.root.bind('<Left>', self.ship.rotate)
self.root.bind('<Right>', self.ship.rotate)
self.root.bind('<Up>', self.ship.accel)
self.root.bind('<Down>', self.ship.accel)
self.root.mainloop()
def gameWindow(self):
self.frame = Frame(self.root)
self.frame.pack(fill=BOTH, expand=YES)
self.canvas = Canvas(self.frame,width=self.gameWidth, height=self.gameHeight, bg="black", takefocus=1)
self.canvas.pack(fill=BOTH, expand=YES)
asteroids = Game(600,600)
As an aside, you might want to use properties to allow for easier handling of the points and such.