Как проверить, касаются ли два объекта друг друга в python tkinter? - PullRequest
0 голосов
/ 01 марта 2020

Я пытаюсь создать функцию, которая сообщает мне, если два объекта Canvas (в моем коде re c и block) касаются друг друга в Tkinter. Я пытался сделать это с информацией об их координатах, но, похоже, это не сработало. Не могли бы вы мне помочь?

Функция будет использоваться в Snake, которую я создаю в приведенном ниже коде, поэтому не спрашивайте, почему код такой длинный.

Функция в строке 119, same_poz

Это мой код:

from tkinter import *
import time
import random
import threading

root = Tk()
root.title('Snake')
x, y = 484, 484
recX, recY = x // 2, y // 2
recW, recH = recX + 22, recY + 22
randoms = []
for i in range(484):
    if i % 11 == 0:
        randoms.append(i)
blockX, blockY = random.choice(randoms), random.choice(randoms)
blockW, blockH = blockX + 22, blockY + 22
c = Canvas(root, bg='black', width=x, height=y)
c.pack()



class Snake(threading.Thread):
    def __init__(self, c, x, y, recX, recY, recW, recH, blockX, blockY, blockW, blockH):
        super(Snake, self).__init__()
        self.c = c
        self.x = x
        self.y = y
        self.recX = recX
        self.recY = recY
        self.recW = recW
        self.recH = recH
        self.blockW = blockW
        self.blockH = blockH
        self.rec = c.create_rectangle(recX, recY, recW, recH, fill='red', outline='white')
        self.blockX = blockX
        self.blockY = blockY
        self.block = c.create_rectangle(blockX, blockY, blockW, blockH, fill='green', 
        outline='white')
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False
        self.moving = False

    def movingright(self):
        self.moving_right = True
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False
        self.moving = True
        c.move(self.rec, 11, 0)
        self.after4 = root.after(150, self.movingright)

    def movingleft(self):
        self.moving_left = True
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False
        self.moving = True
        c.move(self.rec, -11, 0)
        self.after3 = root.after(150, self.movingleft)

    def movingup(self):
        self.moving_up = True
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving = True
        c.move(self.rec, 0, -11)
        self.after = root.after(150, self.movingup)

    def movingdown(self):
        self.moving_down = True
        self.moving_right = False
        self.moving_left = False
        self.moving_down = False
        self.moving = True
        c.move(self.rec, 0, 11)
        self.after2 = root.after(150, self.movingdown)

    def stop(self):
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False
        self.moving = False

        try:
            root.after_cancel(self.after)
        except AttributeError:
            pass
        try:
            root.after_cancel(self.after2)
        except AttributeError:
            pass
        try:
            root.after_cancel(self.after3)
        except AttributeError:
            pass
        try:
            root.after_cancel(self.after4)
        except AttributeError:
            pass

    def move(self, n):
        if n.keysym == 'Up':
            self.stop()
            self.movingup()
        if n.keysym == 'Down':
            self.stop()
            self.movingdown()
        if n.keysym == 'Right':
            self.stop()
            self.movingright()
        if n.keysym == 'Left':
            self.stop()
            self.movingleft()

    def same_poz(self):
        if self.blockY == self.recY:
            self.helpY = random.randint(10, self.y - self.blockY)
            self.c.move(self.block, 0, self.helpY)
        if self.blockX == self.recY:
            self.helpX = random.randint(10, self.x - self.blockX)
            self.c.move(self.block, 0, self.helpX)
        if self.blockW == self.recW:
            self.helpW = random.randint(10, self.x - self.blockW)
            self.c.move(self.block, 0, self.helpW)
        if self.blockH == self.recH:
            self.helpH = random.randint(10, self.y - self.blockH)
            self.c.move(self.block, 0, helpH)



cube = Snake(c, x, y, recX, recY, recW, recH, blockX, blockY, blockW, blockH)
cube.start()




cube.c.bind_all('<Key>', cube.move, cube.stop)
cube.c.bind_all('<Key>', cube.moving_left, cube.moving_right)
cube.c.bind_all('<Key', cube.moving_up, cube.moving_down)
cube.c.bind(cube.same_poz)
root.mainloop()

1 Ответ

0 голосов
/ 01 марта 2020

