Это старая версия документа!


Эксперимент 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. Монтажная схема эксперимента

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

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

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