How to rotate a polygon on a Tkinter Canvas?

前端 未结 1 1603
野的像风
野的像风 2021-02-15 22:27

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

相关标签:
1条回答
  • 2021-02-15 22:56

    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.

    0 讨论(0)
提交回复
热议问题