Как повернуть многоугольник в python на холсте Tkinter?

Я работаю над созданием версии астероидов с использованием Python и Tkinter. При нажатии клавиши со стрелкой влево или вправо корабль должен вращаться. Судно представляет собой треугольник на Tkinter холст. У меня возникли проблемы с формулой, чтобы настроить координаты для треугольника. Я считаю, что это имеет какое-то отношение к греху и cos, хотя я не совсем уверен. Пока у меня есть два класса: один для корабля, а другой для игры. В классе ship у меня есть методы обратного вызова для ключа нажимает. Любая помощь будет очень признательна. Спасибо.

Корабль Класса

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()

Игра Класс

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)

1 ответов


прежде всего, вам нужно вращаться вокруг центра треугольника. Центроид, вероятно, сработает лучше всего. Чтобы найти это, вы можете использовать формулу C = (1/3*(x0 + x1 + x2), 1/3*(y0 + y1 + y2)), так как это среднее значение всех точек в треугольнике. Затем вы должны применить вращение с этой точкой в качестве центра. Значит, что-то вроде этого...

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()

я внес некоторые изменения в элементы управления, которые делают игру немного больше, как астероиды, кстати. (Однако не реализовал стрельбу. Я, возможно, получил больше, чем я ожидал, но я не собираюсь делать все. Кроме того, есть небольшая проблема, когда вы пытаетесь использовать несколько ключей движения одновременно, но это связано с тем, как Tk обрабатывает события. Он не был предназначен для игр, поэтому вам придется немного повозиться, чтобы правильно работать с 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)

в стороне, вы можете использовать свойства, чтобы облегчить обработку точек и тому подобное.