如何在 Tkinter 画布上旋转多边形? [英] How to rotate a polygon on a Tkinter Canvas?

查看:47
本文介绍了如何在 Tkinter 画布上旋转多边形?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力使用 Python 和 Tkinter 创建一个小行星版本.当按下向左或向右箭头键时,船需要旋转.这艘船是 Tkinter 画布上的一个三角形.我在想出调整三角形坐标的公式时遇到了麻烦.我相信这与 sin 和 cos 有关,尽管我不确定.到目前为止,我有两个类,一个用于船舶,另一个用于游戏.在船类中,我有按键的回调方法.任何帮助将不胜感激.谢谢.

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 Tkinter canvas. I am having trouble coming up with formula to adjust the coordinates for the triangle. I believe it has something to do with sin and cos, though I am not exactly sure. So far I have two classes one for the ship and the other for the game. In the ship class I have callback methods for the key presses. Any help would be greatly appreciated. Thanks.

船级

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)

推荐答案

首先,您需要围绕三角形的中心旋转.质心可能最适合那个.要找到它,您可以使用公式 C = (1/3*(x0 + x1 + x2), 1/3*(y0 + y1 + y2)),因为它是所有的平均值三角形中的点.然后,您必须以该点为中心应用旋转.所以它会是这样的......

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

顺便说一下,我对控件进行了一些更改,使游戏更像 Asteroids.(虽然没有实现射击.我可能比我预期的更深入,但我不会做所有事情.此外,当您尝试同时使用多个移动键时会出现一些问题,但是这是因为 Tk 处理事件的方式.它不是为游戏设计的,所以你必须摆弄一些东西才能让它与 Tk/Tkinter 一起正常工作.)

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.

这篇关于如何在 Tkinter 画布上旋转多边形?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