MMORPG Базис

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Вс май 05, 2024 3:20 pm

Как в Delphi 11 в firemonkey проиграть в TMediaPlayer музыку/звуки из встроенных ресурсов. Чтоб работало и на Windows, и на Android

У TMediaPlayer нет метода "загрузи-ка мне вон тот файл из ресурсов".

Ответ первый - воспользоваться сторонними библиотеками, которые это умеют. Типа давно известной BASS. Ну сторонние компоненты не хочется добавлять, там определенные условия использования и т.п.

Ответ второй - создать свой модуль медиакодека и зарегистрировать его через TMediaCodecManager.RegisterMediaCodecClass под поддельное расширение файла. Собственный медиакодек будет отбрасывать расширение файла и грузить ресурс по имени файла. Описано здесь в ответах - https://stackoverflow.com/questions/362 ... m-resource . Сложновато, всю обвязку городить пусть и на основе примера...

Ответ третий - загрузить из ресурса через LoadResource и потом проигрывать через sndPlaySound, но это не будет работать под андроидом =(

Ответ четвертый - вытащить файл из ресурсов во временную папку и потом указать его плееру через MediaPlayer.FileName.

Код: Выделить всё

var
rest:TResourceStream;
fil:string;
begin
  rest := TResourceStream.Create(hInstance, 'mustit', RT_RCDATA);
  fil:=TPath.GetPublicPath+TPath.DirectorySeparatorChar+'mustit.mp3';
  rest.SaveToFile(fil);
  rest.Free;
  MediaPlayer1.FileName:=fil;
  MediaPlayer1.Play();
end;
Временная папка (или "публичная") по TPath.GetPublicPath, как пишут в помощи https://docwiki.embarcadero.com/Librari ... PublicPath , находится:
Windows XP: C:\Documents and Settings\All Users\Application Data
Windows Vista и новее: C:\ProgramData (там хорошо бы создать подпапку для вашей программы, но можно кидать и просто в эту папку как в примере наверху)
Android: /storage/emulated/0/Android/data/<application ID>/files

Все вытаскивается из ресурсов и проигрывается на ура. Главное потом не забыть удалить эти файлы (особенно если это какие-то огромные музыкальные или видео-файлы) чтоб они не занимали место у пользователя после закрытия вашей программы.

Если подумать, то кроме мелких звуков придется грузить и среднюю по размеру музыку, и огромные по размеру видеофайлы (катсцены). Хранить все это в памяти тяжеловато. Временная папка с файлом - решение получше. Видимо поэтому MediaPlayer и проигрывает только из файлов, а не из памяти.

Чтоб играть музыку и звуки одновременно - кидаем на форму несколько MediaPlayer. Заранее в них грузим через MediaPlayer.FileName:='путь и имя файла' файлы (в каждый медиаплеер свой файл). И когда нужно их проиграть вызываем для конкретного плеера перемотку назад и проигрывание:

Код: Выделить всё

MediaPlayer1.CurrentTime:=0;
MediaPlayer1.Play;
Звучащая музыка и звуки с разных плееров сами миксуются и попадают в общий аудиопоток на ваши колонки ПК/динамики телефона.

Чтоб зациклить музыку раньше было событие onNotify, которое вызывалось при смене статуса с Playing на Stopped или другой. В Firemonkey такого события что-то не видно у медиаплеера. Т.к. программа в постоянном цикле отрисовки, то в конец цикла можно добавить проверку на запуск музыки снова если она доигралась до конца:

Код: Выделить всё

if MediaPlayer1.State=TMediaState.Stopped then
begin
MediaPlayer1.CurrentTime:=0;
MediaPlayer1.Play;
end;

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Вс май 05, 2024 8:26 pm

blackstrip писал(а): Пт май 03, 2024 8:59 pm EXE от Delphi 11 работает на Windows XP и выше (если в PE заголовке заменить в настройках Delphi минимальную версию ОС с 6.0 на 5.1. А если заменить на 4.0, то даже в Windows 98 пытается запуститься, сначала ищет DirectX 9, его еще можно поставить, а потом спотыкается на отсутствии Unicode-функций в Windows-овских DLL-ках. Так что минимальная версия - Windows XP).
Опытным путем было установлено, что 32-битные EXE-шники, сделанные в Delphi 11, работают на Windows XP SP3. А если запустить на Windows XP SP2 - то ошибка и вылет. Установка на тот Windows пакета SP3 исправила вылет - программа заработала и на нем.

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Чт май 09, 2024 10:47 am

Обработка неожиданных ошибок (необработанных нормально по try...except) в Delphi 11 работает по-старому.

В начале проги пишем

Код: Выделить всё

Application.OnException:=ExceptHandle;
И отдельно прописываем процедуру ExceptHandle:

Код: Выделить всё

procedure TForm1.ExceptHandle(Sender: TObject; E: Exception);
var
mes:string;
excad:string;
f:Text;
filn:string;
begin
  excad := Format('%p',[ExceptAddr]);
  mes:='ERROR: '+E.Message+'/'+Sender.ClassName+'/'+excad;
  filn:=TPath.GetPublicPath+TPath.DirectorySeparatorChar+'basiserr.txt';
  AssignFile(f,filn);
  if FileExists(filn) then Append(f) else Rewrite(f);
  WriteLn(f,mes);
  CloseFile(f);

  showerrortext:=mes;
  showerror:=true;
end;
В ней сначала получаем адрес исключения, потом сообщение об исключении и имя класса, в котором произошло исключение. По адресу (при включенной генерации отладочного MAP файла) можно узнать по внутренностям MAP файла где конкретно произошла ошибка в исходном коде.

Выкидываем ошибку в общую папку в файл basiserr.txt (на случай если всё совсем рухнуло, или переполнилось и не хватает памяти даже на отрисовку экрана), а заодно и пробуем вывести ее на экране. Например, если нет файла mp3-музыки, указанной медиаплееру в filename, то на экране останется такая одинокая надпись об ошибке и останется только закрыть игру.

Изображение

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Чт май 09, 2024 2:19 pm

Delphi 11 официально не поддерживает Windows XP, но созданные в Delphi 11 проги работают на нем (как выше писалось) если подправить минимальную версию ОС в PE-заголовке на 5.1:
Изображение

Если в Firemonkey рисовать на TPlane3D размером ровно с окно (в basis это по умолчанию 320x320 пикселей), проекцию камеры формы сделать не камерой, а просто вперед куда смотрим при разработке формы (UsingDesignCamera выставить в True), то можно рисовать попиксельно как на обычном рисунке.

В Windows 10 все отрисовывается хорошо:
Изображение

В Windows XP все отрисовывается странно - в правой нижней части окна пиксели сбиты:
Изображение

Затекстурим TPlane3D такой шахматной текстурой чтобы посмотреть что происходит с отрисовкой:
Изображение

Видим вот такое:
Изображение

TPlane3D как просто 3D объект состоит из двух треугольников, первый треугольник рисуется хорошо (но, кстати, сдвинут на один пиксель по горизонтали и вертикали от верхнего-левого угла формы), а правый вообще плохо. Текстура сбивается и рисуется страшно.

Загрузим текстуру с клетками 2x2 пикселя:
Изображение

Видим вот такое:
Изображение

Искажения те же. Посмотрим теперь что происходит с минилиниями. Закрасим разноцветными горизонтальными и вертикальными линиями крупные клетки на текстуре:
Изображение

Вот что стало с линиями:
Изображение

На исходной текстуре клетки кончались белой линией. В окошке этих белых линий (горизонтальных снизу и вертикальных справа) не видно, они срезались.

Увеличиваем окно на 1 пиксель по горизонтали и вертикали, ClientWidth и ClientHeight делаем равными 321. И все сразу выправляется как надо. Белые линии справа и внизу в клетках становятся видны, искажения пропадают:
Изображение

Итог: в Windows XP картинка сдвинута на один пиксель вправо и вниз. Верхний горизонтальный и левый вертикальный ряды точек повторяют соседние (вторые ряды). Delphi пытается масштабировать картинку 320x320 пикселей до оставшегося места 319x319, и текстура искажается.

Чтобы влезло все - надо увеличить область отрисовки на один пиксель - в данном случае, с 320 до 321. Тогда в TImage3D будет растр из двух ненужных рядов пикселей (сверху и слева), а потом наша картинка 320x320 пикселей.

На рендере стартового экрана после этого увеличения окна тоже все рисуется без искажений:
Изображение

На всякий случай, тест в Windows 7 - там и при 320x320 (без увеличения) все рисуется без искажений:
Изображение

Вставляем код исправления в Form.Create при загрузке игры - увеличиваем окно для Windows XP (версия у него 5.1, пропишем меньше 6, т.е. младше висты):

Код: Выделить всё

if TOSVersion.Platform=pfWindows then
begin
  if TOSVersion.Major<6 then
  begin
  Form1.ClientWidth:=321;
  Form1.ClientHeight:=321;
  end;
end;
Теперь отрисовка идет одинаково корректно и в Windows XP, и в Windows 10, и в Windows 7.

Билд с возможностью выбора языка кликом на планету, с шипением ракеты при нажатии на любое место на экране (проверка микширования звука и музыки), с выводом ошибок, с корректной отрисовкой в Windows XP (увеличенное на один пиксель окно):
Win32: http://blackstrip.ru/tmp/basisd11/basis.exe
Android: http://blackstrip.ru/tmp/basisd11/basis.apk

На андроиде имя пакета изменено с com.embarcadero.basis на com.bss.basis, поэтому ставится рядом с предыдущей версией, а не обновляет ее (будет как отдельная вторая прога). Поэтому старую версию вручную удалить надо.

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Чт май 09, 2024 7:05 pm

Как узнать язык системы в Firemonkey

Что под Android, что под Windows - после первого запуска игры нужно узнать язык системы и автоматически выставить его в игре (чтоб не было что англичанину выдается русский интерфейс).

Простая функция, узнающая язык системы. Если не удалось узнать, то выставляем просто en (английский). Если удалось - то в результате будет пара букв, обозначающая язык системы.

Код: Выделить всё

function TForm1.GetCurrentLocale():string;
var
LocServ: IFMXLocaleService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXLocaleService,IInterface(LocServ)) then result := LocServ.GetCurrentLangID() else result:='en';
end;
В игре всего два языка интерфейса - русский (0) и английский (1). Для россиян, украинцев и белорусов ставим русский, а для остальных английский с помощью этого кода:

