Продолжаю изучать Unity. В основу текущей игры положены уроки по созданию Space shooter с сайта Unity3d.
- Изменил плоское представление на объемное. Сделал эмуляцию полёта над поверхностью.
- Добавил сохранение результатов (для Web версии не работает)
- Добавил врагов
- Добавил бонусы
Все ассеты, используемые в моей игре бесплатны и взяты с Asset store на сайте проекта.
В своих играх я стараюсь использовать управление удобное как на настольных ПК, так и на мобильных устройствах.
Все изменения я делал в рамках изучения как самой Unity, так и языка программирования C# (си шарп). Ниже выкладываю скрипты и материалы, которые были использованы для этого этапа обучения.
Скачать материалы asteroid.unitypackage для Unity
Скачать игру Asteroid для Android
Скрипты, используемые в игре:
GameController.cs управляет логикой игры
using UnityEngine; using UnityEngine.UI; using System.Collections; using System.IO; using System.Linq; public class GameController : MonoBehaviour { public GameObject[] asteroids; //список единичных врагов public GameObject enemy1; //первая группа врагов public GameObject enemy2;//вторая группа врагов public GameObject sputnik;//бонусы public PlayerController playerController; public Vector3 spawnValue;//место старта врагов public int asteroidCount;//количество врагов в волне public float nextWait;//время между вылетом врагов public float wavesWait;//время между волнами врагов public float startWait;//начало вылета врагов public Slider slider;//уровень здоровья int score; //заработанные очки string yourName;//Имя пользователя public Text ScoreText;//текст вывода заработанных очков public int level;//счётчик уровней bool gameOver = false;//признак конца игры int gameOverCount = 5;//количество жизней float ButtonW, ButtonH;//размеры кнопок string[] names = new string[6];//список имен int[] scores = new int[6];//список результатов string mePath;//путь сохранения результатов public bool IsBattle;//признак финальной битвы private int CountOfAsteroids, CountOfSputniks, CountOfEnemys;//общее количество игровых юнитов для статистики void Start() { mePath = Path.Combine(Application.persistentDataPath, "rezult.rz"); if (!Directory.Exists(Path.GetDirectoryName(mePath))) Directory.CreateDirectory(Path.GetDirectoryName(mePath)); ButtonW = Screen.width / 3; ButtonH = Screen.height / 8; slider.value = 100; gameOverCount = 5; level = 0; gameOver = false; score = 0; yourName = "Your name"; CountOfAsteroids = 0; CountOfSputniks = 0; CountOfEnemys = 0; UpdateScore(); IsBattle = false; StartCoroutine(SpawnWaves()); } IEnumerator SpawnWaves() { bool flagHazard = true;//признак волны врагов или единичных групп yield return new WaitForSeconds(startWait); while (true) { if (!IsBattle) { for (int j = 0; j < 3; j++)//три раза { if (flagHazard) //asteroid { for (int a = 0; a < 3; a++)//три волны астероидов { for (int i = 0; i < asteroidCount; i++) { Vector3 spawnposition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), Random.Range(-spawnValue.y, spawnValue.y), spawnValue.z); GameObject asteroid = asteroids[Random.Range(0, asteroids.Length)]; Instantiate(asteroid, spawnposition, Quaternion.identity); if (!gameOver) { CountOfAsteroids++; }; yield return new WaitForSeconds(nextWait); } Vector3 sputnikposition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), Random.Range(-spawnValue.y, spawnValue.y), spawnValue.z); Instantiate(sputnik, sputnikposition, Quaternion.identity); if (!gameOver) { CountOfSputniks++; }; yield return new WaitForSeconds(wavesWait); } flagHazard = !flagHazard; } else//enemy { for (int i = 0; i < 4; i++)//четыре группы врагов { GameObject goenemy = i % 2 == 0 ? enemy1 : enemy2; Vector3 spawnposition = new Vector3(0.0f, 0.0f, spawnValue.z); Instantiate(goenemy, spawnposition, Quaternion.identity); if (!gameOver) { CountOfEnemys += 3; }; yield return new WaitForSeconds(nextWait); } Vector3 sputnikposition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), Random.Range(-spawnValue.y, spawnValue.y), spawnValue.z); Instantiate(sputnik, sputnikposition, Quaternion.identity); if (!gameOver) { CountOfSputniks++; }; yield return new WaitForSeconds(wavesWait); flagHazard = !flagHazard; } } IsBattle = !IsBattle; } else { for (int a = 0; a < 3; a++)//три волны enemy { for (int i = 0; i < asteroidCount; i++) { Vector3 spawnposition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), Random.Range(-spawnValue.y, spawnValue.y), spawnValue.z); GameObject asteroid = asteroids[3]; Instantiate(asteroid, spawnposition, Quaternion.identity); if (!gameOver) { CountOfEnemys++; }; yield return new WaitForSeconds(nextWait); } } IsBattle = !IsBattle; } } } IEnumerator SliderChange(int value) { for (int i = 0; i < Mathf.Abs(value); i++) { if (value > 0) { slider.value++; } else { slider.value--; } yield return new WaitForSeconds(0.05f); } } public void GameOver(GameObject go) { if (playerController.skill > 0) { playerController.skill--; } else { gameOverCount--; StartCoroutine(SliderChange(-20)); if (gameOverCount <= 0) { Destroy(go); gameOver = true; } } } public void AddScore(int scoreValue) { score += scoreValue; UpdateScore(); } public void Bonus(GameObject go) { if (slider.value < 100) { gameOverCount = 5; playerController.skill = 0; StartCoroutine(SliderChange(20)); } else//прокачаем игрока { if (playerController.skill > 4) { playerController.skill++; } } } void UpdateScore() { ScoreText.text = "Score:" + score.ToString(); } void Sorting() { string A; int B; for (int j = 5; j > 0; j--) { for (int i = 0; i < j; i++) { if (scores[i] < scores[i + 1]) { B = scores[i]; scores[i] = scores[i + 1]; scores[i + 1] = B; A = names[i]; names[i] = names[i + 1]; names[i + 1] = A; } } } } void Save() { names[5] = yourName; scores[5] = score; Sorting(); string[] linesOut = new string[12]; for (int i = 0; i <= 5; i++) { linesOut[i * 2] = names[i]; linesOut[i * 2 + 1] = scores[i].ToString(); } //закоментировать для Web варианта //File.WriteAllLines(mePath, linesOut); } void Load() { if (File.Exists(mePath)) { string[] lines = File.ReadAllLines(mePath); bool Itsname = true; int j = 0; for (int i = 0; i < lines.Length; i++) { if (Itsname) { names[j] = lines[i]; Itsname = !Itsname; } else { int.TryParse(lines[i], out scores[j]); Itsname = !Itsname; j++; } } } } void OnGUI() { if (gameOver) { Load(); if (GUI.Button(new Rect(2, 2, ButtonW, ButtonH), "Continue")) { gameOver = false; Application.LoadLevel(Application.loadedLevelName); } if (GUI.Button(new Rect(Screen.width - ButtonW - 2, 2, ButtonW, ButtonH), "Quit")) { Application.Quit(); } string WinText = "Asteroids:" + CountOfAsteroids.ToString() + ":Sputniks:" + CountOfSputniks.ToString() + ":Enemys:" + CountOfEnemys.ToString(); GUI.Label(new Rect(Screen.width / 2 - WinText.Length * 10 / 2, 70, WinText.Length * 10, 20), WinText); WinText = "HIGH SCORE TAB"; GUI.Label(new Rect(Screen.width / 2 - WinText.Length * 10 / 2, 90, WinText.Length * 10, 20), WinText); int j = 0; if (names != null) { foreach (string name in names) { string stroka = name + "..........." + scores[j]; GUI.Label(new Rect(Screen.width / 2 - stroka.Length * 8 / 2, 110 + j * 25, stroka.Length * 8, 20), stroka); j++; } } if (score > scores[5]) { string stroka1 = "Your score: " + score.ToString(); GUI.Label(new Rect(Screen.width / 2 - stroka1.Length * 10 / 2, 100 + 6 * 25, stroka1.Length * 10, 20), stroka1); yourName = GUI.TextField(new Rect(Screen.width / 2 - 95, 100 + 7 * 25, 190, 30), yourName, 10); if (GUI.Button(new Rect(Screen.width / 2 - 50, 100 + 8 * 25, 100, 60), "Save")) { Save(); } } } } }
WeaponController.cs управление стрельбой
using UnityEngine; using System.Collections; [RequireComponent(typeof(AudioSource))] public class WeaponController : MonoBehaviour { public GameObject shot; public Transform shotSpawn; public float fireRate; public float delay; private AudioSource audioSource; void Start() { audioSource = GetComponent(); InvokeRepeating("Fire", delay, fireRate); } void Fire() { Instantiate(shot, shotSpawn.position, shotSpawn.rotation); audioSource.Play(); } }
DestroyByContact.cs уничтожает объект при столкновении
using UnityEngine; using System.Collections; public class DestroyByContact : MonoBehaviour { public GameObject explousion; public GameObject PlayerExplousion; public int ScoreValue; private GameController gameController; public int Damage; void Start() { GameObject go = GameObject.FindGameObjectWithTag("GameController"); if (go != null) { gameController = go.GetComponent(); } else { Debug.Log("Cant find script GameController"); } Damage += gameController.level; } void OnTriggerEnter(Collider other) { if (other.tag == "Boundary" || other.tag == "Enemy") { return; } else { if (other.tag == "Player") { if (explousion!=null) { Instantiate(explousion, transform.position, transform.rotation); } Instantiate(PlayerExplousion, other.transform.position, other.transform.rotation); gameController.GameOver(other.gameObject); Destroy(this.gameObject); } else { if (explousion != null) { Damage -= 1; Instantiate(explousion, transform.position, transform.rotation); if (Damage <= 0) { Destroy(other.gameObject); Destroy(this.gameObject); gameController.AddScore(ScoreValue); } } } } } }
DestroyByTime.cs уничтожение объекта по истечении времени
using UnityEngine; using System.Collections; public class DestroyByTime : MonoBehaviour { public float idleTime; void Start () { Destroy(gameObject,idleTime); } }
Boundary.cs уничтожение объекта при вылете за пределы игрового поля
using UnityEngine; using System.Collections; public class Boundary : MonoBehaviour { void OnTriggerExit(Collider other) { if (other.tag == "Player" || other.tag == "Boss") { return; } Destroy(other.gameObject); } }
AsteroidController.cs управление вращением астероидов
using UnityEngine; using System.Collections; public class AsteroidController : MonoBehaviour { public float tumble; void Start() { Rigidbody rb = GetComponent(); rb.angularVelocity = Random.insideUnitSphere * tumble; } }
Mover.cs управление полетом объекта
using UnityEngine; using System.Collections; public class Mover : MonoBehaviour { public float speed; void Start () { Rigidbody rb = GetComponent(); rb.velocity = transform.forward * speed; } }
ScrollScreen.cs прокрутка поверхности планеты
using UnityEngine; using System.Collections; public class ScrollScreen : MonoBehaviour { public float speed; public bool Vert; MeshRenderer mr; Material mat; void Start() { mr = GetComponent(); mat = mr.material; } void Update() { Vector2 offset = mat.mainTextureOffset; if (Vert) { offset.y += Time.deltaTime / speed; } else { offset.x += Time.deltaTime / speed; } mat.mainTextureOffset = offset; } }
Rotator.cs вращение вокруг оси
using UnityEngine; using System.Collections; public class Rotator : MonoBehaviour { Transform me; float speedRotation; // Use this for initialization void Start () { me = GetComponent(); speedRotation = Random.Range(-30f, 30f); } void Update () { me.RotateAround(me.position, Vector3.forward, speedRotation * Time.deltaTime); } }