Эксперимент 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)

Метод daraw служит для отрисовки спрайта цели в заданных координатах. Так как координаты цели задаются не в пикселях, а в клетках поля, необходимо пересчитать их в пиксели для оборажения. Поэтому координаты в клетках умножаются на размер клетки — 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(). Отличие заключается в том, что при изменении координаты кладовщика на игровом поле, мы не только рисуем его в новом месте, но и стираем в предыдущем. Это делается с помощью рисования черного прямоугольника по старым координатам кладовщика. Если «старого» кладовщика не закрасить, то он никуда не исчезнет, а просто появится еще один в новом месте. Но почему же мы не закрашивали «старые» ящики при смене их координат? Потому что ящик изменяет координаты только когда его толкает кладовщик. И он сам оказывается на старом месте ящика и закрашивает его собой.

Класса стен не будет. Мы будем пользоваться нашим двумерным массивом в котором уже описали план уровня.

Теперь, когда мы имеем лабиринт и классы для ящика, кладовщика и цели, самое время отобразить это все на дисплее.

Схема эксперимента

Схема не изменилась.

Рисунок 1. Монтажная схема эксперимента с 8 выводами

Рисунок 2. Монтажная схема эксперимента с 11 выводами

Программный код эксперимента

Exp53.py
  1. from machine import Pin, SPI
  2. from tft import TFT_GREEN
  3. _init()
  4. machine.freq(160000000)
  5.  
  6.  
  7. dc = Pin(4, Pin.OUT) #a0
  8. cs = Pin(2, Pin.OUT)
  9. rst = Pin(5, Pin.OUT)
  10. spi = SPI(1, baudrate=40000000, polarity=0, phase=0)
  11.  
  12. # TFT object, this is ST7735R green tab version
  13. tft = TFT_GREEN(128, 160, spi, dc, cs, rst, rotate=0)
  14.  
  15. Map = [
  16. [1,1,0,1,1,1,0,1],
  17. [0,1,1,1,1,1,1,0],
  18. [1,1,0,0,0,1,1,1],
  19. [0,1,0,1,0,1,0,1],
  20. [0,1,0,0,0,1,0,1],
  21. [1,1,1,1,0,0,0,1],
  22. [1,0,0,0,0,0,0,1],
  23. [1,0,0,0,1,0,0,1],
  24. [1,0,0,0,1,1,1,1],
  25. [1,1,1,1,1,0,0,0]
  26. ]
  27.  
  28. Gates = []
  29. Boxes = []
  30.  
  31. class Box:
  32. def __init__(self, tft, x, y):
  33. self.tft = tft
  34. self.x = x
  35. self.y = y
  36. self.picture = 'box.bmp'
  37. self.picture_onGate = 'boxngate.bmp'
  38. self.onGate = False
  39. self.draw()
  40.  
  41. def draw(self):
  42. if (self.onGate):
  43. self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture_onGate)
  44. else:
  45. self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
  46.  
  47. def setOnGate(self, state):
  48. self.onGate = state
  49.  
  50. def getOnGate(self):
  51. return self.onGate
  52.  
  53. def getPos(self):
  54. return (self.x, self.y)
  55.  
  56. def setPos(self, x, y):
  57. self.x = x
  58. self.y = y
  59. self.draw()
  60.  
  61. class Gate:
  62. def __init__(self, tft, x, y):
  63. self.tft = tft
  64. self.x = x
  65. self.y = y
  66. self.picture = 'gate.bmp'
  67. self.draw()
  68.  
  69. def draw(self):
  70. self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
  71.  
  72. def getPos(self):
  73. return (self.x, self.y)
  74.  
  75. class Man:
  76. def __init__(self, tft, x, y):
  77. self.tft = tft
  78. self.x = x
  79. self.y = y
  80. self.picture = 'man.bmp'
  81. self.draw()
  82.  
  83. def draw(self):
  84. self.tft.draw_bmp(self.x * 16,self.y * 16, self.picture)
  85.  
  86. def getPos(self):
  87. return (self.x, self.y)
  88.  
  89. def setPos(self, x, y):
  90. self.tft.rect(self.x * 16, self.y * 16, 16, 16, tft.COLOR_BLACK)
  91. self.x = x
  92. self.y = y
  93. self.draw()
  94.  
  95. # init TFT
  96. tft.initr(tft.BGR) # tft.initr(tft.RGB) #Если вместо синего цвета отображается красный, а вместо красного синий
  97. tft.clear(tft.COLOR_BLACK) #b, g, r
  98.  
  99. x = 0
  100. y = 0
  101.  
  102. for row in Map:
  103. for col in row:
  104. if col:
  105. tft.draw_bmp(x * 16, y * 16,'brick.bmp')
  106. x+=1
  107. x=0
  108. y+=1
  109.  
  110. Boxes.append(Box(tft, 3,4))
  111. Boxes.append(Box(tft, 4,6))
  112. Boxes.append(Box(tft, 2,7))
  113.  
  114. Gates.append(Gate(tft, 6,3))
  115. Gates.append(Gate(tft, 6,4))
  116. Gates.append(Gate(tft, 6,5))
  117.  
  118. man = Man(tft, 5, 6)

Для хранения объектов целей и ящиков создаем массивы:

  1. Gates = []
  2. Boxes = []

Создаем три объекта ящиков с координатами 3,4; 4,6 и 2,7:

  1. Boxes.append(Box(tft, 3,4))
  2. Boxes.append(Box(tft, 4,6))
  3. Boxes.append(Box(tft, 2,7))

Оператор append добавляет элементы в конец массива.

Создаем объекты целей:

  1. Gates.append(Gate(tft, 6,3))
  2. Gates.append(Gate(tft, 6,4))
  3. Gates.append(Gate(tft, 6,5))

Создаем кладовщика:

  1. man = Man(tft, 5, 6)

Теперь мы имеем игровое поле с лабиринтом, кладовщика, ящики и цели. В следующем эксперименте мы научим кладовщика двигаться при нажатиях на кнопки.