Код: Выделить всё

lang:=GetCurrentLocale();
if (lang='ru') or (lang='uk') or (lang='be') then Lg.CurLang:=0 else Lg.CurLang:=1;
При последующих запусках после первого запуска надо уже грузить из сохраненных настроек язык, который был при последнем выходе из игры.

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Пт май 10, 2024 11:15 am

blackstrip писал(а): Вс май 05, 2024 3:20 pm Временная папка (или "публичная") по TPath.GetPublicPath, как пишут в помощи https://docwiki.embarcadero.com/Librari ... PublicPath , находится:
Windows XP: C:\Documents and Settings\All Users\Application Data
Windows Vista и новее: C:\ProgramData (там хорошо бы создать подпапку для вашей программы, но можно кидать и просто в эту папку как в примере наверху)
Android: /storage/emulated/0/Android/data/<application ID>/files
Храним файлы цивилизованно в публичной папке

Если посмотреть в папку C:\Program Data на Windows 10, то увидим что каждая программа создает там подпапку для себя (например, "Blizzard Entertainment", "Microsoft"). Basis пока что клал файлы прямо в ее корень и подпапки своей не имел.

В Android же /storage/emulated/0/Android/data/<application ID>/files это собственная папка программы и можно класть файлы в ее корень, что basis и делает.

