===== Эксперимент 53. Классы ящиков, человека и цели =====
В прошлом эксперименте мы подключили TFT дисплей, создали игровую карту в виде двумерного массива и отобразили ее на экране. Теперь давайте поймем какие вообще объекты задействованы в игровом процессе.
Как мы уже поняли, это стены, ящики, кладовщик и цели, куда нужно перетащить ящики. Теперь нужно описать каждый из классов.
=== Класс ящика ===
Начнем с класса ящика. Главные свойства ящика, это его координаты на карте и его состояние — находится он на цели или нет.
class Box:
def __init__(self, tft, x, y):
self.tft = tft
self.x = x
self.y = y
self.picture = 'box.bmp'
self.picture_onGate = 'boxngate.bmp'
self.onGate = False
self.draw()
def draw(self):
if (self.onGate):
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture_onGate)
else:
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
def setOnGate(self, state):
self.onGate = state
def getOnGate(self):
return self.onGate
def getPos(self):
return (self.x, self.y)
def setPos(self, x, y):
self.x = x
self.y = y
self.draw()
В конструктор мы передаем такие параметры как объект дисплея, координата x, координата y. Координата ящика указывается не в пикселях, а в клетках игрового поля. В самом конструкторе мы задаем имя спрайта для ящика на цели ''self.picture_onGate = 'boxngate.bmp''' и вне цели ''self.picture = 'box.bmp'''. Свойство ''onGate'' как раз описывает состояние ящика. В конце конструктор вызывает метод отрисовки ''draw''.
Метод ''draw()'' предназначена для отрисовки ящика. Если ящик находится на цели, то функция отображает спрайт ''self.picture_onGate'', иначе ''self.picture''. Координата ящика указывается не в пикселях, а в клетках игрового поля, поэтому для отображения картинки ее необходимо пересчитать в пиксели. Для этого координата умножается на размер ящика в пикселях.
Метод ''setOnGate()'' служит для установки свойства нахождения на цели. А метод ''getOnGate()'' для чтения этого свойства.
Метод ''getPos'' возвращает текущие координаты ящика. А метод ''setPos'' устанавливает новые координаты ящика, после чего перерисовывает его на новом месте.
=== Класс цели ===
Класс цели намного проще. Цель никуда не перемещается и свойств не имеет. Цель имеет только координаты и должна отображаться на экране с помощью своего спрайта.
class Gate:
def __init__(self, tft, x, y):
self.tft = tft
self.x = x
self.y = y
self.picture = 'gate.bmp'
self.draw()
def draw(self):
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
def getPos(self):
return (self.x, self.y)
Метод ''draw()'' служит для отрисовки спрайта цели в заданных координатах. Так как координаты цели задаются не в пикселях, а в клетках поля, необходимо пересчитать их в пиксели для отображения. Поэтому координаты в клетках умножаются на размер клетки — 16.
=== Класс кладовщика ===
Кладовщик также имеет координаты своего нахождения на поле и имеет свой спрайт.
class Man:
def __init__(self, tft, x, y):
self.tft = tft
self.x = x
self.y = y
self.picture = 'man.bmp'
self.draw()
def draw(self):
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
def getPos(self):
return (self.x, self.y)
def setPos(self, x, y):
self.tft.rect(self.x * 16, self.y * 16, 16, 16, tft.COLOR_BLACK)
self.x = x
self.y = y
self.draw()
Конструктор и методы ''draw()'' и ''getPos()'' аналогичны одноименным методам предыдущих классов, поэтому не будем повторно заострять на них внимание. Небольшая разница есть в методе ''setPos()''. Отличие заключается в том, что при изменении координаты кладовщика на игровом поле, мы не только рисуем его в новом месте, но и стираем в предыдущем. Это делается с помощью рисования черного прямоугольника по старым координатам кладовщика. Если "старого" кладовщика не закрасить, то он никуда не исчезнет, а просто появится еще один в новом месте. Но почему же мы не закрашивали "старые" ящики при смене их координат? Потому что ящик изменяет координаты только когда его толкает кладовщик. И он сам оказывается на старом месте ящика и закрашивает его собой.
Класса стен не будет. Мы будем пользоваться нашим двумерным массивом в котором уже описали план уровня.
Теперь, когда мы имеем лабиринт и классы для ящика, кладовщика и цели, самое время отобразить это все на дисплее.
==== Схема эксперимента ====
Схема не изменилась.
{{ :products:esp-iot:exp26_mont.png?direct&600 |}}
//Рисунок 1. Монтажная схема эксперимента с 8 выводами//
{{ :products:esp-iot:exp26_mont_11pin.png?direct&600 |}}
//Рисунок 2. Монтажная схема эксперимента с 11 выводами//
==== Программный код эксперимента ====
from machine import Pin, SPI
from tft import TFT_GREEN
_init()
machine.freq(160000000)
dc = Pin(4, Pin.OUT) #a0
cs = Pin(2, Pin.OUT)
rst = Pin(5, Pin.OUT)
spi = SPI(1, baudrate=40000000, polarity=0, phase=0)
# TFT object, this is ST7735R green tab version
tft = TFT_GREEN(128, 160, spi, dc, cs, rst, rotate=0)
Map = [
[1,1,0,1,1,1,0,1],
[0,1,1,1,1,1,1,0],
[1,1,0,0,0,1,1,1],
[0,1,0,1,0,1,0,1],
[0,1,0,0,0,1,0,1],
[1,1,1,1,0,0,0,1],
[1,0,0,0,0,0,0,1],
[1,0,0,0,1,0,0,1],
[1,0,0,0,1,1,1,1],
[1,1,1,1,1,0,0,0]
]
Gates = []
Boxes = []
class Box:
def __init__(self, tft, x, y):
self.tft = tft
self.x = x
self.y = y
self.picture = 'box.bmp'
self.picture_onGate = 'boxngate.bmp'
self.onGate = False
self.draw()
def draw(self):
if (self.onGate):
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture_onGate)
else:
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
def setOnGate(self, state):
self.onGate = state
def getOnGate(self):
return self.onGate
def getPos(self):
return (self.x, self.y)
def setPos(self, x, y):
self.x = x
self.y = y
self.draw()
class Gate:
def __init__(self, tft, x, y):
self.tft = tft
self.x = x
self.y = y
self.picture = 'gate.bmp'
self.draw()
def draw(self):
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
def getPos(self):
return (self.x, self.y)
class Man:
def __init__(self, tft, x, y):
self.tft = tft
self.x = x
self.y = y
self.picture = 'man.bmp'
self.draw()
def draw(self):
self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
def getPos(self):
return (self.x, self.y)
def setPos(self, x, y):
self.tft.rect(self.x * 16, self.y * 16, 16, 16, tft.COLOR_BLACK)
self.x = x
self.y = y
self.draw()
# init TFT
tft.initr(tft.BGR) # tft.initr(tft.RGB) #Если вместо синего цвета отображается красный, а вместо красного синий
tft.clear(tft.COLOR_BLACK) #b, g, r
x = 0
y = 0
for row in Map:
for col in row:
if col:
tft.draw_bmp(x * 16, y * 16,'brick.bmp')
x+=1
x=0
y+=1
Boxes.append(Box(tft, 3,4))
Boxes.append(Box(tft, 4,6))
Boxes.append(Box(tft, 2,7))
Gates.append(Gate(tft, 6,3))
Gates.append(Gate(tft, 6,4))
Gates.append(Gate(tft, 6,5))
man = Man(tft, 5, 6)
Для хранения объектов целей и ящиков создаем массивы:
Gates = []
Boxes = []
Создаем три объекта ящиков с координатами 3,4; 4,6 и 2,7:
Boxes.append(Box(tft, 3,4))
Boxes.append(Box(tft, 4,6))
Boxes.append(Box(tft, 2,7))
Оператор ''append'' добавляет элементы в конец массива.
Создаем объекты целей:
Gates.append(Gate(tft, 6,3))
Gates.append(Gate(tft, 6,4))
Gates.append(Gate(tft, 6,5))
Создаем кладовщика:
man = Man(tft, 5, 6)
Теперь мы имеем игровое поле с лабиринтом, кладовщика, ящики и цели. В следующем эксперименте мы научим кладовщика двигаться при нажатиях на кнопки.