#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
- 1743 reads