Функция, создающая подпапку фирмы-производителя программ (BSS), и внутри нее папку программы (Basis) в публичной папке Windows (если этих папок там нет) и возвращающая путь к этой папке программы со слэшем на конце (C:\ProgramData\BSS\Basis\). А для Android просто возвращающая путь к папке программы со слэшем на конце (/storage/emulated/0/Android/data/<application ID>/files/).

Т.к. basis для двух платформ - Windows и Android, то будем проверять Windows ли у нас - и если нет, то будем считать это андроидом. Если писать программу для большего числа платформ, то нужно отдельно проверять if TOSVersion.Platform=pfWindows then ... else if TOSVersion.Platform=pfAndroid then ... else if TOSVersion.Platform=pf<другая платформа> then... и т.д.

Код: Выделить всё

function TForm1.GetPublicFolder():string;
var
toppath:string;
winpath:string;
begin
  toppath:=TPath.GetPublicPath+TPath.DirectorySeparatorChar;
  if TOSVersion.Platform=pfWindows then
  begin
    //windows
    winpath:=toppath+'BSS';
    if not TDirectory.Exists(winpath,false) then TDirectory.CreateDirectory(winpath);
    winpath:=winpath+TPath.DirectorySeparatorChar+'Basis';
    if not TDirectory.Exists(winpath,false) then TDirectory.CreateDirectory(winpath);
    result:=winpath+TPath.DirectorySeparatorChar;
  end
  else
  begin
    //android
    result:=toppath;
  end;
