Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.50/68: Рейтинг темы: голосов - 68, средняя оценка - 4.50
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140

Анимированное изображение в RAD Studio 2010

04.12.2012, 15:47. Показов 13025. Ответов 21

Студворк — интернет-сервис помощи студентам
Здравствуйте!
На 6-ом Билдере я запускал анимацию (.gif) с помощью очень удобного компонента RxGifAnimator библиотеки RxLib.
Проблема в том, что я перешел на RAD Studio 2010, и сейчас снова возникла необходимость отображать анимированное изображение. RxLib на RAD Studio 2010 ставится не хочет, да и способы которые предлагаются (из тех что я видел) не подходят для этой студии.
Подскажите, пожалуйста, компонент, который будет аналогом RxGifAnimator. Ну или помогите мне с RxLib для RAD Studio 2010
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
04.12.2012, 15:47
Ответы с готовыми решениями:

Компиляция в RAD STUDIO 2010
Скомпилированные приложения на RAD STUDIO 2010 не запускаются на других компьютерах. Несколько решений нашёл С++ Linker->Dynmica...

Embarcadero RAD Studio 2010
Где можно скачать Embarcadero RAD Studio 2010 с нормальным кряком?

codegear rad studio 2010
Как запускать приложения в Codegear Rad Studio 2010?

21
 Аватар для Deimon
35 / 35 / 6
Регистрация: 29.08.2009
Сообщений: 183
05.12.2012, 13:06
freemanc, можно поинтересоваться, с какой целью Вы хотите использовать в своей программе именно Gif?

Может у Вас получиться запустить. У меня на CodeGear Builder 2009, в подключаемом файле ругается на строчку
Pascal
1
2
  BYTE(PChar(longInt(Scanline[y]) + x)^) := Value; 
//[DCC Error] GIFImage.pas(8081): E2064 Left side cannot be assigned to
Паскаль плохо помню, поэтому не знаю в чем проблема
.gif анимация в C++ Builder 6
Можете еще по ссылкам там походить, где-то говорилось о том, что при помощи GDI можно проиграть анимацию gif.
0
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
05.12.2012, 16:07  [ТС]
можно поинтересоваться, с какой целью Вы хотите использовать в своей программе именно Gif?
Дипломный проект, путеводитель по техникуму, на форме лежит Image который изображает этаж, сам этаж выполнен в 2х цветах - желтом и фиолетовом. Вторым Image мы ходим по этажу. Определяется цвет пикселя, что бы нельзя было ходить сквозь стены. Второй Image и есть анимация (кроме него на этаже еще несколько фикс. анимаций)
Аналогичный проект уже создавался мною на Делфи 7, там была классная библиотека RxLib. Подскажите мне пожалуйста как эту RxLib установить на RAD Studio 2010
0
 Аватар для Deimon
35 / 35 / 6
Регистрация: 29.08.2009
Сообщений: 183
05.12.2012, 16:45
freemanc, к сожалению как подключить RxLib не смогу подсказать. Но вашу задачу можно решить иным способом. Например проверку на столкновения производить при помощи описанных через HRGN (регионов) стен.
Анимацию сделать с помощью спрайтов, а не gif-анимации.

Если Вы не найдете другого способа выполнить дипломный проект, я мог бы помочь вам с кодом и алгоритмами описанного выше способа. Я как раз сейчас занимаюсь тем, что пытаюсь написать игрушку такого рода.
Миниатюры
Анимированное изображение в RAD Studio 2010  
0
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
05.12.2012, 23:38  [ТС]
Ого!) Если это ваш скрин - то у вас классно получается)
Я изначально хотел лепить что то в этом роде (то что у вас на скрине), но столкнулся с рядом вопросов на которых ответов так и не получил (хотя тема очень интересная, очень хотелось научиться лепить какие то примитивные игрушки и т.д.)
Первая проблема - это то, как все это добро отрисовывается и главное, где? Вот на скрине все графические элементы находятся на/в каком то компоненте (и мне по началу казалось, что это какой то компонент, который позволяет рисовать граф. элементы внутри себя, что то вроде GLScene, тоесть какая то связка компонентов, которая реализовывает некий граф. редактор, элементами которого управлят сама прога)
Спрашивал по этому поводу сдесь - Визуальная среда создания 3D объектов, подключаемая к С++ Builder!, где мне сказали что таких вещей не существует.
Вторая проблема - я понятие не имею что такое HRGN и спрайты)
Но мне очень интересна эта тема, и я был бы очень благодарен вам если бы вы разъяснили мне как это реализовывается

Добавлено через 2 минуты
только не судите меня строго, я еще большой чайник в этих делах))) Хотя свою первую игру создал))) (чисто средствами билдера)

Добавлено через 6 часов 37 минут
Deimon, в любом случае, спасибо вам за идею, возможно теперь начну думать в этом направлении. Единственная просьба, дайте мне пожалуйста какой-нибудь источник, где хорошо описывается работа со спрайтами, с HRGN, ну и с приведенным вами примером... потому что ничего путевого я пока что не нахожу
0
 Аватар для Deimon
35 / 35 / 6
Регистрация: 29.08.2009
Сообщений: 183
05.12.2012, 23:38
freemanc, Очень извиняюсь за отметку "не согласен", чисто случайно нажал, мышь дрогнула , когда я подсказку к кнопке читал))))

На самом деле, на форме нет никаких компонентов кроме 2 Timer'ов. Вся отрисовка происходит на Canvas-е формы. Я сам чайник чайником, даже кипятильник)
Практически всё, что я использовал, нашел на форуме здесь и нескольких игровых форумах.
Завтра, я смогу показать готовые коды для Вас с комментариями. Правда это будет не совсем по теме данного топика, а больше по игровой тематике.
1
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
05.12.2012, 23:47  [ТС]
Было бы замечательно)))
Спасибо, завтра тогда почитаю, постараюсь вникнуть в суть, как я понимаю, посути, каких то больших сложностей быть не должно, по крайней мере надеюсь)

