Доброго времени суток! В этой статье я хочу поделиться опытом разработки своей игры с использованием игрового движка Unity.
Концепция игры заключается в том что вам нужно взять на себя управление звездолетом и уничтожить как можно большее количество метеоритов. На вашем пути будут появляться вражеские звездолеты, которые будут мешать вам и после их уничтожения будут появляться «капсулы» после подбора которых будет доступен новый тип оружия. Игра будет называться Galaxy Desteroid.
Разработка
Графика игры состоит из следующих текстур:
На основе этих текстур были созданы следующие префабы
где:
asteroidrotate — метеорит который нужно уничтожать
enemy — вражеский звездолет
explosionasteroid, explosionenemy, explosionplayer — это анимации взрыва созданные с использованием particle system
gunactivator(s) — это капсулы которые будут активировать разный тип оружия в игре
Все остальное типа laser и т.п. это и есть оружие.
Игра будет включать в себя 2 сцены: главное меню и игровая сцена.
Главное меню
Где «menu» это главное меню а «1» это игровая сцена.
В качестве фона я использовал спрайт с именем «space», на нем нарисован космос.
Далее создаем скрипт «menu.cs» (Щелкаем правой кнопкой > выбираем Create > C# Script) и «вешаем» его на background. background это спрайт со 100% прозрачностью на котором создаются основные элементы управления (START/EXIT) а space служит просто для декорации.
Содержимое скрипта:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using UnityEngine.SceneManagement;
public class menu : MonoBehaviour {
public GUIStyle mystyle;//объявляется для того чтобы изменять начертание GUI компонентов(шрифт, размер и.т.п.)
string score;
void Start ()
{
StreamReader scoredata = new StreamReader (Application.persistentDataPath + "/score1.gd");//создание файловой переменной
score = scoredata.ReadLine ();//чтение строки
scoredata.Close ();//закрытие файловой переменной
}
// Update is called once per frame
void Update () {
}
void OnGUI(){
GUI.Box (new Rect (Screen.width*0.15f, Screen.height*0.8f, Screen.width*0.7f, Screen.height*0.1f), "MAX DESTROYED:"+score,mystyle);
if (GUI.Button (new Rect (Screen.width*0.15f, Screen.height*0.25f, Screen.width*0.7f, Screen.height*0.1f), "START",mystyle))
{
SceneManager.LoadScene (1);//Загрузка игровой сцены
}
if (GUI.Button (new Rect (Screen.width*0.15f, Screen.height*0.4f, Screen.width*0.7f, Screen.height*0.1f), "EXIT",mystyle))
{
Application.Quit();//Выход из игры
}
}
}
Еще не забываем повесить на «space» скрипт activemenu. Он служит для того чтобы создать анимацию движения фона меню. Потом создаем копию «space» и ставим ее чуть выше.
Содержимое скрипта activemenu:
using UnityEngine;
using System.Collections;
public class activemenu : MonoBehaviour {
float speed=-0.1f;
void Start () {
}
// Update is called once per frame
void Update () {
transform.Translate (new Vector3 (0f,speed,0f));
if (transform.position.y < -12f)
{
transform.position=new Vector3(0f,13f,0f);
}
}
}
Должно получиться примерно вот так:
Создание игровой сцены
Игровая сцена состоит из следующих ключевых объектов:
космос (спрайт «space»)
игрок
метеориты
вражеские корабли
прочее(лазеры и взрывы).
Космос организован также как в главном меню. Здесь можно ничего не трогать.
Далее нужно обратить внимание на то что в игре будут постоянно генерироваться из префабов, такие объекты как метеориты, выстрелы и враги. И те объекты которые упустил игрок нужно удалять, чтобы в лишний раз не нагружать память.
Это можно сделать следующим образом. Создаем новый игровой объект на сцене(у меня это «controlcountobjects»), добавляем к нему компонент boxcollider и растягиваем его вокруг игровой зоны.
Далее добавляем на него скрипт со следующим содержимым:
using UnityEngine;
using System.Collections;
public class systemcontrolobjects : MonoBehaviour {
void Start ()
{
}
void Update () {
}
void OnTriggerExit2D(Collider2D col)
{
Destroy(col.gameObject);
}
}
В этом скрипте происходит удаление элементов при выходе из box collider объекта controlcountobject. Таким образом получается некая зона при выходе из которой происходит удаление объектов.
Генерация метеоритов
Добавляем данный скрипт на камеру:
using UnityEngine;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class blockgenerator : MonoBehaviour {
public GameObject asteroid;//Добавляем сюда метеорит из префабов
float x,y,timer;
float timerespawn=0.25f;//Период возрождения метеоритов. С помощью данной переменной можно контролировать плотность трафика метеоритов.
bool trigtime=false;//Переключатель для отсчета времени. Если его значение true то тогда отсчитывается время для генерации следующего метеорита.
public int score;//Подсчет общего числа подбитых метеоритов за текущую игру и если данное число превысит рекорд то тогда оно будет записано в файл рекорда. С этой переменной будут взаимодействовать другие скрипты. Каждый созданный метеорит будет передавать команду, которая будет увеличивать данную переменную на единицу.
public float data;
void Start ()
{
score = 0;//
timer = timerespawn;
StreamReader scoredata = new StreamReader (Application.persistentDataPath + "/score1.gd");
data = float.Parse(scoredata.ReadLine ());
scoredata.Close ();
}
void Update ()
{
if (timer==timerespawn)//Если установлено время для отсчета генерации метеорита то:
{
x = Random.Range (-2.5f, 2.5f);//1)Выбираем рандомную координату в которой появится метеорит из динамического диапазона.
Instantiate(asteroid, new Vector3(x,5.5f,-2.17f),transform.rotation);//2)Генерация метеорита из префаба. x выбирается рандомно из указанного выше диапазона.
trigtime = true;//3)Активируем переключатель для отсчета времени. После его активации в течение времени указанного в timerrespawn метеориты не будут появляться.
}
if (trigtime==true)//Проверка переключателя
{
timer = timer-Time.deltaTime;//Отсчет времени для генерации слудующего метеорита.
}
if (timer < 0)//Если время периода истекает то тогда:
{
timer = timerespawn;//1)Сброс времени на число записанное в timerespawn
trigtime = false;//2)Блокирование отсчета времени
//Здесь происходит возврат значений на "по умолчанию". При их сбросе снова будет происходить генерация метеоритов и отсчет времени. И так по кругу
}
}
}
Метеорит
Выше можно заметить, то что метеориты которые появляются на сцене разного размера и еще они вращаются. Это все потому что метеорит реализован с помощью двух GameObject, где один находится внутри другого (матрешка).
Внешний объект «asteroidrotate» описывает движение метеорита, содержит circle collider и запускает эффект взрыва, в случае столкновения, а «aster» рандомно при своем создании задает скорость направления вращения и размер.
Скрипт для asteroidrotate:
using UnityEngine;
using System.Collections;
public class asteroidlogic : MonoBehaviour {
public GameObject explosion;//Здесь нужно добавить префаб взрыва
float speedasteroid=-0.1f;//Скорость движения метеорита
void Start ()
{
}
void Update ()
{
transform.Translate (new Vector3 (0, speedasteroid, 0f));//Движение метеорита вниз по экрану с заранее указанной скоростью
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "systemcontrol")
{
return;//Возврат необходим чтобы избежать конфликта с объектом controlcountobjects
}
if (col.tag == "laser") //Проверка на попадание выстрелов игрока в метеорит
{
Destroy (col.gameObject);//Удаляем сам выстрел
GameObject.Find ("Main Camera").GetComponent<blockgenerator> ().score++;//Добавляем единицу к общему числу подбитых метеоритов
Instantiate(explosion, transform.position,transform.rotation);//Генерируем взрыв из префабов
Destroy (this.gameObject);//Удаление метеорита
}
}
}
Скрипт для aster:
using UnityEngine;
using System.Collections;
public class rotator : MonoBehaviour {
int f;
float sc;
void Start ()
{
f = Random.Range (-5, 5);
sc = Random.Range (0.1f,0.2f);
transform.localScale = new Vector3 (sc,sc,sc);//размер
}
void Update ()
{
transform.rotation *= Quaternion.AngleAxis (f,new Vector3(0,0,1));//вращение
}
}
Игрок
Кидаем спрайт с звездолетом на игровую сцену, добавляем на него box collider(не забываем отмечать «Is Triger»), добавляем rigibody(нужно заморозить Y координату). Все это необходимо чтобы игрок мог взаимодействовать с другими игровыми объектами.
Можно еще добавить компонент particle system, который будет изображать струю из двигателя.
В скрипте для игрока происходит установление «связи» с такими префабами как explosionplayer, laser, laser3x, laser3xhor и т.п. а также организация управления и подбор «капсул» для активации других видов оружия. Управление устроено так что игровой объект просто движется за пальцем пользователя и попутно ведет огонь.
Содержимое скрипта:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class carconroller : MonoBehaviour
{
public GameObject laser;//Эта переменная будет связана с префабом laser
public GameObject laser3x;//Эта переменная будет связана с префабом laser3x
public GameObject laserhor;//Эта переменная будет связана с префабом laserhor
public GameObject laser3xhor;//Эта переменная будет связана с префабом laser3xhor
public GameObject sphere;//Эта переменная будет связана с префабом sphere
public GameObject sphere3x;//Эта переменная будет связана с префабом sphere3x
public GameObject explosionplayer;//Эта переменная будет связана с префабом explosionplayer
float x,y,z,x1,y1;
bool trigtime=false;
public float speedreset=0.25f;//период перезарядки орудия в миллисекундах
float timer;
Vector2 startpos;
Vector2 startcar;
//объекты laser...sphere3x являются оружием а ниже булевы переменные их соответствующие активаторы
public bool gun1 = true;//По умолчанию активировано 1 оружие(laser), оно является базовым и имеет неограниченный боезапас, в отличие от других видов(laser3x...)
public bool gun2 = false;
public bool gun3 = false;
public bool gun4 = false;
public bool gun5 = false;
public bool gun6 = false;
//При активации оружия отличного от базового боезапас будет заканчиваться. Когда боезапас закончится будет автоматически активировано базовое оружие.
int guncount=0;//Количество боезапаса для оружия, которое отличается от базового. Для каждого из дополнительного вида оружия устанавливается свое число боезапаса.
void Start ()
{
timer = speedreset;
y = laser.transform.position.y;
z = laser.transform.position.z;
}
public void Update ()
{
if (timer < 0)
{
timer = speedreset;
trigtime = false;
}
if (Input.GetMouseButton(0))//Отслеживание нажатия на экран
{
Vector2 pos = Camera.main.ScreenToWorldPoint (Input.mousePosition);//Запись в переменную pos координат места, где произошло касание экрана.
transform.position = pos;//присвоение позиции игровому объекту координат из переменной pos
transform.position = new Vector2 (transform.position.x,transform.position.y+1f);//корректировка координат игрока(необязательно)
if (timer==speedreset)//проверка истечения времени(время перезарядки)
{
if (gun1 == true)//проверка базового орудия
{
Instantiate (laser, new Vector2(transform.position.x,transform.position.y+1.1f), transform.rotation);//Генерация выстрелов из префаба laser в месте текущей позиции игрока(с некоторыми поправками)
trigtime = true;
}
if (gun2 == true && guncount > 0)//В случае активации 2-го орудия выстрелы будут генерироваться по данному коду
{
guncount--;//Снижение числа боеприпасов
if (guncount == 0) //В случае если боеприпасы закончатся то активируем 1 орудие а остальные блокируем
{
gun1 = true;
gun2 = false;
gun3 = false;
gun4 = false;
gun5 = false;
gun6 = false;
}
Instantiate (laser3x, new Vector2(transform.position.x,transform.position.y+1.1f), transform.rotation);//Генерация выстрелов из префаба laser3x
trigtime = true;
}
//Ниже для остальных видов "вооружения" все происходит аналогично
if (gun3 == true && guncount > 0)
{
guncount--;
if (guncount == 0)
{
gun1 = true;
gun2 = false;
gun3 = false;
gun4 = false;
gun5 = false;
gun6 = false;
}
Instantiate (laserhor, new Vector2(transform.position.x,transform.position.y+1.1f), transform.rotation);
trigtime = true;
}
if (gun4 == true && guncount > 0)
{
guncount--;
if (guncount == 0)
{
gun1 = true;
gun2 = false;
gun3 = false;
gun4 = false;
gun5 = false;
gun6 = false;
}
Instantiate (laser3xhor, new Vector2(transform.position.x,transform.position.y+2f), transform.rotation);
trigtime = true;
}
if (gun5 == true && guncount > 0)
{
guncount--;
if (guncount == 0)
{
gun1 = true;
gun2 = false;
gun3 = false;
gun4 = false;
gun5 = false;
gun6 = false;
}
Instantiate (sphere, new Vector2(transform.position.x,transform.position.y+1.1f), transform.rotation);
trigtime = true;
}
if (gun6 == true && guncount > 0)
{
guncount--;
if (guncount == 0)
{
gun1 = true;
gun2 = false;
gun3 = false;
gun4 = false;
gun5 = false;
gun6 = false;
}
Instantiate (sphere3x, new Vector2(transform.position.x,transform.position.y+2f), transform.rotation);
trigtime = true;
}
}
if (trigtime == true)
{
timer = timer - Time.deltaTime;//Отсчет времени для перезарядки
}
}
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "systemcontrol")
{
return;//Возврат необходим чтобы избежать конфликта с объектом controlcountobject
}
if (col.tag == "gunactivator2")//проверка на пересечение с "капсулой" для gunactivator2
{
gun2 = true;//активация второго орудия
guncount = 85;//установка количества боеприпасов
gun1 = false;//отключение остальных "орудий"
gun3 = false;
gun4 = false;
gun5 = false;
gun6 = false;
Destroy (col.gameObject);//уничтожение "капсулы" с которой произошло пересечение
return;
}
//Ниже все аналогично
if (col.tag == "gunactivator3")
{
gun3 = true; guncount = 50;
gun1 = false;
gun2 = false;
gun4 = false;
gun5 = false;
gun6 = false;
Destroy (col.gameObject);
return;
}
if (col.tag == "gunactivator4")
{
gun4 = true; guncount = 15;
gun1 = false;
gun3 = false;
gun2 = false;
gun5 = false;
gun6 = false;
Destroy (col.gameObject);
return;
}
if (col.tag == "gunactivator5")
{
gun5 = true; guncount = 100;
gun1 = false;
gun3 = false;
gun4 = false;
gun2 = false;
gun6 = false;
Destroy (col.gameObject);
return;
}
if (col.tag == "gunactivator6")
{
gun6 = true; guncount = 50;
gun1 = false;
gun3 = false;
gun4 = false;
gun5 = false;
gun2 = false;
Destroy (col.gameObject);
return;
}
//Если столкновение с "капсулами" не произошло то это значит что игрок столкнулся с лазером противника, метеоритом или с вражеским звездолетом. Значит нужно удалить игрока со сцены
Handheld.Vibrate ();//активация вибрации(для смартфонов)
if (GameObject.Find ("Main Camera").GetComponent<blockgenerator> ().score>GameObject.Find ("Main Camera").GetComponent<blockgenerator> ().data)//Если число сбитых метеоритов в текущей игровой сессии выше чем в файле с игровым рекордом, то делаем запись нового рекорда в файл
{
StreamWriter scoredata=new StreamWriter(Application.persistentDataPath + "/score1.gd");
scoredata.WriteLine(GameObject.Find ("Main Camera").GetComponent<blockgenerator> ().score);
scoredata.Close();
}
Instantiate (explosionplayer, transform.position, transform.rotation);//Генерация взрыва звездолета игрока
Destroy (col.gameObject);//Удаление объекта с которым произошло пересечение
Destroy(this.gameObject);//Удаление игрока с игровой сцены
}
}
Активаторы оружия.
Результат работы скрипта.
Когда игрок будет удален а в файл будет записан новый рекорд уничтоженных метеоритов, то тогда нужно переходить на главное меню.
Создаем скрипт со следующим содержимым и подключаем его на камеру:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class Exit : MonoBehaviour {
public GameObject target;//Добавляем сюда звездолет игрока
float timer=3f;
void Start ()
{
}
void Update ()
{
if (Input.GetKey (KeyCode.Escape))//Если будет нажата кнопка назад во время игры, то:
{
if (GameObject.Find ("Main Camera").GetComponent<blockgenerator> ().score>GameObject.Find ("Main Camera").GetComponent<blockgenerator> ().data)
{
StreamWriter scoredata=new StreamWriter(Application.persistentDataPath + "/score1.gd");
scoredata.WriteLine(GameObject.Find ("Main Camera").GetComponent<blockgenerator> ().score);
//Запись в файл рекорда переменную score из "blockgenerator", если она больше нее
scoredata.Close();
}
SceneManager.LoadScene (0);//Загрузка главного меню
}
if (!GameObject.Find ("playercar"))//Если игрок был удален то по истечению времени(в секундах) указанного в timer будет открыто главное меню
{
timer = timer - Time.deltaTime;
if (timer < 0)
{
SceneManager.LoadScene (0);
}
}
}
}
Данный скрипт наблюдает за состоянием игрока.
Взрывы
Эффект взрыва можно создать с помощью Particle System. Но тогда он будет зацикленным и вечно повторяться. Чтобы этого не было, на префаб взрыва нужно добавить следующий скрипт.
using UnityEngine;
using System.Collections;
public class DestroyAsteroid : MonoBehaviour {
void Start ()
{
}
void Update ()
{
Destroy (this.gameObject,0.4f);//Удаление игрового объекта после его создания. Время жизни 400 миллисекунд. Значение можно изменить.
}
}
Выстрелы
Выстрелы генерируются из места нахождения игрока во время его движения.
В скрипте для выстрела нужно задать только скорость и направление.
using UnityEngine;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class laser : MonoBehaviour {
float speedlaser=0.5f;
void Start ()
{
}
void Update ()
{
transform.Translate (new Vector3 (0, speedlaser, 0f));
}
}
Генерация противников
Генерация противников устроена аналогично генерации метеоритов.
Содержимое скрипта Enemygenerator:
using UnityEngine;
using System.Collections;
public class enemygenerator : MonoBehaviour {
public GameObject enemy;
bool trigtime=false;
float speedreset=2f;
float timer,x;
void Start ()
{
timer = speedreset;
}
void Update ()
{
if (timer < 0)
{
timer = speedreset;
trigtime = false;
}
if (timer == speedreset)
{
x = Random.Range (-2.5f, 2.5f);//Задаем местоположение
Instantiate(enemy, new Vector2(x,5.5f),transform.rotation);//Генерация противника из префаба
trigtime = true;
}
if (trigtime == true)
{
timer = timer - Time.deltaTime;
}
}
}
Логика противника
Поведение противника организовано таким образом, что он просто летит по прямой, ведет огонь и в случае своего поражения может оставить капсулу для активации игроком нового оружия
Выглядит это вот так:
using UnityEngine;
using System.Collections;
public class enemylogic : MonoBehaviour {
public GameObject explosionenemy;//Префаб вызрыва
public GameObject laserenemy;//Префаб выстрела
//Префабы капсул для активации других видов оружия
public GameObject gunactivator2;
public GameObject gunactivator3;
public GameObject gunactivator4;
public GameObject gunactivator5;
public GameObject gunactivator6;
//
bool trigtime=false;
float speedreset=1.5f;//Время перезарядки
float timer;
float speedenemy = -0.02f;//Скорость и направление
float x;
void Start ()
{
timer = speedreset;
}
void Update ()
{
if (timer < 0)
{
timer = speedreset;
trigtime = false;
}
if (timer == speedreset)
{
Instantiate (laserenemy, new Vector2(transform.position.x,transform.position.y-0.4f), transform.rotation);
trigtime = true;
}
if (trigtime == true)
{
timer = timer - Time.deltaTime;
}
transform.Translate (new Vector3 (0, speedenemy, 0f));
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "systemcontrol")
{
return;
}
if (col.tag == "Player")
{
Instantiate (explosionenemy, transform.position, transform.rotation);
Destroy(this.gameObject);
}
if (col.tag == "laser")
{
x = Random.Range (0f, 100f);//Генерируется любое число от 0 до 100
if (x > 1f && x < 5f) //если число в диапазоне от 1 до 5 то создается капсула gunactivator2
{
Instantiate (gunactivator2, transform.position, transform.rotation);
}
//Внизу все аналогично
if (x > 20f && x < 25f)
{
Instantiate (gunactivator3, transform.position, transform.rotation);
}
if (x > 40f && x < 45f)
{
Instantiate (gunactivator4, transform.position, transform.rotation);
}
if (x > 60f && x < 65f)
{
Instantiate (gunactivator5, transform.position, transform.rotation);
}
if (x > 80f && x < 85f)
{
Instantiate (gunactivator6, transform.position, transform.rotation);
}
Instantiate (explosionenemy, transform.position, transform.rotation);
Destroy(this.gameObject);
}
}
}
Каждый префаб «капсулы» с оружием просто движется по направлению к игроку и в случае пересечения уничтожается.
using UnityEngine;
using System.Collections;
public class gunactivatorlogic : MonoBehaviour {
float speed = -0.025f;
void Start ()
{
}
void Update ()
{
transform.Translate (new Vector3 (0, speed, 0f));
}
}
Итог
Публикация
Выход игры на google play не увенчался особым успехом. За все время пребывания в маркете набралось чуть больше 500 загрузок.
play.google.com/store/apps/details?id=com.WRXM4STER.SpaceStriker
Комментарии (17)
polar_bear_m
19.03.2017 04:35+2Автор проделал большую работу, молодец! Я только посоветую ему и всем кто начинает работать с unity прочитать что такое Пул Объектов (object pool) и использовать его как альтернативу клонирования префабов. Процесс клонирования и уничтожения префабов создает пиковые нагрузки для процессора(особенно на моб. устройствах). Пул Объектов позволяет при загрузке уровня сгенерировать большое количество объектов за пределами сцены, затем при необходимости активировать их, использовать неким образом, и деактевировать(это могут быть пули, враги, частицы). Это спасает от падения фпс в момент клонирования нескольких объемных префабов. Очень жаль что в большинстве туториалов для начинающих повсеместно используют только клонирование.
maaGames
19.03.2017 07:08+2Об инструментах не спорят… но на libGdx эта же игра была бы в 10 раз меньше.
Lailore
19.03.2017 14:26+5Не учите никого программировать, пожалуйстаpublic GameObject gunactivator2; public GameObject gunactivator3; public GameObject gunactivator4; public GameObject gunactivator5; public GameObject gunactivator6;
if (col.tag == "gunactivator2")//проверка на пересечение с "капсулой" для gunactivator2 { gun2 = true;//активация второго орудия guncount = 85;//установка количества боеприпасов gun1 = false;//отключение остальных "орудий" gun3 = false; gun4 = false; gun5 = false; gun6 = false; Destroy (col.gameObject);//уничтожение "капсулы" с которой произошло пересечение return; } //Ниже все аналогично if (col.tag == "gunactivator3") { gun3 = true; guncount = 50; gun1 = false; gun2 = false; gun4 = false; gun5 = false; gun6 = false; Destroy (col.gameObject); return; } if (col.tag == "gunactivator4") { gun4 = true; guncount = 15; gun1 = false; gun3 = false; gun2 = false; gun5 = false; gun6 = false; Destroy (col.gameObject); return; } if (col.tag == "gunactivator5") { gun5 = true; guncount = 100; gun1 = false; gun3 = false; gun4 = false; gun2 = false; gun6 = false; Destroy (col.gameObject); return; } if (col.tag == "gunactivator6") { gun6 = true; guncount = 50; gun1 = false; gun3 = false; gun4 = false; gun5 = false; gun2 = false; Destroy (col.gameObject); return; }
НЕ ИСПОЛЬЗОВАТЬ ПУУЛ?!
Instantiate(enemy, new Vector2(x,5.5f),transform.rotation);//Генерация противника из
и т.п.FadeToBlack
19.03.2017 19:24Я бы посоветовал автору убрать статью в черновики и идти учиться делать игры дальше. Через полгода почитать статью опять, и понять, что это было единственно верное решение. Не позорьтесь, пожалуйста.
inborn_killer
20.03.2017 16:20Автор почти год назад уже публиковал другую статью подобного же содержания. И вот опять.
FadeToBlack
21.03.2017 17:14+1Печально, что модерация пропускает такой шлак. Я пытался писать статью на хабр лет 8 назад, мне даже не сказали, почему она не подходит, такие уж были времена. Хочу, чтобы НЛО выпилил эту статью и принес мне свои извинения за доставленные мне оскорбления.
Bvp254
21.03.2017 22:54-1Спасибо! После прочтения возникло желание узнать про геймдев и Unity побольше.
a3dline
21.03.2017 22:54-3Что вы тут навязываете программирование ради программирования. Нормальный код. Свою задачу выполняет. Что за предвзятость. Я когда начинал Unity именно по таким и учился.
Delphin92
21.03.2017 22:54+1Тут многие написали что автору надо учиться, но не написали что именно не так, а это может быть не столь очевидным. Поэтому я позволю себе привести небольшой список.
- Насколько я знаю, GUI — устаревшее апи, сейчас есть UI с удобным расположением кнопок на сцене. Незачем захламлять код разметкой.
- Уже много раз тут написали про пул объектов. В двух словах его суть в том, что для всех «расходников» — метеоритов, лазеров, врагов и т.д. при загрузке сцены создаются пулы с некоторым количеством этих объектов. Далее вместо инстанциирования префаба объект достается из пула и активируется, а вместо удаления он деактивируется и кладется обратно в пул. В итоге пропадает необходимость постоянно создавать-удалять объекты.
- Вешать на game object что-то, никак не связанное с ним по смыслу — дурной тон. Вместо того чтобы вешать все подряд скрипты на камеру, лучше было бы для каждого создать отдельный пустой объект, названный так же, как класс.
- Не стоит использовать GameObject.Find без крайней необходимости. Гораздо лучше сделать из нужного скрипта синглтон. Самый простой способ это сделать — создать в классе public static поле и инициализировать его через this на Awake.
- Зачем вешать Rigidbody на звездолет игрока, если все равно двигается он через transform? Помимо бессмысленности это еще и бьет по производительности.
Эффект взрыва можно создать с помощью Particle System. Но тогда он будет зацикленным и вечно повторяться.
Да, потому что там есть такая галочка:
Впрочем, удалять (а точнее, возвращать в пул) взрыв все равно надо.
Ну и код, конечно, отдельная боль… Но это в двух словах не объяснишь. Разве что «учите ООП».FadeToBlack
22.03.2017 05:225. А там без ригид бади не работают триггеры, насколько мне известно. Юнити, конечно, тоже «хорош».
Delphin92
22.03.2017 11:07+2Усп, был не прав. Действительно, ерунду написал. Надо делать так:
1) Поставить rigidbody на все двигающиеся объекты.
2) Установить им isKinematic = true, чтобы отключить физику.
Невыполнение обеих пунктов бьет по производительности.
Пруф из официальной документации (правда, он про обычный коллайдер, но с 2D, насколько знаю, так же):
If the object with the Collider needs to be moved during gameplay then you should also attach a Rigidbody component to the object. The Rigidbody can be set to be kinematic if you don't want the object to have physical interaction with other objects.
По логике юнити коллайдеры без rigidbody не должны вообще двигаться, а все что двигается и хочет физику, должно иметь rigidbody. Поэтому и триггеры у двух пустых коллайдеров не работают. Тут не юнити «хорош», а просто вопрос чуть более сложный.
xKorteSx
21.03.2017 22:54Интересная статья, правда мне не совсем понятно чем получившийся продукт отличается от официального туториала от Unity? Ну кроме того, что немного другие модельки?
https://unity3d.com/ru/learn/tutorials/projects/space-shooter-tutorial
comerc
Такое на продажу клепают, насколько я понимаю.