How to rotate a polygon on Tkinter canvas?

I am working on a version of the asteroids using Python and Tkinter. When the left or right arrow key is pressed, the ship should rotate. A ship is a triangle on Tkinter canvas. I'm having trouble applying a formula to adjust the coordinates for a triangle. I believe this has something to do with sin and a neighbor, although I'm not quite sure. So far I have two classes for the ship, and another for the game. In the ship class, I have callback methods for pressing keys. Any help would be greatly appreciated. Thank you

Ship class

import math class Ship: def __init__(self,canvas,x,y,width,height): self.canvas = canvas self.x = x - width/2 self.y = y + height/2 self.width = width self.height = height self.x0 = self.x self.y0 = self.y self.x1 = self.x0 + self.width/2 self.y1 = self.y0-self.height self.x2 = self.x0 + self.width self.y2 = self.y0 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 rotateLeft(self, event=None): # Should rotate one degree left. pass def rotateRight(self, event=None): # Should rotate one degree right. self.x0 = self.x0 -1 self.y0 = self.y0 - 1 self.x1 = self.x1 + 1 self.y1 = self.y1 + 1 self.x2 = self.x2 - 1 self.y2 = self.y2 + 1 self.changeCoords() 

Game class

 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) self.root.bind('<Left>', self.ship.rotateLeft) self.root.bind('<Right>', self.ship.rotateRight) 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) 
+7
python vector tkinter
source share
1 answer

First of all, you need to rotate around the center of the triangle. Probably a centroid is best suited for this. To find this, you can use the formula C = (1/3*(x0 + x1 + x2), 1/3*(y0 + y1 + y2)) , since it is the average value for all points of the triangle. Then you need to apply the rotation with this point as the center. So it will 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, which, by the way, make the game more like asteroids. (However, I didn’t realize the shooting. Perhaps I got more in this than I expected, but I am not going to do everything. In addition, there is a small problem when you try to use several navigation keys at once, but what about the way Tk handles events, it was not intended for games, so you will need to fight for a fair bit in order to work normally 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) 

Aside, you can use properties to simplify the processing of points, etc.

+9
source share

All Articles