Добавлено через 3 минуты
я уж было подумал, глядя на отметку "не согласен", что своим глупым вопросом вызвал у Вас негодование))
0
 Аватар для Deimon
35 / 35 / 6
Регистрация: 29.08.2009
Сообщений: 183
06.12.2012, 17:59
Часть 1
1. На чем можно рисовать.
У каждого компонента(инструмента) Builder'a, который можно добавить на форму, и у которого есть параметр Visible(отобразить) - есть свой холст(Canvas), на котором этот компонент отрисовывается.
TCanvas (Канва) - это класс, предназначенный для вывода и хранения графических обьектов в C++ Builder
C++
1
2
3
4
MediaPlayer1->Canvas->...
Edit1->Canvas->...
Button1->Canvas->...
Image1->Canvas->...//и т.д.
2. Что можно рисовать.
Битовый массив изображения без сжатия. Изображения формата BMP занимают значительно больше места, чем JPG, GIF или PNG. Но без сжатия, картинка сохраняет свой первозданный вид (тоесть качество не ухудшается), к плюсом так же можно отнести предсказуемый размер файла.
Чтобы загрузить битовую картинку в программу, можно воспользоваться классом TBitmap, находящимся в прострастве имен Graphics. Для этого надо убедиться, что подключена библиотек vcl.h. Примечание: vcl.h автоматически подключается к cpp файлу, созданной формы в C++ Builder.
TGraphic является базовым типовым абстрактным классоом для таких объектов, как значки, растровые и векторные, который может хранить и отображать зрительные образы.
TBitmap является инкапсуляцией растрового Windows (HBITMAP), в том числе палитры (HPALETTE).
Объявление указателя выглядит так:
C++
1
2
 #include <vcl.h>
  Graphics::TBitmap * myPicture;

Не по теме:

На всякий случай - Graphics::TBitmap означает, что создается объект именно того класса TBitmap, что находится в пространстве имен Graphics. (чтобы не возникало конфликтов с другими классами, именнованных так же).
Тоесть:

C++
1
2
3
4
5
6
7
8
9
#include <vcl.h>
Graphics::TBitmap * myPicture;
 
/*эквивалентно такой записи:*/
 
#include <vcl.h>
namespace Grpahics {
  TBitmap * myPicture;
};


Почему создается именно указатель на класс TBitmap, а не сам объект, с уверенностью сказать не могу. Но может быть, здесь есть прямая связь с немаловажной особенностью расположения изображения TBitmap в памяти компьютера:
Создание копий TBitmap происходит очень быстро, так как копируется handle(уникальный идентификатор, 4-8 байт), а не само изображение. Если изображение изменяется и handle является общим для более чем одного объекта TBitmap, изображение копируется до выполнения изменения (то есть, копирование при записи).
2.1 Как использовать TBitmap.
Так как мы создали только указатель на объект класса, а не сам объект, нужно выделить в памяти компьютера место для указателя под этот объект:
C++
1
myPicture = new Graphics::TBitmap();
Указатель указывает, место есть. Теперь загружаем любую картинку формата BMP:
Открываем Paint >> Нажимаем Ctrl+E >> Устанавливаем разрешение 100Х100px >> Рисуем, что угодно, например человечка >> Обзываем и Сохраняем изображение в формате BMP в любом месте.
Далее в программе пишем следующий код, где в качестве параметров метода LoadFromFile() пишем путь к созданной картинке:
C++
1
myPicture->LoadFromFile("C:/pic01.bmp");
2.2 Рисуем
Пришло время что-нибудь нарисовать. Тут все просто.
Чтобы было интереснее, используем Event формы OnMouseMove, которая передает в пользование параметры, из них два параметра - X и Y положения курсора мыши на форме.
Так как задача стоит просто отобразить рисунок, его можно нарисовать на Канве(Canvas) формы. Следовательно обращяемся к Канве и используем метод Draw(int,int,TGraphics*), который принимает три значения. X и Y того места на канве формы, где будет расположен верхний левый угол картинки myPicture, и соответственно указатель на саму картинку.
C++
1
2
3
4
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) 
{
  Form1->Canvas->Draw(X,Y,myPicture); //В качестве положения верхнего левого угла картинки, используется положение курсора
}
Компилируем, запускаем, водим мышью по форме. Получаем каляку маляку =)
Поводив немного мышью, любой в конце-концов догадается почему получается каляка маляка. Но баяны ни кто не отменял, дело в том, что каждый раз при движении мышью на канве формы рисуется изображение (100Х100 px) именно в том месте, где оно должно быть и не пикселем больше. Следовательно, рисуя каждый раз эти 10,000 px в новом месте, на канве остаются следы от предыдущих рисований.

