#28.1 pascal Практика ООП: анимация и заготовка для консольной игры
Primary tabs
В этом уроке мы постараемся привести более "жизненные" примеры использования ООП в простых анимациях и играх
Пример №1 - Перемещение объекта по команде от пользователя -- переход к ООП
Есть код, который, используя обновление консоли как в примере с анимацией, может дать пользователю перемещать символ по экрану влево и вправо:
uses Crt; procedure drawFrame(skin: char; position: integer); var i: integer; begin for i:=1 to (position-1) do write(' '); writeln(skin); end; function getNewPosition(position: integer): integer; var command: char; begin readln(command); if (command = '2') then result := position + 1 else if (position > 1) then result := position - 1; end; // * var position: integer; skin, command: char; begin skin := '*'; // внешний вид command := '1'; // последняя введенная команда position := 1; // текущая позиция while(command <> 'q') do begin ClrScr(); drawFrame(skin, position); position := getNewPosition(position); end; writeln('Программа завершена!'); readln(); end.
его можно переписать в стиле ООП, например, так:
uses Crt; type Animal = class public constructor create(skinValue: char); procedure drawFrame(); procedure handleCommand(); function getCommand(): char; protected skin: char; // внешний вид position: integer; // текущая позиция command: char; // последняя введенная команда function getNewPosition(commandValue: char): integer; end; constructor Animal.create(skinValue: char); begin self.skin := skinValue; self.command := ' '; // значение по умолчанию end; procedure Animal.drawFrame(); var i: integer; begin for i:=1 to (self.position-1) do write(' '); writeln(self.skin); end; procedure Animal.handleCommand(); begin readln(self.command); self.position := self.getNewPosition(self.command); end; function Animal.getNewPosition(commandValue: char): integer; begin if (commandValue = '2') then result := position + 1 else if (position > 1) then result := position - 1; end; function Animal.getCommand(): char; begin result := self.command; end; var position: integer; skin, command: char; AnimalItem: Animal; begin AnimalItem := Animal.create('*'); while(AnimalItem.getCommand() <> 'q') do begin ClrScr(); AnimalItem.drawFrame(); AnimalItem.handleCommand(); end; writeln('Программа завершена!'); readln(); end.
Примечание: для лучшего понимания см. видео-разбор перехода от процедурного стиля к ООП: часть 1 разбора и часть 2 разбора.
Пример №2 - переписываем в ООП стиле более сложное управление сценой
Возьмем пример, где не нужно нажимать Enter для ввода команды и сцена обновляется вне зависимости от того посылает ли пользователь команды или нет:
uses Crt; // импортираем модуль Crt procedure drawFrame(skin: char; position: integer); var i: integer; begin for i:=1 to (position-1) do write(' '); writeln(skin); end; procedure handleCommand(var position: integer; var command: char); begin command := ReadKey(); // вместо readln(command) if (command = '2') then position += 1 else if (position > 1) then position -= 1; end; // * var position: integer; skin, command: char; begin skin := '*'; // внешний вид command := '1'; // последняя введенная команда position := 1; // текущая позиция while(command <> 'q') do begin ClrScr(); // обновляем "кадр" drawFrame(skin, position); // рисуем очередной кадр Delay(500); {далее высчитываем новую позицию только если клавиша нажата, иначе просто отрисовываем кадр заново без изменений } if (keyPressed()) then handleCommand(position, command); // обрабатываем ввод end; writeln('Programma zavershena!'); readln(); end.
И перепишем его в ООП стиль по аналогии с предыдущим примером:
uses Crt; // импортираем модуль Crt type Animal = class public constructor create(skinValue: char); procedure drawFrame(); procedure handleCommand(); function getCommand(): char; protected skin: char; // внешний вид position: integer; // текущая позиция command: char; // последняя введенная команда function getNewPosition(commandValue: char): integer; end; constructor Animal.create(skinValue: char); begin self.skin := skinValue; self.command := ' '; // значение по умолчанию end; procedure Animal.drawFrame(); var i: integer; begin for i:=1 to (self.position-1) do write(' '); writeln(self.skin); end; procedure Animal.handleCommand(); begin self.command := ReadKey(); // вместо readln(command) self.position := self.getNewPosition(self.command); end; function Animal.getNewPosition(commandValue: char): integer; begin if (commandValue = '2') then result := self.position + 1 else if (position > 1) then result := self.position - 1; end; function Animal.getCommand(): char; begin result := self.command; end; var AnimalItem: Animal; begin AnimalItem := Animal.create('*'); while(AnimalItem.getCommand() <> 'q') do begin ClrScr(); AnimalItem.drawFrame(); Delay(500); if (keyPressed()) then AnimalItem.handleCommand(); end; writeln('Programma zavershena!'); readln(); end.
Пример №3 - Вводим класс "Сцены"
Продолжим усовершенствовать наш, пример анимации с ООП из предыдущего примера, где он выглядел так:
uses Crt; // импортираем модуль Crt type Animal = class public constructor create(skinValue: char); procedure drawFrame(); procedure handleCommand(); function getCommand(): char; protected skin: char; // внешний вид position: integer; // текущая позиция command: char; // последняя введенная команда function getNewPosition(commandValue: char): integer; end; constructor Animal.create(skinValue: char); begin self.skin := skinValue; self.command := ' '; // значение по умолчанию end; procedure Animal.drawFrame(); var i: integer; begin for i:=1 to (self.position-1) do write(' '); writeln(self.skin); end; procedure Animal.handleCommand(); begin self.command := ReadKey(); // вместо readln(command) self.position := self.getNewPosition(self.command); end; function Animal.getNewPosition(commandValue: char): integer; begin if (commandValue = '2') then result := self.position + 1 else if (position > 1) then result := self.position - 1; end; function Animal.getCommand(): char; begin result := self.command; end; var AnimalItem: Animal; begin AnimalItem := Animal.create('*'); while(AnimalItem.getCommand() <> 'q') do begin ClrScr(); AnimalItem.drawFrame(); Delay(500); if (keyPressed()) then AnimalItem.handleCommand(); end; writeln('Programma zavershena!'); readln(); end. </pre>
Наша задача далее - ввести для анимации отдельный класс и инкапсулировать в нем реализацию цикла обработки событий, наличие такого класса позволит убрать из объекта живущего на сцене (Animal) взаимодействие с консолью:
uses Crt; // импортираем модуль Crt type Animal = class public constructor create(skinValue: char); procedure drawFrame(); procedure handleCommand(commandValue: char); function getCommand(): char; protected skin: char; // внешний вид position: integer; // текущая позиция command: char; // последняя введенная команда function getNewPosition(commandValue: char): integer; end; Scene = class // Сцена Анимации public procedure handleCommand(); procedure run(AnimalItem: Animal); function getCommand(): char; protected command: char; // последняя введенная команда end; // -- Реализация Scene ------- procedure Scene.run(AnimalItem: Animal); begin // обработки событий while(self.getCommand() <> 'q') do begin ClrScr(); AnimalItem.drawFrame(); Delay(500); if (keyPressed()) then begin self.handleCommand(); AnimalItem.handleCommand(self.getCommand()); end; end; writeln('Scena zavershena!'); end; procedure Scene.handleCommand(); begin self.command := ReadKey(); // читаем символ из консоли end; function Scene.getCommand(): char; begin result := self.command; end; // -- Реализация Animal ------- constructor Animal.create(skinValue: char); begin self.skin := skinValue; self.command := ' '; // значение по умолчанию end; procedure Animal.drawFrame(); var i: integer; begin for i:=1 to (self.position-1) do write(' '); writeln(self.skin); end; procedure Animal.handleCommand(commandValue: char); begin self.position := self.getNewPosition(commandValue); end; function Animal.getNewPosition(commandValue: char): integer; begin if (commandValue = '2') then result := self.position + 1 else if (position > 1) then result := self.position - 1; end; function Animal.getCommand(): char; begin result := self.command; end; // --------------- var AnimalItem: Animal; SceneItem: Scene; begin AnimalItem := Animal.create('*'); SceneItem := Scene.create(); // создаем сцену SceneItem.run(AnimalItem); // запускаем анимацию readln(); end.
-- архитектурно уже видно, что теперь сцена отвечат за напр. за время задержки между кадрами, и за цикл событий, также в run() можно передать более одного объекта и есть переписать способ их отрисовки (опять, же передав сцене, как минимум вывод "пустого пространства"), то на сцене можно будет управлять сразу несколькими объектами, чем мы и займемся далее.
Переносим управление "пространством" в класс сцены, вывод двух объектов
Перепишем последний пример кода, так:
uses Crt; // импортираем модуль Crt type Animal = class public constructor create(skinValue: char); procedure drawFrame(); procedure handleCommand(commandValue: char); function getCommand(): char; function getPosition(): integer; function isCurrentPosition(position: integer): boolean; protected skin: char; // внешний вид position: integer; // текущая позиция command: char; // последняя введенная команда function getNewPosition(commandValue: char): integer; end; Scene = class // Сцена Анимации public procedure handleCommand(); procedure run(AnimalItem, SecondAnimal: Animal); procedure drawSceneFrame(AnimalItem, SecondAnimal: Animal); function getCommand(): char; protected command: char; // последняя введенная команда function getMax(a, b: integer): integer; end; // -- Реализация Scene ------- procedure Scene.run(AnimalItem, SecondAnimal: Animal); begin // обработки событий while(self.getCommand() <> 'q') do begin ClrScr(); // новый метод, для отрисовки кадра и всех объектов self.drawSceneFrame(AnimalItem, SecondAnimal); Delay(500); if (keyPressed()) then begin self.handleCommand(); AnimalItem.handleCommand(self.getCommand()); SecondAnimal.handleCommand(self.getCommand()); end; end; writeln('Scena zavershena!'); end; // Отрисовка кадра сцены procedure Scene.drawSceneFrame(AnimalItem, SecondAnimal: Animal); var maxPosition, i: integer; begin maxPosition := self.getMax(AnimalItem.getPosition(), SecondAnimal.getPosition()); for i:=1 to maxPosition do begin if (AnimalItem.isCurrentPosition(i)) OR (SecondAnimal.isCurrentPosition(i)) then begin if (AnimalItem.isCurrentPosition(i)) then AnimalItem.drawFrame(); if (SecondAnimal.isCurrentPosition(i)) then SecondAnimal.drawFrame(); end else write(' '); end; end; function Scene.getMax(a, b: integer): integer; begin if (a > b) then result := a else result := b; end; procedure Scene.handleCommand(); begin self.command := ReadKey(); // читаем символ из консоли end; function Scene.getCommand(): char; begin result := self.command; end; // -- Реализация Animal ------- constructor Animal.create(skinValue: char); begin self.skin := skinValue; self.command := ' '; // значение по умолчанию self.position := 1; end; procedure Animal.drawFrame(); begin write(self.skin); end; // больше не работаем с консолью напрямую procedure Animal.handleCommand(commandValue: char); begin self.position := self.getNewPosition(commandValue); end; function Animal.getNewPosition(commandValue: char): integer; begin if (commandValue = '2') then result := self.position + 1 else if (position > 1) then result := self.position - 1; end; function Animal.getCommand(): char; begin result := self.command; end; function Animal.getPosition(): integer; begin result := self.position; end; function Animal.isCurrentPosition(position: integer): boolean; begin result := (position = self.position); end; // --------------- var AnimalItem, // первое животное Dog // второе животное : Animal; SceneItem: Scene; begin AnimalItem := Animal.create('*'); Dog := Animal.create('@'); SceneItem := Scene.create(); // создаем сцену SceneItem.run(AnimalItem, Dog); // запускаем анимацию readln(); end.
- в этом коде:
- Класс сцены отвечает за заполнение пустого пространства (если в этой позиции нет объекта) или предлагает объекту нарисовать себя, если для очередной проверяемой координаты объект-животное отвечает. что "да, это мое положение в пространстве"
- Объеты-"животные" больше не работают с консолью напрямую, символ команды считывает объекты Сцена
- Сцена в своем метода отрисовки кадра вызывает методы отрисовки объетов
Подробности:
- Видео: проблемы старого когда для работы с несколькими объектами в сцене (управление "пустым пространством")
- Видео: передаем управление пространством классу Сцена
Пример №5 Разные клавиши для управления положением разных объектов
Можно переписать класс Animal так, чтобы положение каждого объекта этого класса управлялось своими клавишами:
uses Crt; // импортираем модуль Crt type Animal = class public constructor create(skinValue, moveLeftChar, moveRightChar: char); procedure drawFrame(); procedure handleCommand(commandValue: char); function getCommand(): char; function getPosition(): integer; function isCurrentPosition(position: integer): boolean; protected moveLeftChar: char; // клавиша (символ) для движения влево moveRightChar: char; // клавиша (символ) для движения вправо skin: char; // внешний вид position: integer; // текущая позиция command: char; // последняя введенная команда function getNewPosition(commandValue: char): integer; end; Scene = class // Сцена Анимации public procedure handleCommand(); procedure run(AnimalItem, SecondAnimal: Animal); procedure drawSceneFrame(AnimalItem, SecondAnimal: Animal); function getCommand(): char; protected command: char; // последняя введенная команда function getMax(a, b: integer): integer; end; // -- Реализация Scene ------- procedure Scene.run(AnimalItem, SecondAnimal: Animal); begin // обработки событий while(self.getCommand() <> 'q') do begin ClrScr(); // новый метод, для отрисовки кадра и всех объектов self.drawSceneFrame(AnimalItem, SecondAnimal); writeln(); Delay(50); if (keyPressed()) then begin self.handleCommand(); AnimalItem.handleCommand(self.getCommand()); SecondAnimal.handleCommand(self.getCommand()); end; end; writeln('Scena zavershena!'); end; // Отрисовка кадра сцены procedure Scene.drawSceneFrame(AnimalItem, SecondAnimal: Animal); var maxPosition, i: integer; begin maxPosition := self.getMax(AnimalItem.getPosition(), SecondAnimal.getPosition()); for i:=1 to maxPosition do begin if (AnimalItem.isCurrentPosition(i)) OR (SecondAnimal.isCurrentPosition(i)) then begin if (AnimalItem.isCurrentPosition(i)) then AnimalItem.drawFrame(); if (SecondAnimal.isCurrentPosition(i)) then SecondAnimal.drawFrame(); end else write(' '); end; end; function Scene.getMax(a, b: integer): integer; begin if (a > b) then result := a else result := b; end; procedure Scene.handleCommand(); begin self.command := ReadKey(); // читаем символ из консоли end; function Scene.getCommand(): char; begin result := self.command; end; // -- Реализация Animal ------- constructor Animal.create(skinValue, moveLeftChar, moveRightChar: char); begin self.skin := skinValue; self.moveLeftChar := moveLeftChar; self.moveRightChar := moveRightChar; self.command := ' '; // значение по умолчанию self.position := 1; end; procedure Animal.drawFrame(); begin write(self.skin); end; // больше не работаем с консолью напрямую procedure Animal.handleCommand(commandValue: char); begin self.position := self.getNewPosition(commandValue); end; function Animal.getNewPosition(commandValue: char): integer; begin result := self.position; // предыдущее значение, как значение по умолчанию if (commandValue = self.moveRightChar) then result := self.position + 1 else if ((commandValue = self.moveLeftChar) AND (self.position > 1)) then result := self.position - 1; end; function Animal.getCommand(): char; begin result := self.command; end; function Animal.getPosition(): integer; begin result := self.position; end; function Animal.isCurrentPosition(position: integer): boolean; begin result := (position = self.position); end; // --------------- var AnimalItem, // первое животное Dog // второе животное : Animal; SceneItem: Scene; begin AnimalItem := Animal.create('*', '1', '2'); Dog := Animal.create('@', '3', '4'); SceneItem := Scene.create(); // создаем сцену SceneItem.run(AnimalItem, Dog); // запускаем анимацию readln(); end.
-- для лучшего понимания см. видео-разбор перехода к этому состоянию кода.
- Log in to post comments
- 1070 reads