Слишком много проблем с вашим кодом, с тем, что вы предоставили. Я не знаю, почему вы программируете с использованием Tkinter, когда вам все еще не хватает основ Python. (Не хочу звучать хар sh)

  1. Я не знаю, почему вы решили унаследовать от threading.Thread, когда вы не используете ничего, что Thread предоставляет в этом классе .
  2. Слишком много параметров экземпляров, которые вы вообще не используете, и слишком много параметров экземпляров, которые вы должны изменять каждый раз.
  3. Не использовать elif и вместо этого использовать if постоянно.
  4. Если вы используете поток для обработки изменений Tkinter вне mainl oop, нет необходимости использовать .after, поскольку он в основном это делает.
  5. Tkinter имеет выделенные привязки для каждой клавиши, включая комбинации клавиш. Нет необходимости перехватывать каждое ключевое событие.
  6. Используйте if __name__ == '__main__':, если вы работаете с одним скриптом, для его тестирования.
  7. Некоторые материалы для чтения на Tkinter - http://effbot.org/tkinterbook/

Вот минимальная переделка кода, который работает.

import time
import random
import threading
from tkinter import *

MOVINGUP = 'u'
MOVINGDOWN = 'd'
MOVINGLEFT = 'l'
MOVINGRIGHT = 'r'
NOTMOVING = '0'


class Snake:
    def __init__(self, root, recX, recY, recW, recH, blockX, blockY, blockW, blockH):
        self.root = root

        self.c = Canvas(root, bg='black', width=x, height=y)
        self.c.pack()

        self.rec = self.c.create_rectangle(recX, recY, recW, recH, fill='red', outline='white')
        self.block = self.c.create_rectangle(blockX, blockY, blockW, blockH, fill='green', outline='white')

        self.direction = NOTMOVING

        self.root.bind('<Up>', lambda e: self.moveset(MOVINGUP))
        self.root.bind('<Down>', lambda e: self.moveset(MOVINGDOWN))
        self.root.bind('<Left>', lambda e: self.moveset(MOVINGLEFT))
        self.root.bind('<Right>', lambda e: self.moveset(MOVINGRIGHT))

    def moveset(self, direction):
        self.direction = direction

    def movement(self):
        if self.direction == MOVINGUP:
            self.c.move(self.rec, 0, -11)
        elif self.direction == MOVINGDOWN:
            self.c.move(self.rec, 0, 11)
        elif self.direction == MOVINGLEFT:
            self.c.move(self.rec, -11, 0)
        elif self.direction == MOVINGRIGHT:
            self.c.move(self.rec, 11, 0)
        self.same_poz()

    def run(self):
        while True:
            time.sleep(0.15)
            self.movement()

    def same_poz(self):
        # Snake (x0, y0, x1, y1)
        snakepos = self.c.bbox(self.rec)
        # Food block (x0, y0, x1, y1)
        food = self.c.bbox(self.block)
        # If direction matters, if not then possible to only use self.hit in a single condition.
        if self.direction == MOVINGRIGHT and self.hit(snakepos, food):
            print('Caught the food moving right.')
        elif self.direction == MOVINGLEFT and self.hit(snakepos, food):
            print('Caught the food moving left.')
        elif self.direction == MOVINGUP and self.hit(snakepos, food):
            print('Caught the food moving up.')
        elif self.direction == MOVINGDOWN and self.hit(snakepos, food):
            print('Caught the food moving down.')

    def hit(self, snakepos, food):
        """
            Recieves coordinates of food block and snake block and returns if they collide.
        :param snakepos: Tuple containing (x0, y0, x1, y1) of the snake.
        :param food: Tuple containing (x0, y0, x1, y1) of the food block.
        :return: Boolean whether they collide
        """
        snakex = (snakepos[0], snakepos[2])
        snakey = (snakepos[1], snakepos[3])
        foodx = (food[0], food[2])
        foody = (food[1], food[3])
        # Returns True if any of the snake x cooridnates are between the food x coordinates, or both x coordinates match.
        if any((foodx[0] < xcoord < foodx[1] for xcoord in snakex)) or foodx == snakex:
            # Returns True if any of the snake y cooridnates are between the food y coordinates, or both y coordinates match.
            return any((foody[0] < ycoord < foody[1] for ycoord in snakey)) or foody == snakey
        return False


if __name__ == '__main__':
    root = Tk()
    root.title('Snake')
    x, y = 484, 484
    recX, recY = x // 2, y // 2
    recW, recH = recX + 22, recY + 22
    randoms = []
    for i in range(484):
        if i % 11 == 0:
            randoms.append(i)
    blockX, blockY = random.choice(randoms), random.choice(randoms)
    blockW, blockH = blockX + 22, blockY + 22


    snake = Snake(root, recX, recY, recW, recH, blockX, blockY, blockW, blockH)
    threading.Thread(target=snake.run, daemon=True).start()

    root.mainloop()
...