Суть игры простая: прыгать с платформы на платформу и продержаться как можно дольше.
Первое, с чего нужно начать — это анимация. Изображать джампера будем простым кругом. Перед тем, как мы заставим его прыгать, научим его двигаться.
# -*- coding: utf-8 -*- # строка выше нужна для того, чтобы можно было использовать русские буквы в комментариях from tkinter import * root = Tk() root.geometry('800x600') canv = Canvas(root, bg = 'white') canv.pack(fill=BOTH,expand=1) x = 40 y = 450 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) # просто нарисовали круг mainloop()
Попробуем заставить его двигаться:
# -*- coding: utf-8 -*- from tkinter import * root = Tk() root.geometry('800x600') canv = Canvas(root, bg = 'white') canv.pack(fill=BOTH,expand=1) x = 40 y = 450 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) # изменили координаты x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) mainloop()
Мяч двигается, но мы этого не видим. Дело в том, что все происходит слишком быстро. Замедлим процесс
from tkinter import * import time # модуль для работы со временем root = Tk() fr = Frame(root) root.geometry('800x600') canv = Canvas(root, bg = 'white') canv.pack(fill=BOTH,expand=1) x = 40 y = 450 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) time.sleep(0.3) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) time.sleep(0.3) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) time.sleep(0.3) mainloop()
Оказалось, дело было не только в том, что изображение менялось слишком быстро. Оно вообще не менялось! И появилось только после того, как сработала команда mainloop()
Чтобы показать изображение раньше будем использовать canv.update()
from tkinter import * import time # модуль для работы со временем root = Tk() fr = Frame(root) root.geometry('800x600') canv = Canvas(root, bg = 'white') canv.pack(fill=BOTH,expand=1) x = 40 y = 450 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.3) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.3) x += 10 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.3) mainloop()
Вот теперь видно, что круг немного двигается. Добавим цикл:
from tkinter import * import time # подключаем модулть root = Tk() fr = Frame(root) root.geometry('800x600') canv = Canvas(root, bg = 'white') canv.pack(fill=BOTH,expand=1) x = 40 y = 450 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) for z in range(100): x += 5 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.3) mainloop()
Теперь надо увеличить скорость смены кадров, т.е. уменьшить задержку. time.sleep(0.03)
будет в самый раз.
Если вы заметили мерцание, то сразу сообщу, что с мерцанием мы бороться не будем, просто смиримся с ним. Для создания игр больше подходят другие пакеты (pygame,kivy,..) но на данный момент наша задача — это обучение, а не создание красочных игры. Поэтому мы ограничимся tkinter’ом и не будем обращать внимание на мерцание. Kivy будет, но немного позже.
А теперь сделаем так, чтобы шарик менял скорость:
v = 10 for z in range(100): x += v v -= 0.15 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Это первый способ замедления. Обратите внимание, что скорость становится отрицательной и круг начинает двигаться в обратном направлении. Это очень скоро нам пригодится для вертикальной составляющей скорости. А для горизонтальной больше подойдет другой способ:
v = 20 for z in range(100): x += v v *= 0.9 # скорость уменьшается, пока не превратиться в 0 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Теперь отработаем составляющую часть движения:
x = 40 y = 150 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) v = 0 for z in range(100): y += v v += 0.3 # скорость увеличивается, чтобы круг двигался вниз все быстрее и быстрее (координата y растет вниз) canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Как сделать так, чтобы шарик остановился в нижней части экрана? А лучше отскочил от нее и подпрыгнул вверх? Подсказка: используйте цикл while
**** тут должен быть спойлер *****
x = 40 y = 150 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) v = 0 while 1: # вечный цикл y += v v += 0.3 # скорость увеличивается, чтобы круг двигался вниз все быстрее и быстрее (координата y растет вниз) if y > 550: v *= -1 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Теперь объединим движение по вертикали и по горизонтали:
x = 40 y = 150 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) vy = 0 vx = 10 # чтобы было видно движение по горизонтали while 1: # вечный цикл y += vy x += vx vy += 0.3 # скорость увеличивается, чтобы круг двигался вниз все быстрее и быстрее (координата y растет вниз) vx *= 0.99 # при меньших значениях скорость уменьшается слишком быстро if y > 550: vy *= -1 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Отскоки должны быть затухающими, чтобы после удара круг отскакивал вверх не на такую же высоту, а примерно на 70% от первоначальной высоты:
vy = 0 vx = 10 while 1: # вечный цикл y += vy x += vx vy += 0.3 # скорость увеличивается, чтобы круг двигался вниз все быстрее и быстрее (координата y растет вниз) vx *= 0.99 if y > 550: vy *= -0.7 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Не очень-то получилось, правда?
Дело в том, что когда круг уходит ниже, чем 550, то он не всегда может вернуться обратно из-за того, что его обратная скорость стала меньше, чем была.
Например, при скорости 20 круг может пройти по таким координатам: 548, 568. В этот момент мы разворачиваем его, но скорость уменьшаем -20*-0.7 = 14 и он из 568 переходит в точку с y = 568-14 = 554, что ниже, чем 550. Все, круг попался: опять сработает условие y > 550 и опять мы развернем его. Результат вы уже видели.
Решение простое:
vy = 0 vx = 1 while 1: # вечный цикл y += vy x += vx vy += 0.5 # немного увеличим скорость, а то падает слишком медленно для Земли vx *= 0.99 if y > 550: vy *= -0.7 y = 550 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Ну вот, теперь вылезла другая проблема: он прыгает и не успокаивается. Математическую сторону этой проблемы я предлагаю вам увидеть самостоятельно, я же дам самое простое решение:
vy = 0 vx = 6 # скорость снова увеличил, чтобы поймать еще одну проблему while 1: # вечный цикл vy += 0.5 # скорость изменяем до изменения координаты y += vy x += vx vx *= 0.99 if y > 550: vy *= -0.7 y = 550 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Jumper падает с ускорением, как положено, отскакивает, замедляется и … продолжает скользить по горизонтали. Смотрится не очень хорошо, не так ли?
vy = 0 vx = 6 while 1: # вечный цикл vy += 0.5 y += vy x += vx vx *= 0.99 if y > 550: vy *= -0.7 vx *= 0.8 y = 550 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03)
Ну вот теперь просто замечательно! В момент касания земли джампер будет замедлять горизонтальную скорость, чтобы не скользить.
Теперь заставим его прыгать по щелчку мыши. Вы помните, как назначить обработку события? Как определить щелчок мыши на холсте?
***** тут должен быть спойлер *****
from tkinter import * import time # подключаем модулть root = Tk() fr = Frame(root) root.geometry('800x600') canv = Canvas(root, bg = 'white') canv.pack(fill=BOTH,expand=1) def click(event): print('!!!') canv.bind('<1>',click) # создать обработчик события "Щелчок левой кнопкой мыши" для холста и связать его с вызовом функции click # обратите внимание, что эти действия нужно выполнять до цикла, т.к. все, что после цикла не выполняется (цикл-то вечный!) x = 40 y = 150 r = 20 jumper = canv.create_oval(x-r,y-r,x+r,y+r) vy = 0 vx = 6 while 1: # вечный цикл vy += 0.5 y += vy x += vx vx *= 0.99 if y > 550: vy *= -0.7 vx *= 0.8 y = 550 canv.coords(jumper,x-r,y-r,x+r,y+r) canv.update() time.sleep(0.03) mainloop()
После того, как мы убедились, что щелчок мыши обрабатывается, можно начинать «пинать» джампера:
def click(event): global vy vy = -10
Остановим движение по горизонтали:
def click(event): global vy,vx vy = -10 vx = 0
Добавим немного понимания геометрии, чтобы джампер прыгал в сторону мыши (это проще, поэтому начнем с такого варианта).
Чтобы рассчитать значение скорости мы найдем расстояние между точками и возьмем скорость как 1/10 этого расстояния.
На самом деле немного проще, так как составляющие скорости у нас две: vx и vy (скорость по горизонтали и скорость по вертикали). Находим проекцию расстояния между точками на оси, берем 1/10 часть от них — это и будут соответствующие значения скоростей.
def click(event): global vy,vx vx = (event.x - x)/10 vy = (event.y - y)/10
2. Повторить все столько раз, сколько потребуется, чтобы вы могли сами написать этот код.
Следующий урок будет сложнее: мы будем создавать движущиеся платформы. Если вы до конца не понимаете то, что сделано в этом уроке или не можете сами это повторить, то следующий урок будет просто наслоением непонимания. Повторите этот код, даже если для этого потребуется переписывать его 10 раз. (когда-то давно я запомнил с 15-го, несмотря на то, что придумал все это сам)
3. Добавьте цвет джамперу и фону.
4. Поиграйте со значениями скорости и ускорением падения. Сейчас он падает слишком медленно, на мой взгляд.
5. Сделайте отскок от стен слева и справа.