2.3 Рисуем нормально.
По идеи, чтобы рисовать без каляк-маляк, надо стирать прошлый рисунок на канве, и рисовать новый уже на чистом "холсте". Хранить в памяти прошлое расположение рисунка и закрашивать именно то место, не всегда получается возможным. Поэтому можно очистить всю канву формы и рисовать на чистой.
Очистить канву можно методом FillRect(const TRect & Rect), который принимает в качестве установочных параметров объект типа TRect(int,int,int,int). FillRect просто "обнуляет" значеие каждого пикселя в квадратной области, указываемой TRect'ом
Используем этот метод в Эвенте движения мыши перед тем, как рисовать на канве:
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
Form1->Canvas->FillRect(TRect(0,0,Form1->Width,Form1->Height));
Form1->Canvas->Draw(X,Y,map);
}
Получаем нормальное перемещение картинки на форме. Но не идеальное. При не быстром движении мышью можно обратить внимание, что картинка мерцает. Почему именно так, с уверенностью не скажу. Но думаю из-за того, что отрисовка формы и рисование во время перемещения мыши выполняются параллельно.
2.4 Рисуем с умом.
Чтобы момент отрисовки окна(формы) не попадал в момент между рисованием в Эвенте движения мыши, нужно обновлять канву формы за один раз. Тоесть обращяться к канве формы один раз!
Для этого понадобится изрображение с размеры окна. Это будет своего рода буфером, куда надо будет поочередно заносить все изображения(ну пока только одно), а потом разом отрисовывать буфер на форме.
Создаем буфер для рисования:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Unit1.h
...
Graphics::TBitmap * myBuffer;
...
 
//Unit1.cpp
...
myBuffer = new Graphics::TBitmap();
/*Так как конкретная картинка с диска(накопителя) компьютера не 
понадобится, нужно вручную сообщить каких размеров холст нужен
Для этого используем размеры формы*/
myBuffer->Height=Form1->Height; //высота
myBuffer->Width=Form1->Width;   //ширина
...
Теперь в Эвенте движения мыши исправляем код так, чтобы очищалась канва буфера а не окна, и на ней же рисовалась картинка. И только потом буфер отрисовывался на канве формы:
C++
1
2
3
4
5
6
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
  myBuffer->Canvas->FillRect(TRect(0,0,Form1->Width,Form1->Height));
  myBuffer->Canvas->Draw(X,Y,myPicture);
  Form1->Canvas->Draw(0,0,myBuffer);
}
Теперь все круто и почти гладко. Без blur движений конечно же =(, но уже смотрится.

То что я здесь написал, является моими личными наблюдениями, и не являются неоспоримой и единственной инстинной. Моих знаний не было бы в первую очередь без Otaka, KTYJIX(автора темы Создание игры(2d), которую хотелось бы вернуть, а то закончить не получается), QVO, DefineTrueFalse и остальных. Кстати весь этот пост практически дублирует Краткое руководство по работе с классом TCanvas для начинающих

Позже напишу про спрайты, а то я устал сильно-сильно. А редактировать тут нельзя. Не хочу оставлять тему кусками =(
Миниатюры
Анимированное изображение в RAD Studio 2010   Анимированное изображение в RAD Studio 2010   Анимированное изображение в RAD Studio 2010  

Изображения
 
0
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
06.12.2012, 19:59  [ТС]
Deimon, спасибо Вам огомнейшее!) Все прочитанное понял с первого раза, при чем написано настолько доходчиво, что даже в книгах редкость ))) Сейчас попробую нарисовать что нибудь)
Пока что все ясно и понятно, буду с нетерпением ждать новых сообщений)

Добавлено через 1 час 3 минуты
Решил попрактиковаться:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
Graphics::TBitmap *myBuffer;
Graphics::TBitmap *myPicture;
myBuffer = new Graphics::TBitmap();
myPicture = new Graphics::TBitmap();
myPicture->LoadFromFile("C:\\1.bmp");
myBuffer->Height->Form1->Height;
myBuffer->Width=Form1->Width;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
 
{
        myBuffer->Canvas->FillRect(0,0,Form1->Width, Form1->Height);
        myBuffer->Canvas->Draw(X,Y, myPicture);
        Form1->Canvas->Draw(0,0,myBuffer);
}
Но этот код выдает след. ошибки:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[BCC32 Error] Unit1.cpp(11): E2303 Type name expected
[BCC32 Error] Unit1.cpp(11): E2238 Multiple declaration for 'myBuffer'
[BCC32 Error] Unit1.cpp(9): E2344 Earlier declaration of 'myBuffer'
[BCC32 Error] Unit1.cpp(12): E2303 Type name expected
[BCC32 Error] Unit1.cpp(12): E2238 Multiple declaration for 'myPicture'
[BCC32 Error] Unit1.cpp(10): E2344 Earlier declaration of 'myPicture'
[BCC32 Error] Unit1.cpp(13): E2238 Multiple declaration for 'myPicture'
[BCC32 Error] Unit1.cpp(12): E2344 Earlier declaration of 'myPicture'
[BCC32 Error] Unit1.cpp(13): E2141 Declaration syntax error
[BCC32 Error] Unit1.cpp(14): E2238 Multiple declaration for 'myBuffer'
[BCC32 Error] Unit1.cpp(11): E2344 Earlier declaration of 'myBuffer'
[BCC32 Error] Unit1.cpp(14): E2141 Declaration syntax error
[BCC32 Error] Unit1.cpp(15): E2238 Multiple declaration for 'myBuffer'
[BCC32 Error] Unit1.cpp(14): E2344 Earlier declaration of 'myBuffer'
[BCC32 Error] Unit1.cpp(15): E2141 Declaration syntax error
[BCC32 Error] Unit1.cpp(25): E2064 Cannot initialize 'const TRect &' with 'int'
  Full parser context
    Unit1.cpp(24): parsing: void _fastcall TForm1::FormMouseMove(TObject *,TShiftState,int,int)
[BCC32 Error] Unit1.cpp(25): E2342 Type mismatch in parameter 'Rect' (wanted 'const TRect &', got 'int')
  Full parser context
    Unit1.cpp(24): parsing: void _fastcall TForm1::FormMouseMove(TObject *,TShiftState,int,int)
[BCC32 Error] Unit1.cpp(25): E2227 Extra parameter in call to _fastcall TCanvas::FillRect(const TRect &)
  Full parser context
    Unit1.cpp(24): parsing: void _fastcall TForm1::FormMouseMove(TObject *,TShiftState,int,int)
Независимо от того, где я буду объявлять указатели, в .cpp или .h
Подскажите пожалуйста где я лопухнулся
0
 Аватар для Deimon
35 / 35 / 6
Регистрация: 29.08.2009
Сообщений: 183
06.12.2012, 23:57
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//Здесь можно объвлять указатели и переменные, но они будут глобальными, что не желательно для переменных, 
//используемых только в одном файле
Graphics::TBitmap *myBuffer; //Это объвление указателей.
Graphics::TBitmap *myPicture;//И это тоже.
 
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
/*Объявлять переменные можно практически в любом месте, но 
когда выполненять операции с этими переменными должно быть
известно программе. Вообщем, в .h файле и вне блоков нельзя 
совершать операции. Для этого обязательно надо использовать 
объявления функций, но потом всеровно придется использовать 
функцию в каком-то блоке*/
 
//все ниже, это операции с переменными, поэтому они должны быть здесь. 
//Программа знает, что при создании TForm1 она должна будет совершить операции с этими указателями
myBuffer = new Graphics::TBitmap();
myPicture = new Graphics::TBitmap();
myPicture->LoadFromFile("C:\\1.bmp");
myBuffer->Height->Form1->Height;
myBuffer->Width=Form1->Width;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
 
{
        //myBuffer->Canvas->FillRect(0,0,Form1->Width, Form1->Height);  << Здесь тоже ошибка, функция FillRect 
//принимает в качестве установочных значений один параметр типа TRect. У Вас же передается в FillRect четыре 
//параметров типа int. Поэтому 4 параметра углов квадрата, перед отправкой в функцию FillRect необходимо 
//преобразовать в тип TRect. Обратите внимание как написано в следующей строчке
        myBuffer->Canvas->FillRect(TRect(0,0,Form1->Width, Form1->Height));
        myBuffer->Canvas->Draw(X,Y, myPicture);
        Form1->Canvas->Draw(0,0,myBuffer);
}
Проследите, чтобы не было объявлений переменных с одним и тем же именем несколько раз в одной программе. Вы говорили, что пробывали добавлять объявление указателя в .h файл.
Так же, убедитесь, что вы создавали эвент движения мыши через окно Events окна, а не просто скопировали в cpp файл исходный код.

Добавлено через 18 минут
C++
1
2
//myBuffer->Height->Form1->Height;  << Ошибка
myBuffer->Height=Form1->Height;  //знак присваивания =
0
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
07.12.2012, 02:27  [ТС]
Deimon, Благодарю, до меня дошло... протупил конечно жестко. Вы извините что я тут Вам детскими вопросами голову морочу, это скорее всего изза невнимательности.
... а не просто скопировали в cpp файл исходный код.
- я не занимаюсь копи-пастом, все выше набиралось с осмыслением и целью научиться, просто жестко тупанул с объявлением указателей)
Спасибо, таких глупостей в преть совершать не буду)

Добавлено через 27 минут
Сейчас вот заново все сделал, работает гораздо быстрее чем с компонентом Image, это очень радует
0
 Аватар для Deimon
35 / 35 / 6
Регистрация: 29.08.2009
Сообщений: 183
11.12.2012, 11:57
Извиняюсь, что так долго не отвечал, некоторые трудности с интернетом . Если все еще актуально, то продолжу.

1. Что такое спрайт.

Спрайт (англ. Sprite — фея; эльф) — графический объект в компьютерной графике. Чаще всего — растровое изображение, свободно перемещающееся по экрану.

Наблюдение спрайта под несоответствующим углом приводит к разрушению иллюзии. То есть легче всего воспринимать спрайт как перемещающуюся в пространстве проекцию какого-то объёмного тела так, что разница незаметна.
В первых трёхмерных играх, например, Wolfenstein 3D (1992) и Doom (1993), персонажи представлялись в виде двумерных рисунков, изменяющих размер в зависимости от расстояния.

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

2. Теория использования спрайта.

в память компьютера загружается одна картинка с кадрами(frames) какого-нибудь действия. На этой картинке может быть одно или несколько действий, относящихся к одному или нескольким персонажам. (Например в конструкторе игр RPGmaker в одном файле картинки для нескольких персонажей. На экран за раз выводится только один кадр(frame), и через некоторое время(реже при каком-то условии) сменяется следующим. Если действие описано 5 фрэймами, то по достижению 5-ого фрэйма, следующий фрэйм будет 1-ым. В итоге на экране должна получиться зацикленная анимация действия.
Правила для всех спрайта:
1. На кадрах должны быть отображены только объекты на чистом фоне.
2. Все кадры должны быть расположены на одинаковом расстоянии друг от друга.

3. Готовый класс для использования спрайтов.
Принцип работы спрайтов простой и придумать незамысловатый алгоритм для их проигрывания не сложно. Поэтому я предлогаю Вам свой готовый класс для проигрывания анимаций. Но так как я делал его под свои нужды, на него накладывается дополнительные правила ограничения:
...
3. В одной картинке должно быть только одно действие и один персонаж
(Связано с тем, что я не хотел загонять себя в рамки по ограничению кол-ва кадров в анимации(для каких-то действий 3 кадров достаточно, и рисовать например 5 - лень. Или же 5 кадров могут быть не достаточными для замысловатого действия и его лучше отобразить 7-9). В тоже время не хотел загромождать функцию вызова анимацией двадцатью с лишним параметров. Ну и главное, я не придумал простого алгоритма нахождения нужных мне фрэймов в общей куче.)
4. Высота кадра, должна быть равне ширине кадра. (тоесть квадратной).
5. Фрэймы должны быть расположены горизонтально.
Это необходимо, так как я не передаю в вызов функции ширину и высоту фрэйма. Высота фрэйма определяется по высоте всей картинки. Ширина фрэйма определяется по ширине картинке, деленной на кол-во кадров (следует указывать верное кол-во кадров, иначе анимация будет "Нарезанна" не правильно!

Не по теме:

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


6. Картинка должна быть формата BMP.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// h.файл
#define GTB Graphics::TBitmap  //чтобы каждый раз не писать Graphics::TBitmap
 
class CAnimation
{
  private:
    //Используемые переменные. В комменатрии написано для чего нужна каждая.
    int frameCount, frame, frameSpeed, frameHeight, frameWidth, animWait; 
/*кол-во кадров, текущий кадр, время(скорость) смены кадров( в мс), высота кадра, 
ширина кадра, время ожидания до следующего цикла проигрывания анимации(в мс)*/
    
    unsigned long nextFrame; 
/*Время когда нужно сменить кадр*/
    bool autoreverse, reverse; 
/*1-ая переменная обозначает Как проигрывать анимацию. 1-2-3-1-2-3 или 1-2-3-2-1-2-3. 
2-ая переменная для того, чтобы знать в какую сторону в данный момент идут кадры(на убывание 
или возрастание) и используется только если autoreverse==true*/
    GTB * pAnimation;
    GTB * pFrame;
/* pAnimation - сюда загружается вся картинка с анимацией
   pFrame - здесь хранится текущий кадр*/
  
  public:
    //Методы класса
    CAnimation(void); //конструктор
    virtual ~CAnimation(void); //деструктор, не помню почему он виртуальный (о_О)
    CAnimation(const CAnimation&); //конструктор копий
    void Copy (const CAnimation & _org);  //копирование в ручную, потому что конструктор копий в stl::vector не работал
    int Animate(void); // проиграть анимацию проверив не настало ли время следующего кадра.
/*Возвращяет:
0 - проигрывать кадр не требуется. В анимации всего 1 кадр
1 - проигрывать кадр не требуется, потому что время следующего кадра не настало
2,3 - кадр сменился на следующий.*/
    GTB* Draw(void); //возвращает pFrame, чтобы нарисовать его
    void LoadAnimation(int framesCount,int fps,bool autorevers,int waitting,String Path); //загрузить новую анимацию
/*framesCount - кол-во кадров в анимации
  fps - скорость смены кадров
  autorevers - как зацикливать анимацию 1-2-3-1-2-3 или 1-2-3-2-1-2-3
  waitting - значение для переменной aniWait. Когда цикл анимации завершился - сколько времени выждать перед следующим циклом
  Path - путь к файлу с анимацией*/
};
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//cpp.файл
 
CAnimation::CAnimation(void) {
  pFrame = new GTB();
  pFrame->Transparent= true;
  pAnimation = new GTB();
  pAnimation->Transparent= true;
}
CAnimation::~CAnimation(void) {
  delete pFrame;
  delete pAnimation;
}
CAnimation::CAnimation(const CAnimation& _org)
{
  pFrame = new GTB();
  pFrame->Transparent= true;
  pAnimation = new GTB();
  pAnimation->Transparent= true;
  animWait= _org.animWait;
  frameCount= _org.frameCount;
  frame= _org.frame;
  frameSpeed= _org.frameSpeed;
  frameHeight= _org.frameHeight;
  frameWidth= _org.frameWidth;
  nextFrame= _org.nextFrame;
  autoreverse= _org.autoreverse;
  reverse= _org.reverse;
  _org.pAnimation->Transparent= false;
  pAnimation->Height= _org.pAnimation->Height;
  pAnimation->Width= _org.pAnimation->Width;
  pAnimation->Canvas->Draw(0,0,_org.pAnimation);
  pFrame->Height= frameHeight;
  pFrame->Width= frameWidth;
}
void CAnimation::LoadAnimation(int _count, int _fps, bool _reverse, int _wait, String _Path)
{
  animWait= _wait;
  frameCount= _count;
  frameSpeed= _fps;
  autoreverse= _reverse;
  //pAnimation->LoadFromResourceName((unsigned int)DataDLL,_Path); //Чтобы загружать из DLL (Вам это пока не понадобится), заменена LoadFromFile
  pAnimation->LoadFromFile(_Path);
  frameHeight= pAnimation->Height;
  frameWidth= pAnimation->Width / _count;
  frame= 0;
  reverse= false;
  pFrame->Height= frameHeight;
  pFrame->Width= frameWidth;
  nextFrame= GetTickCount() + _fps;
}
 
int CAnimation::Animate(void) {
  if (frameCount > 1)
  {
    if (GetTickCount() > nextFrame)
    {
      if (reverse)
      {
        if (frame <= 0)
        {
          reverse= false;
          frame= 0;
          nextFrame= GetTickCount() + animWait;
          return 3;
        }
        else
        {
          frame--;
        }
      }
      else
      {
        if (frame >= frameCount-1)
        {
          if (autoreverse)
          {
            reverse= true;
            frame--;
          }
          else
          {
            frame= 0;
            nextFrame= GetTickCount() + animWait + frameSpeed;
            return 3;
          }
        }
        else
        {
          frame++;
        }
      }
      nextFrame= GetTickCount() + frameSpeed;
      return 2;
    }
    return 1;
  }
  return 0;
}
 
GTB* CAnimation::Draw(void){
  pFrame->Canvas->FillRect(TRect(0,0,frameWidth,frameHeight));
  pFrame->Canvas->CopyRect(TRect(0,0,frameWidth,frameHeight),pAnimation->Canvas,TRect(frame*frameWidth,0,frame*frameWidth+frameWidth,frameHeight));
  return pFrame;
}
 
void CAnimation::Copy (const CAnimation & _org)
{
  _org.pAnimation->Transparent= false;
  pAnimation->Height= _org.pAnimation->Height;
  pAnimation->Width= _org.pAnimation->Width;
  pAnimation->Canvas->Draw(0,0,_org.pAnimation);
  animWait= _org.animWait;
  frameCount= _org.frameCount;
  frame= _org.frame;
  frameSpeed= _org.frameSpeed;
  frameHeight= _org.frameHeight;
  frameWidth= _org.frameWidth;
  nextFrame= _org.nextFrame;
  autoreverse= _org.autoreverse;
  reverse= _org.reverse;
  pFrame->Height= frameHeight;
  pFrame->Width= frameWidth;
  pFrame->Transparent= true;
}
Примечание: перменная bool transporent, пространства имен Graphics::TBitmap задает прозрачность картинки (прозрачная == true). Прозрачность у BMP картинки выглядит следующим образом: цвет нижнего левого пикселя задает TransparentColor, и при рисовании картинки с transparent==true, на фоне другой картинки игнорируются все пиксели с значением цвета равным TransparentColor. Тоесть трудяга выводится на экран без белого фона.


4. Создаем объект анимации
Название: slave.JPG
Просмотров: 744

Размер: 3.2 Кб
Я вырезал из спрайтов Warcraft'а "трудягу рубящего дерево" и расположил фрэймы в соответствии с правилами, сохранил как BMP картинку и кинул в корневую папку диска "С".
Создайте новый проект или продолжите старый >> Через "Add...", добавьте к проекту готовый класс Animation >> Теперь его надо подключить в хэдере формы и создать объект этого класса:
C++
1
2
3
4
5
6
7
8
// h.файл
...
//подключаем добавленный в проект файл с классом.
#include "Animation.h>
...
private:
    CAnimation anim; //создаем где-нибудь(например в привате) объект класса CAnimation
...
Теперь обращяемся к объекту, чтобы загрузить в него анимацию.
C++
1
2
3
4
5
6
//cpp.файл
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  anim.LoadAnimation(5,200,false,300,"C:/slave.BMP");  //5 кадров, 200 миллисекнд между кадрами, повторять по принципу 1-2-3-1-2-3, по окончанию цикла, ждать 300 мс и сразу же начинать новый, путь к картинке
}
4.1. Задаем цикл смены кадров
Для того, чтобы загруженная анимация проигрывалась, нужно переодически обращяться к объекту anim с методом Animate(void); Чем чаще обращяемся - тем лучше, но слишком часто не стоит, чтобы не грузить процессор. если скорость смены кадров 200 мс, то обращение каждые 50-100мс будут, наверно, достаточно.

Для того чтобы зациклить подобное обращение к объекту, я использую компнент TTImer.
Добавляем TTImer из вкладки system на форму >> Выставляем Interval=30 (например) >> Кликаем два раза на компнент TTimer или же переходим во вкладку Events и создаем событие OnTimer. И там пишем обращение к anim.Animate();
C++
1
2
3
4
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  anim.Animate();
}
Теперь анимация проигрывается и в frame каждые ~200мс обновляется значение кадра

4.2. Выводим анимацю на экран
Раньше для перерисовки на экране картинки Вы использовали событие создаваемое перемещением мыши. Для того чтобы экран сам перерисовывался, можно воспользоваться тем же TTImer. Так как ресурсов задействовано не много(всего одна анимация), можно перерисовывать в том же Timer1 и не создавать новый. Мы как раз установили Interval 30 мс, что ровно 1000/30=33 кадрам в секунду.

Так как вывод анимации выглядит так же как и рисование BMP картинки, нужно просто заменитть myPicture на вызов метода Draw объекта anim:

C++
1
2
3
4
5
6
7
8
9
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  anim.Animate();
  myBuffer->Canvas->FillRect(TRect(0,0,Form1->Width,Form1->Height));
  //myBuffer->Canvas->Draw(X,Y,myPicture);
  myBuffer->Canvas->Draw(40,50,anim.Draw()); //Заменили указатель на картинку myPicture, функцией объекта класса CAnimation, которая возвращяет указатель на картинку pFrame. 
/*Обратите внимание, что X,Y заменены на констатные знаечния. Так как это больше не событие движения мыши и этих переменных нет в этом пространстве имен*/
  Form1->Canvas->Draw(0,0,myBuffer);
}


На самом деле, если понять принцип, то все очень просто, скорее всего у Вас могут возникнуть вопросы, когда будете разбирать код, спрашивайте, постараюсь по возможности отвечать. Так же вы можете реализовать свой класс, заточенный под ваши нужды Может у Вас получиться решить задачу с наименьшими ресурсными затратами или упрощенным алгоритмом. Буду рад полезной критике и советам, т.к. являюсь новичком в этом деле.
Вот готовый к подлюкчению класс CAnimation http://zalil.ru/34057163
Миниатюры
Анимированное изображение в RAD Studio 2010  
2
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
13.12.2012, 17:12  [ТС]
Если все еще актуально, то продолжу.
- конечно актуальна, спасибо что не забываете про тему)
Вопросы конечно же возникли, и чем их больше, тем мне печальнее становиться...(ввиду, скорей всего, моей недалекости и идиотизма)
По коду не понятно следующее:
1)
C++
1
GetTickCount()
непонятно что это за функция и что она считает
2)
C++
1
2
3
4
5
6
7
int CAnimation::Animate(void) {
  if (frameCount > 1)
  {
    if (GetTickCount() > nextFrame)
    {
      if (reverse)
      {
дальше этих строчек я вообще почти ничего не понимаю что происходит в этой функции...
По реализации:
Попробовал создать проект по примеру, но в нем ничего не происходит, тоесть: добавил хеадер файл, потом пытался вставлять код с определением функций и в сам Unit1.cpp, и через Project->Add to project..., но мне это не помогло.
Конечно, я чувствую что ошибка будет банальной и нубской, Вы уж меня простите что морочу Вам голову, мне реально сложно разбираться в этом...
Посмотрите пожалуйста что не так у меня в программе:
Вложения
Тип файла: rar Sprites.rar (472.6 Кб, 22 просмотров)
0
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
13.12.2012, 17:22  [ТС]
внутрь я положил 2 спрайта которые использовал, может дело в них...
0
 Аватар для Deimon
35 / 35 / 6
Регистрация: 29.08.2009
Сообщений: 183
14.12.2012, 09:50
Цитата Сообщение от freemanc Посмотреть сообщение
Попробовал создать проект по примеру, но в нем ничего не происходит
У Вас все правильно написано, ошибка была в том, что Вы создали объект TBitmap, но не указали его размеров.
C++
1
2
3
4
5
        anim.LoadAnimation(4,150,false,300,"C://2.bmp");
        myBuffer = new Graphics::TBitmap();
 
        myBuffer->Height=Form1->Height;//Добавьте вот эти строки
        myBuffer->Width=Form1->Width;//...
Тоесть, у Вас получается, что все отрисовыввается на холсте размером 0 на 0 пикселей, а потом этот холст 0на0 отрисвывается на форме. Такую проблему можно выявить взглянув на цвет формы. Так как FillRect(...) делает заливку белым цветом, а форма по умолчанию сероватая, то это означает, что либо myBuffer'у не чего отрисовывать, либо что на форму не чего не отрисовывается.


Цитата Сообщение от freemanc Посмотреть сообщение
GetTickCount()
C++
1
DWORD WINAPI GetTickCount(void);
Возвращает количество миллисекунд, прошедших с момента запуска системы, до 49,7 дней.
Тоесть, когда вы загружаете виндус, включается счетчик переменной типа unsigned long, начиная отсчитывать с 0, и каждую миллисекунду прибавляет +1. (1секунда=1000миллисекуд) И так каждую секунду счетчик увеличивается на 1000, до тех пор, пока память, выделенная для хранения этой переменной не ичерпает свой лимит и не начнет отсчет заново. Наступит это по данным Micrisoft'a через 49,7 дней. Чтобы воспользоваться этой функцией, необходимо подключить Winbase.h (include Windows.h)
Следовательно, если Вы напишите программу, базирующуюся на таком счетчике. И которая не будет защищена от ошибок, связанных с тем, что в один "прекрасный" момент счетчик начнет свой отсчет с нуля. И продадите ее, то можете смело тратить полученные деньги, так как 49 дней больше, чем 14 дней (срок возврата товара продавцу...)


Цитата Сообщение от freemanc Посмотреть сообщение
непонятно что это за функция и что она считает
При помощи GetTickCount() в функции считается не наступило ли время сменить кадр. Смена кадра происходит не буквально - просто меняется переменная frame, которая хранит значение какой по счету кадру сейчас рисовать. А рисуется в методе Draw(), по значению переменной frame.
переменная nextFrame, преднезначена для хранения числа, возвращяемого функцией GetTickCount();
Когда наступиоло время сменить кадр, то счетчик frame изменяется, а в переменную nextFrame заносится теущее "время" возвращяемое GetTickCount() и прибавляется скорость "fps", указываемая при инициализации анимации методом LoadAnimation(...)
C++
1
nextFrame= GetTickCount() + animWait;
Следующий кадр не сменится до тех пор, пока GetTickCount() не вернет число больше, чем число хранимое в nextFrame.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
int CAnimation::Animate(void) {
  //frameCount - количество кадров
  if (frameCount > 1)  //если кадров больше, чем 1, то проверить не наступило ли время сменить кадр. Если анимация 
//состоит из одной статичной картинки, то зачем производить какие-нибудь расчет?! Если будет возвращено false, то 
//вызов этого метода закончится быстро.
  {
    if (GetTickCount() > nextFrame) //Наступило ли время сменить кадр? Если время не наступило, то завершить 
//вызов метода Animate(). 
    {
      if (reverse) //если при инициализации анимации было указано, что анимация должна идти по схеме 
//1-2-3-2-1 (на это указывает autoreverse равная true), и сейчас как раз кадры идут на убывание 3-2-1, то выполнить 
//этот блок код, если autoreverse был указан в false, то reverse всегда будет равен false, и эта часть кода будет 
//пропускаться
      {
        if (frame <= 0)  //достигнуто начало анимации - тоесть первый кадр, и теперь надо отсчитывать 
//вперед. Тут я накосячил, и правильнее будет написать if (frame <= 1)
        {
          reverse= false;  //чтобы кадры увеличиывались на 1
          frame= 0; //frame выставлен в положение первого кадра
          nextFrame= GetTickCount() + animWait; //время следующей смены кадра
          return 3;  //метод завершается возвратом значения 3.
        }
        else  //если анимация идет на убывание, и первый кадр еще не наступил=> продолжаем 
//отсчитывать кадры уменьшая frame на единицу. 
        {
          frame--;
        }
      }
      else  //если кадры идут на увеличение. Не важно по какой схеме 1-2-3-2-1 или 1-2-3-1-2-3. Сейчас 
//выполняется первый шаг: 1-2-3... 
      {
        if (frame >= frameCount-1)  //проверяет не достигнут ли последний кадр, если да, то...
        {
          if (autoreverse) //Загруженная анимация выполняется по схеме 1-2-3-2-1
          {
            reverse= true;  //теперь следующие вызовы метода Animate() будут выполняться в 
//первой части кода, когда счетчик идет на убываение.
            frame--; //...и уменьшаем значение кадра на единицу.
          }
          else  //Загруженная анимация не выполняется по схеме 1-2-3-2-1, следовательно она 
//выполняется по кругу
          {
            frame= 0; //...а это значит что следующий кадр будет первым.
            nextFrame= GetTickCount() + animWait + frameSpeed; //записываем время следующей 
//смены кадра: nextFrame будет присвоенна сумма значений ткекущего времени, плюс скорость fps, и плюс animWait
//(значение которому присваивается при инициализации LoadAnimation(...)... оно служит для того, если нам нужна 
//задержка при анимации, тоесть схема получается "1-2-3-подождать animWait миллисекунд-1-2-3..."
            return 3; //завершить выполнение метода Animta()
          }
        }
        else  //если конец анимации не достигнут....
        {
          frame++; //...увеличить анимацию на единицу
        }
      }
      nextFrame= GetTickCount() + frameSpeed;  //время следующей смены кадра. (Текущее время+ время fps)
      return 2;  //завершить  Animate() 
    }
    return 1;  //завершить Animate(), потому что время следующего кадра не настало
  }
  return 0;  //завершить Animate(), потому что в анимации всего один кадр
}
Я вот тут понял, можно было выход из Animate() и присваивание следующего кадра делать каждый раз при изменении значения frame, а то как то не красиво получается=)) , да и почему то у меня нет задержки через animWait при анимации по схеме 1-2-3-2-1, а ведь могло бы пригодиться.
2
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
19.12.2012, 02:01  [ТС]
Спасибо большое, теперь все работает нормально) Вроде бы с кодом разобрался, все ясно и понятно, Вы здорово излагаете материал, Вам бы книги обучающие писать)))) С нетерпением буду ждать нового материала))
0
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
07.01.2013, 19:10  [ТС]
Вобщем решил, опираясь на ваш класс, и на то что нашел в другой теме, создать свой собственный для диплома.
Вроде бы все получилось, анимация проигрывается, но почему то в самом конце анимации она мигает, такое впечатление что кадра не хватает, или идет какая то задержка перед проигрышем её заново.
Не могли бы Вы глянуть, что не так?
Вложения
Тип файла: rar Animation.rar (6.7 Кб, 24 просмотров)
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
08.01.2013, 00:23
Аа ?? Установка библ. RxLib на RAD Studio 2010 (C++ Builder)
0
 Аватар для freemanc
14 / 14 / 6
Регистрация: 27.11.2012
Сообщений: 140
09.01.2013, 14:12  [ТС]
Avazart, та тема тоже нужна была, т.к. мне все равно нужно было найти способ проигрывать анимацию в TImage (просто не факт, что это НЕ понадобится). Тем более что у меня в проекте есть объект в котором хранится изображение, которое будет изображать фикс. анимацию. Так что тема очень мне пригодилась))))
Кстати Вы не знакомы с описанием регионов через HRGN ? Не могли бы помочь?
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
09.01.2013, 16:06
Да инфы в инете по моему достаточно по регионам, да и в Архангельском пример есть, а по форуму ищите про нестандартной формы окна.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
09.01.2013, 16:06
Помогаю со студенческими работами здесь

Embarcadero RAD Studio 2010
Установил на комп Embarcadero RAD Studio 2010, при запуске С++ Builder 2010 отурывается Opera???:)))

Глюк в RAD Studio 2010
Здравствуйте хочу узнать почему когда я создаю новый проект то у меня создаётся проект с Form2 и unit 2, ведь должно быть Form1 и Unit 1...

Касперский 2012 vs Rad Studio XE 2010
Антивирус Касперского обнаружил троян в папке Embarcadero я его обработал а потом призадумался стоило ли это делать. Кокого ваше мнение...

Настройка интерфейса Rad Studio XE 2010
Установил среду разработки Rad Studio XE 2010. Кто вкурсе как настроить внешний вид Rad Studio по цвету, ато привык к серому цвету Buildera...

С Embarcadero RAD Studio 2010 на XE8
Доброе время суток уважаемые форумчане! Я не являюсь программистом по этому строго не судите) Суть моей проблемы в том, что я написал...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
Киев стоит - украинская песня
zorxor 28.01.2026
wfWdiRqdTxc О Господи, Вечный, Ты . . . Я помоги, Бесконечный. . . Я прошу Ты. . . Я погибаю, спаси. . . Я прошу Тебя Вечный. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL3_image
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
влияние грибов на сукцессию
anaschu 26.01.2026
Бифуркационные изменения массы гриба происходят тогда, когда мы уменьшаем массу компоста в 10 раз, а скорость прироста биомассы уменьшаем в три раза. Скорость прироста биомассы может уменьшаться за. . .
Воспроизведение звукового файла с помощью SDL3_mixer при касании экрана Android
8Observer8 26.01.2026
Содержание блога SDL3_mixer - это библиотека я для воспроизведения аудио. В отличие от инструкции по добавлению текста код по проигрыванию звука уже содержится в шаблоне примера. Нужно только. . .
Установка Android SDK, NDK, JDK, CMake и т.д.
8Observer8 25.01.2026
Содержание блога Перейдите по ссылке: https:/ / developer. android. com/ studio и в самом низу страницы кликните по архиву "commandlinetools-win-xxxxxx_latest. zip" Извлеките архив и вы увидите. . .
Вывод текста со шрифтом TTF на Android с помощью библиотеки SDL3_ttf
8Observer8 25.01.2026
Содержание блога Если у вас не установлены Android SDK, NDK, JDK, и т. д. то сделайте это по следующей инструкции: Установка Android SDK, NDK, JDK, CMake и т. д. Сборка примера Скачайте. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru