Как сделать отдачу в unity

Добавил пользователь Евгений Кузнецов
Обновлено: 04.10.2024

Учебные материалы для школы программирования. Часть 17

Деревья (плагин SpeedTree)

Моделирование дома в SketchUp

Эффект дождя. Частицы

Стики и работа с Event System

Синтезатор на Unity 3D

Судно на воздушной подушке

Регдоллы на Unity 3D

Как работают векторы. Баскетбол на Unity 3D

В этом проекте рассмотрим процесс работы:

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

Ключевыми темами проекта выступают именно рейкасты и векторы. Последним, необходимо уделять довольно много времени каждый день, и на простых примерах объяснять, как они устроены. Но если вам удалось быстро выполнить проект с учениками, то в этом уроке будет уместно рассмотреть mecanim-систему.

Порядок выполнения

Создаём новый проект, импортируем приложенный ассет.
Помимо стандартных ресурсов пакет имеет сторонний плагин для рисовки декалей. Его работа в контексте данного урока не рассматривается.

Проект урока разбит на 2 части - тир и гранаты.

Тир - самый простой способ научить детей пользоваться рейкастами. Для упрощения, был заранее подготовлен ассет, содержащий все необходимые ресурсы, группе же перекидывается ассет, не содержащий скрипта мишени и её префаба.
Во время этой части урока необходимо объяснить, каким образом работает рейкаст и то, как можно взаимодействовать с другими скриптами из него.


Внутри проекта есть скрипт DecalShooter, который создаёт декали, и в котором расположен весь код стрельбы, включая рейкаст. В нём будет вводиться код взаимодействия с мишенью.
Для начала, необходимо подготовить саму мишень. Ею служит цилиндр, который необходимо уменьшить по Y до состоянии платины, удалить CapsuleCollider и поставить MeshCollider с галочкой Convex. Дополнительно, на цилиндр устанавливается текстура мишени, внутри цилиндра создаётся point light, подсвечивающий мишень, и объект с AudioSource для воспроизведения звука, а на сам цилиндр устанавливается Rigidbody с обработкой коллизий типа Continius Dynamic и галочкой isKinematik. У AudioSource не забудьте убрать галочку PlayOnAwake и закинуть звук попадания в мишень.

Следующим шагом становится создание скрипта Target, который сразу же накладываем на нашу мишень.



Код мишени содержит публичный метод ​shoot,​ который необходим для того, чтобы другие скрипты могли обращаться к нашей мишени. Следующий шаг - проверка из файла DecalShooter попали ли мы в мишень и вызов метода ​shoot. В рейкаст необходимо включить следующий участок кода:

Данный код пытается получить компонент из объекта hitInfo и, если это удаётся, вызывает метод ​shoot.​ Мишень падает, свет от мишени выключается, звук попадания воспроизводится. Далее, желательно дать группе свободное задание по кастомизации своего проекта. Как альтернативу, можно предложить изменить код таким образом, чтобы мишень меняла цвет при попадании. Делается это заменой в Target строк:

Таким образом, свет меняется и не удаляется.

Гранаты

Эта часть урока должна ещё больше закрепить понимание векторов и работы рейкастов.

Для начала, создадим другой скрипт - дальномер.

Скрипт закинем в FPScontroller/FirstPersonCharacter. В Canvas создадим текст, закинем его в скрипт.
В этом скрипте реализован простейший рейкаст, и на его примере мы разбираем, как рейкаст передаёт информацию в структуру и как нам получать из структуры эту информацию.
При срабатывании рейкаста мы выводим дальность на экран.


Следующий шаг - бросок гранаты. Мы не используем никаких анимаций, мы создаём гранату на небольшом расстоянии от игрока и придаём ей скорость по направлению взгляда. Изменим скрипт, добавив в него поля для префаба гранаты и код броска.

Сразу же возникнет вопрос - зачем такое решение? К тому же, при направлении в небо бросок не произойдёт. Сделано всё это в учебных целях. Т.к. в рамках урока код пишется поэтапно, на данном участке объясняется, как найти вектор направления на объект по конечным и начальным координатам. Теперь дело за самой гранатой. Создадим скрипт Grenade.

Закидываем на сцену гранату, на меш Body ставим коллайдеру Convex, добавляем гранате RIgidbody и наш скрипт. Получившуюся гранату добавляем в префаб и удаляем со сцены.


Скрипт удаляет объект, на котором висит, но до этого инстантиирует объект, который мы выбираем в качестве эффекта взрыва.


Создадим эффект взрыва. В нём должен быть свет от взрыва, AudioSource с галочкой PlayOnAwake и звуком взрыва, Spital Blend на 90 процентов переведённый в 3д и увеличенный радиус распространения звука.
Для правильной отработки всех эффектов и разлёта Rigidbody нужно создать ещё один скрипт. Его мы назовём Explosion:

Его нужно закинуть на эффект взрыва и создать префаб.
Данный префаб и используется в скрипте гранаты для создания взрыва.

Сделать всё это в принципе не сложно.
Для начала, нам понадобится прицел для нашего оружия. То есть указатель по центру экрана. Находим или рисуем подходящую нам иконку прицела, с прозрачным фоном. Затем, добавляем на сцену GameObject -> UI -> Image и указываем в качестве источника изображения нашу иконку. Осталось разместить прицел по центру и отрегулировать размер. Внимание! Скрипт стрельбы, нужно вешать именно на этот UI объект.

Напишем небольшой скрипт учета повреждений EnemyHealth и цепляем его на цель:

Тут всё просто, есть "здоровье" и одна публичная функция.

Теперь, займемся скриптом стрельбы, название может быть каким угодно:

Объявляем необходимы переменные:

damage - сколько HP будет отнимать одно попадание.

timeout - темп стрельбы.

targetTags - здесь надо указать теги объектов, у которых будет скрипт EnemyHealth. Например, в игре могут быть различные типы врагов, с разными тегами для определенного взаимодействия. Поэтому, проще использовать текстовый массив, вместо копирования кусков кода.

Так как прицел у нас по центру экрана. Соответственно от центра пускаем луч и при нажатой ЛКМ, если луч попадает на объект у которого есть тег, как в массиве, то отправляем переменную damage.

bullet - префаб снаряда, о котором поговорим позже.

bulletSpeed - скорость пули.

gunPoint - точка, где будут появятся снаряды. То есть создаем пустой объект, затем его надо сделать дочерним модели оружия и разместить так, чтобы этот объект был там-же, где и дуло оружия. Получится, что пули, как бы вылетают из дула.

Стрельба и система повреждений

Скрипт создает объект пули и задет вектор движения, от точки gunPoint - до точки, куда "смотрит" прицел. В общем, как и должно быть.

О префабе пули. На нем обязательно должно быть три компонента: Collider (в режиме триггера), Rigidbody (с отключенной гравитацией) и скрипт Bullet.

Этот урок предполагает, что вы знакомы с интерфейсом Unity и базовыми концепциями скриптинга. Дополнительно, вы должны быть знакомы с концепциями, изложенными в первой части этого урока.

Before we begin — level setup

  • Создайте новый проект (новый! не продолжайте старый!).
  • Скачайте проект с необходимыми файлами и откройте его.
  • Добавьте mainLevelMesh и FPC в сцену.

Weapon switching

Rocket Launcher

Launcher

Rocket


// The reference to the explosion prefab
var explosion : GameObject;
var timeOut = 3.0;

// Kill the rocket after a while automatically
function Start ()
Invoke("Kill", timeOut);
>
Сначала функция Kill ищет системы частиц в иерархии дочерних объектов и выключает их (параметр emit). Затем, открепляет все дочерние объекты от объекта (ракеты в данном случае) и удаляет ракету.

function OnCollisionEnter (collision : Collision)
// Instantiate explosion at the impact point and rotate the explosion
// so that the y-axis faces along the surface normal
var contact : ContactPoint = collision.contacts[0];
var rotation = Quaternion.FromToRotation(Vector3.up, contact.normal);
Instantiate (explosion, contact.point, rotation);

// And kill our selves
Kill ();
>

function Kill ()
// Stop emitting particles in any children
var emitter : ParticleEmitter= GetComponentInChildren(ParticleEmitter);
if (emitter)
emitter.emit = false;

  • Выберите объект RocketLauncher.
  • Перетащите префаб Rocket из WeaponPrefabs в слот параметра Projectile.
  • Запустите игру, тестируйте.

Explosions

  • Перетащите префаб /Standard Assets/Particles/Small explosion на переменную Explosion в скрипте Rocket в префабе Rocket.

function Start ()
var explosionPosition = transform.position;
var colliders : Collider[] = Physics.OverlapSphere (explosionPosition, explosionRadius);
Это возвращает массив коллайдеров, которые попали в сферу.
for (var hit in colliders)
if (!hit)
continue;

if (hit.rigidbody)
hit.rigidbody.AddExplosionForce(
explosionPower, explosionPosition, explosionRadius, 3.0);
Это применяет силу ко всем rigidbodies в пределах радиуса взрывы (сферы).
В принципе, взрыв выглядит неплохо.
var closestPoint = hit.rigidbody.ClosestPointOnBounds(explosionPosition);
var distance = Vector3.Distance(closestPoint, explosionPosition);

// stop emitting ?
if (particleEmitter)
particleEmitter.emit = true;
yield WaitForSeconds(0.5);
particleEmitter.emit = false;
>

  • Добавьте скрипт Explosion-Simple.js к префабу Small explosion.
  • explosionPower — величина силы, с которой взрыв будет передвигать объекты.
  • explosionDamage — величина повреждений.
  • explosionRadius — радиус действия взрыва.
  • Запустите игру.

Machine Gun

  • Создайте пустой объект и назовите его MachineGun. Добавьте его как дочерний объект в объект Weapons.
  • Добавьте меш /Objects/weapons/machineGun в пустой объект MachineGun.
  • Прикрепите скрипт MachineGun.js к объекту MachineGun
  • Поместите muzzle_flash (дочерний объект в machineGun) в переменную Muzzle Flash компонента-скрипта Machine Gun в объекте MachineGun.

function Start ()
hitParticles = GetComponentInChildren(ParticleEmitter);

// Play sound
if (audio)
audio.loop = false;
>
>
>
>
Функция LateUpdate автоматически вызывается после вызова функции Update. Заметьте, что функция Update вызывается в скрипте PlayWeapons.js, который прикреплён к объекту Weapons (родитель объекта MachineGun). В основном, функция LateUpdate будет использоваться тогда, когда нужно реагировать на произошедшее внутри функции Update. В данном случае, если игрок стреляет в функции Update, то в функции LateUp­date будет применяться вспышка (muzzle flash).
function Fire ()
if (bulletsLeft == 0)
return;

// If there is more than one bullet between the last and this frame
// Reset the nextFireTime
if (Time.time - fireRate > nextFireTime)
nextFireTime = Time.time - Time.deltaTime;

// Keep firing until we used up the fire time
while( nextFireTime 0)
clips-- ;
bulletsLeft = bulletsPerClip;
>
>

function GetBulletsLeft ()
return bulletsLeft;
>
Функция Reload перезаряжает оружие. Продолжительность перезарядки — публичная переменная.

Configuring the particle emitter

  • Перетащите префаб /StandardAssets/Particles/Sparks на machineGun в Hierarchy, сделав его дочерним.
  • Это всё. Запустите игру. Оружие переключается клавишами 1 и 2.

Hit Points

Скрипты Explosion.js и MachineGun.js уже показали, как рассчитывать величину повреждений от снарядов (ракеты и пули) и посылать эти значения в повреждаемые объекты. Однако, объекты не знают, как получить (принять) эти значения.
Объекты отслеживают то, насколько они здоровы, с помощью переменной hitPoints. Каждый объект инициализируется со своим значением. Каждый, который объект должен получать повреждения, должен иметь функцию ApplyDamage() (заметьте, что функция вызывается из скриптов Explosion.js и MachineGun.js) Эта функция уменьшает количество HP в игровом объекте и вызывает функции, работающие с объектом, HP которого упали до нуля (обычно, это смерть).
Далее мы продемонстрируем, как использовать hitPoints и ApplyDamage()

Exploding Barrels

Рассматриваемый нами код является типовым и может использовать во многих ситуациях.
Вот полный код DamageReceiver.js:
var hitPoints = 100.0;
var detonationDelay = 0.0;
var explosion : Transform;
var deadReplacement : Rigidbody;

Skybox

  • Edit→Render Settings. Перетащите /Materials/skyBoxTest в параметр Skybox Material.

Prerequisites

Этот урок предполагает, что вы знакомы с интерфейсом Unity и базовыми концепциями скриптинга. Дополнительно, вы должны быть знакомы с концепциями, изложенными в первых двух частях этого урока.

Setting up

  • Откройте предыдущий проект (созданный в части 2).

Waypoints

Robot AI

Robot Damage

  • Прикрепите скрипт CharacterDamage к префабу Robot.
  • Запустите игру и стреляйте по роботу. Он умрёт и исчезнет, ага.

Ragdolls

  • Сначала создайте новую сцену. Мы настроим регдолл здесь. Не забудьте сохранить текущую сцену.
  • Создайте куб больших размеров. Он будет служить платформой для робота. Перетащите робота в сцену и расположите на кубе.
  • Удалите компонент Animation. Это важно, иначе анимация будет конфиликтовать с физикой.
  • Используйте мастера создания регдолла: Gameobject→CreateOther→Ragdoll
  • Сначала, rootHandle назначаем на root.
  • Установите Total Mass на 4.

Upper body / Arms

Using the Ragdoll

  • Откройте исходную сцену, выберите робота в Hierarchy. Перетащите префаб Robot-Ragdoll в переменную Dead Replacement скрипта-компонента Character Damage.
  • Запустите игру и стреляйте по роботу. Должен упасть.

Sound

Machine gun

Rocket Launcher

  • Выберите префаб Rocket.
  • Добавьте компонент Audio Source.
  • Перетащите аудиофайл RocketLauncherFire на параметр Audio Clip.
  • Настройте RolloffFactor (0.5 должно быть достаточно).

GUI (Графический Интерфейс Пользователя) предоставляет пользователю обратную связь и информирует его о текущей ситуации. Стандартные элементы GUI: количество патронов, здоровье, цели миссии и т. п.
В этой игре мы сделаем три элемента GUI.

using UnityEngine ;
using System.Collections ;

public class HealthBar : MonoBehaviour <
//Для показа и скрытия бара
public bool showBar ;
//Ширина бара
public float barWidth ;
//Высота бара
public float barHeight ;
//Хп, которое будет отображаться в баре
public int health ;
public int healthMax ;

// Use this for initialization
void Start ( ) <
//Скрываем бар при старте
showBar = false ;
>

// Update is called once per frame
void Update ( )

void OnGUI ( )
<
//Если бар показывается
if ( showBar )
<
//Создаём строку, которая будет отображаться в 2 вариантах
string str ;
if ( health > 0 ) < str = health + " / " + healthMax ; >
else < str = "Dead" ; >
//Рисуем бар
GUI. Box (
new Rect ( Screen. width / 2 - barWidth / 2, barHeight, barWidth, barHeight ) ,
str ) ;
>
>
>

using UnityEngine ;
using System.Collections ;

public class Health : MonoBehaviour <
//Хп врага
public int health ;
public int healthMax ;

// Use this for initialization
void Start ( ) <
//Хп становится максимальным при старте
health = healthMax ;
>

// Update is called once per frame
void Update ( )


Скрипт, который вешается на камеру. . Камера без Collider. Иначе, добавьте игнорирование коллайдера .

using UnityEngine ;
using System.Collections ;

public class Shoot : MonoBehaviour <
//Префаб с пулей
public GameObject bullet ;
//Сила выстрела
public float force ;
// Use this for initialization
void Start ( )

// Update is called once per frame
void Update ( ) <
//Если нажимается левая кнопка мыши
if ( Input. GetMouseButtonDown ( 0 ) )
<
//Если префаб с пулей указан
if ( bullet )
<
//Создаётся объект (объект, точка создания, его ротация)
GameObject go = Instantiate ( bullet, transform . position , transform . rotation ) as GameObject ;
//Придание объекту ускарение с помощью импульса
go. rigidbody . AddForce ( transform . forward * force, ForceMode. Impulse ) ;
>
>
>
>

using UnityEngine ;
using System.Collections ;

public class Bullet : MonoBehaviour <
//Величина урона
public int damage ;
//Объект со скриптом бара
public GameObject barObject ;
public HealthBar healthBarScript ;

// Use this for initialization
void Start ( ) <
//Находим бар
barObject = GameObject . Find ( "Bar" ) ;
//Получаем бар с найденного объекта
healthBarScript = barObject. GetComponent HealthBar > ( ) ;
>

// Update is called once per frame
void Update ( )

Debug. Log ( "No scripts" ) ;
>
>
//Удаляем объект
Destroy ( gameObject ) ;
>
>

Читайте также: