#24.2 pascal Управление "сценой": примеры кода и технических приёмов для простой анимации и "игр"

Задача этого урока - познакомить вас с техническими деталями, которые хотя и не входят в основную программу нашего курса базовой алгоритмической подготовки, тем не менее позволят проще управлять консольной программой, напр. не нажимая каждый раз Enter, при вводе каких-либо данных.

Почему эти детали не входят в программу - потому что научиться понимать структуру кода/основные идеи программирования можно и без них, но с ними программы будут куда более "реальными" и настоящими ;)

Прежде чем начать вникать в примеры ниже, посмотрите урок по простейшей анимации в консоли.

Пример №1 -- перемещение символа по экрану

Если использовать обычный способ ввода данных, который известен нам из курса, управлять позицией символа на виртуальной сцене мы можем как-то так:

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 getNewPosition(var position: integer; var command: char);
begin
  readln(command);
  if (command = '2') then
    position := position + 1
  else
    if (position > 1) then
      position := position - 1;
end;

//  *
var position: integer;
  skin, command: char;
begin
  skin := '*'; // внешний вид
  command := '1'; // последняя введенная команда
  position := 1; // текущая позиция
  while(command <> 'q') do
  begin
    ClrScr(); // обновляем "кадр"
    drawFrame(skin, position); // рисуем очередной кадр
    getNewPosition(position, command); // обрабатываем ввод
  end;

  writeln('Программа завершена!');

  readln();
end.  

Примечание: для лучшего понимания см.:

Пример №2 - ввод символа без нажатия Enter

Управление позицией символа можно сильно упростить, если не перейти от readln(), который требует нажатия Энтер для получения значения символьного типа к функции ReadKey() из модуля Crt:

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 getNewPosition(var position: integer; var command: char);
begin
  command := ReadKey(); // вместо readln(command)
  if (command = '2') then
    position := position + 1
  else
    if (position > 1) then
      position := position - 1;
end;

//  *
var position: integer;
  skin, command: char;
begin
  skin := '*'; // внешний вид
  command := '1'; // последняя введенная команда
  position := 1; // текущая позиция
  while(command <> 'q') do
  begin
    ClrScr(); // обновляем "кадр"
    drawFrame(skin, position); // рисуем очередной кадр
    getNewPosition(position, command); // обрабатываем ввод
  end;

  writeln('Программа завершена!');
  readln();
end.

-- это сделает уже более похожим на то, как оно происходит в реальных программах/играх, что мы используем в жизни.

Для лучшего понимания можно посмотреть:

Пример №3 - Продолжаем работу программы/анимацию сцены, даже если пользователь бездействует, но при этом в готовности обработать команду

Обычная ситуация - вы ничего не делаете, но на экране меняется сцена, как добавиться этого - ведь - на выручку приходит функция keypressed() (опять же из модуля Crt), которая возвращает булево значение, показывающее нажата ли сейчас какая-нибудь клавиша или нет.

Перепишем предыдущий пример пример используя новую изученную возможность:

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;

var a, i, j: integer;
  c: char;
begin
  a := 1;
  // начни вводить символы с клавитуры
  // используем цикл для того, чтобы успеть ввести много символов
  for i:=1 to 2000 do
  begin
    for j:=1 to 1000000 do
      a := a + 1;
    a := 1;
  end;

  // начинаем читать поток ввода через ReadKey()
  writeln('Start reading');
  for i:=1 to 10 do
  begin
    c := ReadKey();
    writeln();
    writeln(c);
  end;

  writeln('Programma zavershena!');
  readln();
end.