end;
В начале программы сохраняем эту папку в переменную

Код: Выделить всё

pubfold:=GetPublicFolder();
Теперь пользуемся ею везде, например, для распаковки музыки из ресурсов:

Код: Выделить всё

rest := TResourceStream.Create(hInstance, 'mustit', RT_RCDATA);
fil:=pubfold+'mustit.mp3';
rest.SaveToFile(fil);
rest.Free;

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Пт май 10, 2024 8:32 pm

blackstrip писал(а): Чт май 09, 2024 7:05 pm При последующих запусках после первого запуска надо уже грузить из сохраненных настроек язык, который был при последнем выходе из игры.
Сохраняем настройки в публичной папке

При выходе из игры (в событии запроса на закрытие главной формы Form3DCloseQuery) записываем вызов сохранения настроек SaveSettings()

В самой процедуре SaveSettings записываем открытие файла settings.txt с перезаписью и запись всех настроек по очереди. Пока что это две настройки - язык интерфейса и логин, а потом можно будет туда добавить все другие настройки игры:

Код: Выделить всё

procedure TForm1.SaveSettings();
var
f:Text;
s:string;
begin
  AssignFile(f,pubfold+'settings.txt');
  Rewrite(f);
  //CurLang
  s:=IntToStr(Lg.CurLang);
  WriteLn(f,s);
  //login
  s:=login;
  WriteLn(f,s);
  CloseFile(f);
end;
Загружаем настройки из публичной папки

При запуске игры в Form3DCreate после стартовой инициализации (например, выбора языка интерфейса по текущему языку системы и др.) записываем вызов загрузки настроек из файла settings.txt LoadSettings()

В процедуре LoadSettings записываем открытие файла settings.txt для чтения и загрузку всех настроек в той же очередности что и в SaveSettings с проверкой корректности загружаемых значений (мало ли кто подкрутит settings.txt - чтоб игра не вылетала с ошибками после загрузки кривых или битых настроек):

Код: Выделить всё

procedure TForm1.LoadSettings();
var
f:Text;
s:string;
begin
  if not FileExists(pubfold+'settings.txt') then exit;
  AssignFile(f,pubfold+'settings.txt');
  Reset(f);
  try
    //CurLang
    ReadLn(f,s);
    Lg.CurLang:=StrToInt(s);
    if (Lg.CurLang<0) or (Lg.CurLang>1) then Lg.CurLang:=1;    
    //login
    ReadLn(f,s);
    login:=s;
    CloseFile(f);
  except
    CloseFile(f);
  end;
end;

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Пн май 20, 2024 11:30 pm

Запуск и отладка android-программ Delphi 11 в BlueStacks

1. В BlueStacks App Player жмем справа шестеренку (настройки) и включаем ADB. Там же написаны IP-адрес и порт для соединения с андроидовским отладчиком.

Изображение

2. В настройках Delphi смотрим где лежит adb.exe

Изображение

3. Делаем скрипт с командой connect адрес:порт

Изображение

4. Запускаем скрипт, он напишет что удачно соединился (BlueStacks App Player должен быть запущен), ждем некоторое время и потом жмем в Delphi 11 справа от списка выбора устройств для отладки кнопку обновления списка. Автоматически выберется первое устройство - наш эмулятор.

Изображение

5. Теперь можно запускать программу в BlueStacks и отлаживать по шагам или просто запускать без отладки.

Изображение

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Пн май 20, 2024 11:50 pm

Запуск и отладка android-программ Delphi 11 на реальном устройстве через Wi-Fi

1. Включаем Wi-Fi на телефоне и на компьютере с Delphi 11

2. На телефоне в меню разработчика включаем отладку через Wi-Fi нажимая на переключатель справа

Изображение

3. Теперь жмем на сам пункт для настройки соединения

Изображение

4. В открывшемся меню жмем "Подключить устройство с помощью кода подключения"

Изображение

5. Открывается мини-окошко в котором видны код, ip-адрес и порт. Порт при каждом новом спаривании устройства меняется. Оставляем телефон лежать и показывать это окошко

Изображение

6. Пишем скрипт спаривания через ADB командой pair ip-адрес:порт, списывая адрес и порт из окошка на телефоне

Изображение

7. Запускаем скрипт и вводим код из окошка на телефоне, видим сообщение об удачном спаривании. На телефоне в это время окошко пропадет, т.к. код больше не нужен

Изображение

8. Теперь в окне настроек Wi-Fi отладки смотрим ip-адрес и пароль для соединения

Изображение

9. Пишем скрипт на соединение командой connect ip-адрес:порт

Изображение

10. Запускаем скрипт и видим сообщение об удачном соединении с телефоном

Изображение

11. Если захочется проверить какие устройства/эмуляторы были подключены - то можно сделать отдельный скрипт с командой devices и запускать его для проверки соединений

Изображение

12. Скрипт показывает список устройств/эмуляторов, присоединенных через adb connect

Изображение

13. Также как в прошлом посте жмем в Delphi 11 кнопку обновить и видим наш телефон подключенным для отладки по Wi-Fi

Изображение

Это гораздо удобнее для отладки чем соединение через ограниченные по длине провода)

Аватара пользователя
blackstrip
Админ
Сообщения: 1197
Зарегистрирован: Ср янв 02, 2008 1:42 pm
Откуда: Подольск
Контактная информация:

Re: MMORPG Базис

Сообщение blackstrip » Вт май 21, 2024 9:17 pm

TBitmap.TCanvas.BeginScene и TBitmap.TCanvas.EndScene, TBitmap.Map и TBitmap.Unmap - особенности работы под Android

Если в программе под Windows в Delphi 11 можно вызвать у массива рисунков TBitmap.TCanvas.BeginScene, спокойно отрисовать что надо на их канвах и потом вызвать TBitmap.TCanvas.EndScene, то под Android это не прокатит.

Как пишут на форумах, у Android эти BeginScene вроде бы ставятся в очередь и реально вызываются непонятно когда.

Может быть поэтому (а может и по иной причине) чтобы отрисовать что-то на канве массива рисунков под Android нужно делать для каждого рисунка BeginScene, потом отрисовку, потом EndScene. Только так.

Много BeginScene для разных TBitmap одновременно Android-версии программы не нравятся (а в Windows-версии все ок). Вот такие чудеса.

p.s. также под Android наблюдаются глюки при последовательном вызове TBitmap.TCanvas.BeginScene/отрисовке на канве/EndScene с последующим вызовом TBitmap.Map/Unmap, позволяющими получить область памяти TBitmap-данных и рисовать попиксельно прямо там.

Если под Android после TBitmap.TCanvas.BeginScene/отрисовки на канве/EndScene вызвать TBitmap.Map/Unmap и попытаться порисовать попиксельно, то порисовать Delphi 11 даст, но все изменения в пикселях не сохранятся. Т.к., видимо, EndScene ставится в очередь и выполняется когда-то позже, затирая попиксельные изменения Map/Unmap, вызванные в коде после EndScene.

Поэтому под Android сначала Map/Unmap, а только потом BeginScene/EndScene. Ну или вызывать что-то одно без использования второго.

В Windows же можно вызывать их в любом порядке и все будет хорошо рисоваться.

Ответить

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей