Внимание! В программном
коде ошибка которая не позволяет правильно обрабатывать парент-аттачменты к
модели. Результат может быть немного иным..
В этом туториале я коротко расскажу о работе в разных сферах
девелопинга (коддинг, моделинг, скриптование и маппинг), и как с их помощью их
сделать такой эффект Mystify (Windows XP Screensaver)
http://www.youtube.com/watch?feature=player_detailpage&v=0GmC0Iromrw#t=10s
http://www.youtube.com/watch?feature=player_detailpage&v=p-howMhFecQ#t=11s
только не 2D, а 3D
(действие по всему объему карты).
План таков:
1) Делаем модельку, которая будет излучать связующие лазеры (по желанию
разной толщины, формы и цвета).
2) Кодим модельную энтитю (модель с предыдущего пункта, хотя можно
любую взять) которая будет летать по карте и рандомно менять скорость и угол
полета отбиваясь от всех предметов.
3) Маппим карту для испытаний, цепляем лазеры к энтите.
Приступим.
1) Создание модели Создаем в максе модель этого излучателя (к примеру такую. Калечно, но для примера пойдет):
Сферы- излучатели лезаров. Будут двигаться по этим 2-м отрезкам (палочкам).. Далее делаем 4 (или более) кости для аттачментов (точек, за которые будем цеплять лазеры к модели). Называем LB1, LB2, LB3, LB4. Если у нас будут статические (не движущиеся) лазеры на модели относительно центра модели, то кости и аттачменты делать не нужно. Но я хочу чтобы они анимировали и сдвигались.
Прицепим шары к костям. Берем инструмент Select and link, нажимаем на шар и перетягиваем на кость. Делаем анимацию для каждой из кости:
Нажимаем Auto key, перетаскиваем ползунок на 50-й слайд, выделяем и перетягиваем кость на нужное место (появятся 2 маркера на 0-м и 50-м слайде), зажимаем Shift и перетягиваем 0-й маркер на 100-й слайд (скопировали, чтобы положение кости в начале и в конце анимации было одинаково). Теперь сделаем так с остальными костями.. Теперь выделим наши анимированные кости, открываем Curve editor и выделяем наши кривые линии (с точками) и жмем кнопку Set tangents to linear, которая сделает их ровными.. Это для того, чтобы анимация проигрывалась резко без сглаживания. Модель готова. Скомпилим в игру.. Подробнее о компиляции ->> К примеру у меня такой исходник QC: $cd "D:\Steam\steamapps\sourcemods\ВашМод\modelsrc" $scale 1 $modelname "laser_linker.mdl" $model "body" "laser_linker" $cdmaterials "models\laser_linker" $attachment "laser1" LB1 0,0 0,0 0,0 rotate 0.00 0.00 0.00 $attachment "laser2" LB2 0,0 0,0 0,0 rotate 0.00 0.00 0.00 $attachment "laser3" LB3 0,0 0,0 0,0 rotate 0.00 0.00 0.00 $attachment "laser4" LB4 0,0 0,0 0,0 rotate 0.00 0.00 0.00 $sequence idle "laser_linker" fps 30.00 $sequence fly "laser_linker_ani" fps 30 loop $hboxset "default" laser1- laser4 -имена аттачментов, и LB1- LB4 -имена костей. Имя анимации обязательно должно быть FLY. В конце строчки $sequence fly "laser_linker_ani" fps 30.00 обязательно добавляем параметр loop чтобы анимация повторялась постоянно.. 2) Кодим энтитю.Сначала извлекаем с Source SDK исходный код движка, потом вписываем в него свою энтитю..
Create a Mod -> Source code only Создаем папку с именем мода в папке sourcemods, копируем путь.. Ждем завершения (обычно занимает минут 5-10)...
Открываем файл Game_Episodic-2005 в Microsoft Visual Studio.. жмем Next, отключаем Backup.. ждем завершение обработки.. В древе открываем Server Episodic > Source files и добавляем новый элемент Файл C++. Назовем его sdk_modelentity.cpp и вставляем этот код туда: #include "cbase.h" class CMyModelEntity : public CBaseAnimating { public: DECLARE_CLASS( CMyModelEntity, CBaseAnimating ); DECLARE_DATADESC(); CMyModelEntity() { m_bActive = true; } void Spawn( void ); void Precache( void ); void MoveThink( void ); // Input function void InputToggle( inputdata_t &inputData ); private: bool m_bActive; float m_flNextChangeTime; }; LINK_ENTITY_TO_CLASS( my_model_entity, CMyModelEntity ); // Start of our data description for the class BEGIN_DATADESC( CMyModelEntity ) // Save/restore our active state DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), DEFINE_FIELD( m_flNextChangeTime, FIELD_TIME ), // Links our input name from Hammer to our input member function DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), // Declare our think function DEFINE_THINKFUNC( MoveThink ), END_DATADESC() // Name of our entity's model #define ENTITY_MODEL "models/laser_linker.mdl" //----------------------------------------------------------------------------- // Purpose: Precache assets used by the entity //----------------------------------------------------------------------------- void CMyModelEntity::Precache( void ) { PrecacheModel( ENTITY_MODEL ); BaseClass::Precache(); } //----------------------------------------------------------------------------- // Purpose: Sets up the entity's initial state //----------------------------------------------------------------------------- void CMyModelEntity::Spawn( void ) { Precache(); SetModel( ENTITY_MODEL ); SetSolid( SOLID_BBOX ); UTIL_SetSize( this, -Vector(20,20,20), Vector(20,20,20) ); int iSequence;//this is to store your Sequence number iSequence = LookupSequence("fly");//store ResetSequence( iSequence );//this sets the animation m_flAnimTime = gpGlobals->curtime; SetCycle( 0 ); } //----------------------------------------------------------------------------- // Purpose: Think function to randomly move the entity //----------------------------------------------------------------------------- void CMyModelEntity::MoveThink( void ) { // See if we should change direction again if ( m_flNextChangeTime < gpGlobals->curtime ) { // Randomly take a new direction and speed Vector vecNewVelocity = RandomVector( -300.0f, 300.0f ); SetAbsVelocity( vecNewVelocity ); // Randomly change it again within one to three seconds m_flNextChangeTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 3.0f ); } // Snap our facing to where we're heading Vector velFacing = GetAbsVelocity(); QAngle angFacing; VectorAngles( velFacing, angFacing ); SetAbsAngles( angFacing ); // Think every 20Hz SetNextThink( gpGlobals->curtime + 0.05f ); StudioFrameAdvance(); // DispatchAnimEvents( this ); } //----------------------------------------------------------------------------- // Purpose: Toggle the movement of the entity //----------------------------------------------------------------------------- void CMyModelEntity::InputToggle( inputdata_t &inputData ) { // Toggle our active state if ( !m_bActive ) { // Start thinking SetThink( &CMyModelEntity::MoveThink ); SetNextThink( gpGlobals->curtime + 0.05f ); // Start moving SetMoveType( MOVETYPE_FLY ); // Force MoveThink() to choose a new speed and direction immediately m_flNextChangeTime = gpGlobals->curtime; // Update m_bActive to reflect our new state m_bActive = true; } else { // Stop thinking SetThink( NULL ); // Stop moving SetAbsVelocity( vec3_origin ); SetMoveType( MOVETYPE_NONE ); m_bActive = false; } } (Похожий исходный код с комментариями и учебник по нему: <<Ссылка) Сохраняем, жмем F7 для компиляции.. (занимает около 5-10 мин). Должны получить в логе компила: >Затраченное время: 00:04:37.16 ========== Построение: успешно: 2, с ошибками: 0, без изменений: 0, пропущено: 0 ========== Получаем выходные файлы: D:\Steam\steamapps\sourcemods\CMM\game\server\bin\server.dll D:\Steam\steamapps\sourcemods\CMM\game\client\bin\Client.dll Кидаем их в свой мод (только не в мод созданный на Source cod'е. Если нету- создаем. О создании мода <<Ссылка), в папку bin (создаем). Открываем gameinfo.txt и меняем параметр SteamAppId на 218. ВНИМАНИЕ! тут есть трабла. Код запускается только с 218, а хаммер работает с 420. Приходится переключать (и перезапускать стим) или же создавать еще один мод. Один для маппинга другой с кодом (в него копируем карту +модель +материалы после компила). Если уж так не терпится увидеть наше творение, то подпрыгиваем (а то спавнится в полу) и в прыжке открываем консоль и пишем: give my_model_entity ent_fire my_model_entity toggle (если игрок застрянет в моделе (99% что так оно и будет) пишем в консоль noclip) Первая команда создает энтитю, вторая- активирует ее. Создадим файл конфигурации для хамера. В папке sourcemods\CMM \bin создаем текстовый файл myconfig.fgd Пишем в нем: @PointClass base(Targetname, Angles) studio("models/laser_linker.mdl") = my_model_entity : "Tutorial model entity." [ input Toggle(void) : "Starts and stops the entity moving." ] Заходим в настройки хаммера и добавляем наш новый конфиг: Создаем карту (dev room). Ставим 4 наших энтити my_model_entity в 4-х углах карты (да в принципе все равно куда ставить, они сами разлетятся рандомно по карте). Внимание, сразу программируем префаб, т. е. в конце имен ВСЕХ энтитей добавляем приставку _&&i чтобы они самопереименовывались. Так проще. Имена: laser_spawner_1_&&i laser_spawner_2_&&i laser_spawner_3_&&i laser_spawner_4_&&i ВНИМАНИЕ! В коде энтити ошибка которая на данный момент не исправлена. Мы вынуждены будем поставить возле наших энтитей prop_dynamic_override с моделью models/laser_linker.mdl и припарентить к нашим новым энтитям. Имена: laser_model_spawner_1_&&i laser_model_spawner_2_&&i laser_model_spawner_3_&&i laser_model_spawner_4_&&i Припаренчиваем к рядом стоящим "my_model_entity" Указываем параметр: Render Mode: Dont render Создаем logic_auto. Аутпуты: OnMapSpawn -> laser_spawner_* -> Toggle Delay: 0.00 OnMapSpawn -> laser_spawner_* -> Toggle Delay: 0.10 OnMapSpawn -> laser_model_spawner_* -> SetAnimation Параметр: Fly Звездочка * в имени значит что аутпут будет обращаться ко всем энтитям которые начинаются с данного имени. Подаем 2 аутпута на включение т. к. почему-то с первого энтитя не запускается. Создаем 16 env_beam, программировать их удобно вот в таком положении: Внимание! Во избежание проблем исчезанием лазеров после начала движения следует использовать энтитю env_laser . (выяснилось после теста). Brightness: 255 Beam color: 255 0 0 (я выбрал красный) Life: 0 Width beam: 1 Флаги (опции): Start On Имена (по часовой стрелке, начинаем с левого нижнего): Одна сторона: effect_laser_1_1_&&i effect_laser_1_2_&&i effect_laser_1_3_&&i effect_laser_1_4_&&i Следующая: effect_laser_2_1_&&i effect_laser_2_2_&&i effect_laser_2_3_&&i effect_laser_2_4_&&i И так далее.. Соеденяем лазеры: Имеем 4 групы (грани) по 4 энтити. Указываем первые (по имени) энтити в групе, потом вторые и т. д. Например: Энтитя: effect_laser_1_1_&&i Start entity: effect_laser_1_1_&&i Ending entity: effect_laser_2_1_&&i Энтитя: effect_laser_2_1_&&i Start entity: effect_laser_2_1_&&i Ending entity: effect_laser_3_1_&&i Энтитя: effect_laser_3_1_&&i Start entity: effect_laser_3_1_&&i Ending entity: effect_laser_4_1_&&i Энтитя: effect_laser_4_1_&&i Start entity: effect_laser_4_1_&&i Ending entity: effect_laser_1_1_&&i Свяжем по кругу и у нас получится: Выделяем каждую групу и припаренчиваем к своему prop_dynamic_override Внимание! Если исправить проблему с кодом то будем парентить прямо к нашей энтите.
Переносим каждую группу к припаренченому prop_dynamic_override (к которому парентили). Каждую энтитю лазера располагаем возле своего аттачмента (в нашем случае круглая сфера) Добавим в logic_auto аутпуты для точки аттачмента лазеров: OnMapSpawn -> effect_laser_%_1_&&i -> SetParentAttachmentMaintainOffset С параметром: laser% Задержка: 0.20 OnMapSpawn -> effect_laser_%_2_&&i -> SetParentAttachmentMaintainOffset С параметром: laser% Задержка: 0.20 OnMapSpawn -> effect_laser_%_3_&&i -> SetParentAttachmentMaintainOffset С параметром: laser% Задержка: 0.20 OnMapSpawn -> effect_laser_%_4_&&i -> SetParentAttachmentMaintainOffset С параметром: laser% Задержка: 0.20 Где % это номер группы (1, 2, 3, 4). Всего должно быть 16 аутпутов. Внимание! Если исправить проблему с кодом то следует использовать просто команду SetParentAttachment. Она перенесет и прицепит лазеры в нужное место и групы лазеров не нужно будет переносить. Внимание! Во избежания сбивания лазеров в одну точку рекомендуется не использовать этот пункт (команды SetParentAttachmentMaintainOffset до починки кода). PS. Автор приносит свои извинения за траблу с кодом. PSS. Автор плакалъ.. Теперь нам нужно передвинуть энтити my_model_entity в рядом стоящие prop_dynamic_override. Одна в одну. По размеру они одинаковые. По желанию все энтити выделяются, нажимается кнопка Create prefab и сохраняется. Префаб создан, можно натыкать на карте кучу и наблюдать.. o_0 К примеру мои настройки logic_auto: Не особо то важны самы параметры задержки, главное чтобы сначала срабатывал SetParentAttachmentMaintainOffset, потом SetAnimation и потом Toggle. Все это должно выглядеть примерно так..
Архив со всеми исходниками, скомпиленным кодом, картой, конфигом хаммера, материалами и моделями и их исходников прилагаю. Исходники>> Не бейте сильно.. Код не осилил исправить..
|