Так сложилось, что вот уже год как я проживаю в холодной Финляндии. Изначально приехал сюда учиться, но последние несколько месяцев активно подыскиваю себе работу: занимался массовой рассылкой своего резюме, посетил многие ивенты/конференции, побывал в нескольких офисах разных финских геймдев-контор. Коих, к слову, тут довольно много: благо, местная International Game Developers Association (в отличии от родной, украинской) живет, здравствует и собирает народ на халявное пиво раз в месяц. Так вот, я заметил, очень многие компании (вместительностью 10-20 человек) или переходят, или уже перешли на Unity3D.
Изначально статья планировалась как философское размышление на тему баланса что_прогер_пишет/что_игрок_видит, с примесями полезных сниппетов/хинтов по работе с Unity. На самом деле, во время выполнения тестового задания в одну из компаний, ко мне внезапно нагрянула муза написать подобную статью, посему я пока не знаю, что из этого выйдет.
Для начала, пару десятков слов о себе: уже пошел 10-й год, как я интересуюсь геймдевом — ровно 10 лет назад я склепал первую игру на Паскале, Sea Wars, ака морской бой. Занимался на любительском уровне, в основном в энтузиастксих проектах, пару раз работал в относительно крупных компаниях, набирался опыта. Работал со многими движками: сначала работал с 3D Gamestudio, Ogre, Irrlicht, Leadwerks, и прочими Torque. Unity3D я до этого года, правда, совсем не любил. После Global Game Jam — изменил мнение кардинально.
О учебе: закончил магистратуру в Киевском Политехническом, осознал, что уровень образования оставляет желать лучшего (в группе было всего 3 человека, заинтересованных в программировании), посему удачно захотел и не менее удачно поступил в Финский универ.
Вообще, о житии в Финляндии, универах и подробностях IGDA'вых попоек я расскажу в другой раз, пора переходить к геймдеву.
Тестовое задание было следующим: нужно было написать прототип для полета и посадки самолета, вид сбоку. Редактировать его положение (transform) напрямую — запрещено, можно только применять силы (force) и вращающие моменты (torque). Времени я попросил 2 дня, и задание таки закончил через 2 дня. Правда, еще несколько дней игрался с физикой, и через 4 дня отправил финальную версию, которая меня почти полностью удовлетворила.
Итак, мне в голову пришла такая критеризация кода:
1) много кода и временных затрат, для игрока эффект минимален
2) средне кода, эффект так себе
3) пару строк кода, эффект налицо, публика в шоке
1. Много кода, эффект почти незаметен / или на него не обращают внимания
Самая гемморная и неблагодарная часть. Без нее в игру никто играть не будет, но даже если она будет сделана на отлично, никто на это не обратит внимание: это в порядке вещей. А вот мелкие недочеты сразу бросаются в глаза. Короткое название "Она должна присутствовать, если ее нету — фейл".
Физику, к примеру, я переделывал с нуля несколько раз:
— сначала пробовал просто формулы и реальные значения массы/размаха крыльев/мощности двигателя, правда, позже все равно вводил свои коэффициенты. Для себя отмазался тем, что важны не точные значения, но порядок возрастания: например зависимость подъемной силы от скорости. В итоге, в формулах стало больше моих «открытий», чем реальных формул. Фейл.
— затем сделал суровое лицо, накачал совковских книг по аэродинамике, начал делать все по науке. Впрочем, результат был не намного лучше, так как своих добавлений стало относительно мало.
— полез гуглить подобные авиасимуляторы, поигрался чуток с JSBSim. Ничего удовлетворительного не нашел, но формулы на заметку взял.
— в этот раз я забил на науку и реалистичность, поставил самолету минимальный вес/размах/мощность и начал совмещать весь новоприобретенный опыт. В последний день, по совету друга-любителя ИЛ-2, добавил «клевание» носом, и в целом получилось очень даже мило.
Однако, весь этот труд, естественно, мало заметен при игре: так и должно быть, иначе зачем вообще проект показывать? Что, собственно, вполне логично.
Кусок кода оттуда (вся функция расчета физики довольно большая, приведу самую интересную часть). Комментариев в этом нелегком месте поставил побольше, чтоб ревьювер заценил :)
Я обычно пишу для Unity на шарпе, но попросили написать на JavaScript. Ну, JavaScript так JavaScript…
//// Thrust force
// Ft = const * p * n^2 * d^4,
// where const = 1,3; p = 0.125; n = power; d = screw diameter (2,4 for F4U-1 Corsair)
// Ft = 5.39 * n^2
trustCoeff = rotorVelocity * power*power * powerCoeff;
thrustVector = Vector3(1,0,0) * trustCoeff;
//// Angle of attack, Lift force
// http://wright.nasa.gov/airplane/lifteq.html
// http://wright.nasa.gov/airplane/incline.html
// C = 2 * m * alpha
// L = 1/2 * p * V^2 * A * C , where p = 0.125 [kg * sec^2 / meter^4] at +15C; A = 9.172 for F4U-1 Corsair
// L = 1.14 * V^2 * (m * alpha)
speedWeight = 0.0125 * (velocityForwardVectorSqr/2);
liftCoeff = wingspan * attackAlpha * speedWeight * weightCoeff;
liftVector = Vector3(0,1,0) * liftCoeff;
//// Air resistance = base resistance + inductive resistance
// http://en.wikipedia.org/wiki/Parasitic_drag
// http://en.wikipedia.org/wiki/Lift-induced_drag
// resistBasic = Const * (p * V^2 * S) / 2 = Const' * V^2 * S
// resistInductive = liftForce^2 / (p * V^2/2 * S)
dragCoefficient = 2*airDrag + 2 * airDrag * horizonAngle * wingspanCoeff;
dragVector = - airSpeedVector * dragCoefficient;
В последний момент меня еще и порисовать пробило. Так что у вас появляется эксклюзивная возможность насладиться моими умениями :)
Сильно не бить, я не художник.
2. Средне кода, эффект так себе
Самая средняя и невзрачная часть. Короткое название "Если есть — хорошо, если нету — ну и ладно". Сил тратится средне… хм, а еще дает небольшой полет для фантазии.
Муза стукнула по столу кулаком, и сказала делать Решил я немного выпендриться и сделать процедурную генерацию ландшафта. В данном случае он бывает 2 типов: океан или земля. Земля реализована в майнкрафтовском стиле, из кубиков. Даже не знаю, какой кусок кода привести так, чтоб было не очень долго, но интересно. Делал, кстати, в предпоследний вечер, на ночь глядя, так что рефакторить есть куда.
var firstIslandLength: int = Random.Range(70, 130); // width of island
generationDist = 0;
GenerateIsland(generationDist, firstIslandLength);
var firstOceanLength: int = Random.Range(50, 100);
generationDist += firstIslandLength + firstOceanLength;
GenerateOcean(generationDist, firstOceanLength);
var secondIslandLength: int = Random.Range(10, 30);
generationDist += firstOceanLength + secondIslandLength;
GenerateIsland(generationDist, secondIslandLength);
var secondOceanLength: int = Random.Range(100, 150);
generationDist += secondIslandLength + secondOceanLength;
GenerateOcean(generationDist, secondOceanLength);
var thirdIslandLength: int = Random.Range(70, 80);
generationDist += secondOceanLength + thirdIslandLength;
GenerateIsland(generationDist, secondOceanLength);
GenerateTargetObj(generationDist, secondOceanLength);
}
function GenerateOcean(pos: int, length: int)
{
var newOcean = Instantiate(waterObject, Vector3(pos - 5, -2, 50), Quaternion.identity);
newOcean.transform.localScale.x = length/4;
Debug.Log("Ocean. Pos: " + pos + ". Length: " + length);
}
Схема будет примерно такая:
3. Пару строк кода, эффект налицо
Самая важная и интересная часть данного опуса, особенно для тех, кто делает первые-вторые-третьи шаги в геймдеве и прототипировании. И для тех, кто ждал хинтов по Unity3d. Короткое название "Если она есть — круто, если ее нету — проект выглядит сырым".
В Unity:
— Импортируем пак со скайбоксами (Assets/Import package/skyboxes).
— Создаем новый объект (Game Object/Create Empty), можно взять уже существующий.
— Создаем новый скрипт SkyboxControl
— В скрипт записываем целых 3 строки
var skyboxMaterials: Material[];
function Start ()
{
RenderSettings.skybox = skyboxMaterials[Random.Range(0, skyboxMaterials.length - 1)];
}
— Перетаскиваем скрипт на новоиспеченный объект, жмем на стрелочку возле Skybox Materials, вводим число, например 4: создается и открывается массив из 4 элементов
— Открываем Standard Assets/Skyboxes и заполняем вышеуказанный массив любыми скайбоксами на выбор
Что получаем? На действия потрачена 1 минута (это вместе с отхлебнуть пивка и почухаться), а счастливый юзвер каждый новый перезапуск игры будет наблюдать новый случайный скайбокс.
Еще пример: звук. В моем случае, у меня был звук вращающегося пропеллера, я захотел его красиво продемонстрировать.
Пожалуйста:
— Кидаем звук на озвучиваемый объект
— В недрах Тундры, выдры в гетрах скрипта, где-то в функции Update() прописываем аж 1 строку
soundPlayer.audio.pitch = rotorVelocity;
rotorVelocity — это управляемая игроком скорость вращения винта, меняется на интервале [0..1], зависит от того, как долго юзверь жмет кнопку.
Все, получаем профит. Чем больше будет значение скорости вращения мотора, тем быстрей будет проигрываться звук. Дело половины минуты (вместе с допиванием пива).
Вывод
Не забывайте о таких мелочах. Они делаются довольно быстро (в частности, в Unity3d), а пользы проекту приносят много.
PS: да, не верьте, когда на геймдев конкурсах, или на тестовых заданиях говорят, что графика — не важно. Графика — очень важно, если геймплей не гениальный (что встречается крайне редко, но зато заметен сразу — привет Нотчу). К тому же, ее можно сделать крайне быстро, а проект будет намного казаться законченней.
PPS: и еще одно: если интересно, обязательно зацените эту презентацию. Никакой рекламы: я тех парней вообще в первый раз в жизни вижу. Но дело говорят.
PPPS: в ту компанию так и не взяли. Посему, выкладываю линк на то, что вышло. Реализация мне, в принципе, нравится — переделаю физику еще разок (или доведу эту до кондиции), сделаю пару десятков уровней, да попробую зарелизить на какой-нибудь портал Unity игр. Да, я всегда открыт для конструктивной критики.
Автор: VeTaLv