Игра SkyBallz 3D

На этот раз попробуем сделать, что-нибудь наподобие казуальной игры. В этой игре не будет конечной цели как таковой, наверно, типа медитативной темы что-то должно получиться. Хотя, при желании всё это можно изменить, разумеется. Значит, суть игрушки, есть сотня шариков разного цвета, задача игрока, собирать вместе шарики одинакового цвета, по горизонтали или вертикали. Если набирается три и более одинаковых шарика, то они будут удалены, а игроку засчитываются баллы за каждый из них. При этом, на сбор дается определенное время, по истечению которого, будет обновлена сетка, старые шарики заменятся новыми и игрок получает минус один балл, за каждый оставшийся шарик. Смысл в том, чтобы конечное число баллов было в плюсе.


Создаем в Unity новый 3D проект. Первым делом, настроим сцену. Делов на самом деле-то не много, настроим позицию камеры XY по нулям, а Z минус десять, поворот камеры по нулям. Еще добавим на сцену пустой объект с любим именем, на нем будет висеть скрипт GameControl, позиция объекта по нулям. И еще понадобится вывод текстовой информации, текущий счет и время, соответственно добавляем текстовые объекты UI, назовем их например, Stats и Time. Что до фона, то его сделайте на свое усмотрение, что больше нравиться. Напоследок, нужно добавить тег Ball, он будет использоваться в скрипте.

Так-с, продолжим подготовку. В качестве шариков, возьмем стандартный объект Unity, GameObject -> 3D -> Sphere. На наш шарик надо повесить простенький скрипт Ball, а всё остальное оставляем без изменений;

using UnityEngine;
using System.Collections;

public class Ball : MonoBehaviour {

	public int colorID;
	public bool check;
} 

Тут храниться информация о цвете, плюс, дополнительная переменная, чтобы отмечать одинаковые по цвету шарики. Кстати говоря, текстуру для шарика можно выбрать с любим узором, но главное - цвет текстуры должен быть белым, потому что менять цвета будет скрипт. Чтобы добавить текстуру и материал объекту, выберете его, а затем перетащите нужную текстуру на объект в окошко Inspector-а. Далее, перетаскиваем сам объект в папку Prefab и удаляем лишний со сцены. Готово, префаб шарика есть.

Сделаем курсор, точнее что-то вроде указателя. То есть это обычный 3D объект, который будет находится в той-же позиции где и шарик, на который в данный момент направлен курсор. Чтобы игроку было нагляднее, какой шарик выбран. Выглядеть этот указатель может как угодно, например, такой-же шарик чуть большего размера с полупрозрачной текстурой, или можно нарисовать что-нибудь в Blender. Этот объект должен присутствовать на сцене. В нашем варианте это просто распиленный куб.

Игра SkyBallz 3D

Важно! На объекте не должно быть коллайдера!

Окей, далее. В папке Scripts нашего проекта, создаем еще один небольшой скрипт с именем AutoDestroy:

using UnityEngine;
using System.Collections;

public class AutoDestroy : MonoBehaviour {
	
	void Start () 
	{
		Destroy (gameObject, 5);
	}
}

Данный скрипт будет добавляться автоматически к шарикам, которые выбрасываются из сетки. То есть, они уничтожаться через пять секунд.

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

Итак, а теперь посмотрим на главный скрипт GameControl:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class GameControl : MonoBehaviour {

	public GameObject ball;
	public float startPosX = -6f;
	public float startPosY = 6f;
	public int countX = 10;
	public int countY = 10;
	public float outX = 1.1f;
	public float outY = 1.1f;
	public Color[] colors;
	public GameObject cursor;
	public GameObject number;
	public Text statsText;
	public Text timeText;
	public int time = 30;
	private int timeLeft;
	private GameObject[,] grid;
	private Vector3[,] ballPosition;
	private int id_A;
	private int id_B;
	private GameObject obj_A;
	private GameObject obj_B;
	private GameObject cursor_A;
	private GameObject cursor_B;
	private int hash;
	private int totalScore;
	private int addScore;
	private int removeScore;

	void Start () 
	{
		// Создание игровых объектов и заполнение массивов
		float posXreset = startPosX;
		ballPosition = new Vector3[countX,countY];
		grid = new GameObject[countX,countY];
		for(int y = 0; y < countY; y++)
		{
			startPosY -= outY;
			for(int x = 0; x < countX; x++)
			{
				startPosX += outX;
				int id = Random.Range(0, colors.Length);
				ballPosition[x,y] = new Vector3(startPosX, startPosY, 0);
				grid[x,y] = Instantiate(ball, ballPosition[x,y], Random.rotation) as GameObject;
				grid[x,y].GetComponent<MeshRenderer>().material.color = colors[id];
				grid[x,y].GetComponent<Ball>().colorID = id;
				grid[x,y].transform.parent = transform;
			}
			startPosX = posXreset;
		}

		id_A = -1;
		id_B = -1;
		cursor.transform.position = new Vector3(15, 0, 0); // Убрать указатель за границы экрана
		timeLeft = time;
		StartCoroutine (TimeWait());
		AddTags();
	}

	void OnGUI()
	{
		statsText.text = "Собрано:\n" + addScore + "\n\nОсталось:\n" + removeScore + "\n\nОбщий счет:\n" + totalScore;
		timeText.text = "Осалось времени:\n" + timeLeft;
	}

	// Ожидание между сменой цветов шариков и обновлением сетки
	IEnumerator Wait()
	{
		obj_A.GetComponent<MeshRenderer>().material.color = colors[id_B];
		obj_A.GetComponent<Ball>().colorID = id_B;
		obj_B.GetComponent<MeshRenderer>().material.color = colors[id_A];
		obj_B.GetComponent<Ball>().colorID = id_A;
		yield return new WaitForSeconds (0.5f);
		Destroy(cursor_A);
		Destroy(cursor_B);
		UpdateGrid();
		cursor.transform.position = new Vector3(15, 0, 0);
		id_A = -1;
		id_B = -1;
	}

	// Добавление тегов
	void AddTags()
	{
		for(int y = 0; y < countY; y++)
		{
			for(int x = 0; x < countX; x++)
			{
				if(grid[x,y])
				{
					grid[x,y].tag = "Ball";
				}
			}
		}
	}

	// Убрать теги, а так-же указатели
	void RemoveTags()
	{
		for(int y = 0; y < countY; y++)
		{
			for(int x = 0; x < countX; x++)
			{
				if(grid[x,y])
				{
					grid[x,y].tag = "Untagged";
				}
			}
		}
		Destroy(cursor_A);
		Destroy(cursor_B);
		id_A = -1;
		id_B = -1;
		cursor.transform.position = new Vector3(15, 0, 0);
	}

	// Счетчик времени
	IEnumerator TimeWait()
	{
		yield return new WaitForSeconds (1);
		if(timeLeft > 0)
		{
			timeLeft--;
			StartCoroutine (TimeWait());
		}
		else
		{
			RemoveTags();
			timeLeft = time;
			StartCoroutine (ReBuildArray());
		}
	}

	// Перестройка сетки
	IEnumerator ReBuildArray()
	{
		for(int y = 0; y < countY; y++)
		{
			for(int x = 0; x < countX; x++)
			{
				if(grid[x,y])
				{
					// Выкидываем старые шарики
					grid[x,y].AddComponent<AutoDestroy>();
					grid[x,y].AddComponent<Rigidbody>().AddForce(-Vector3.forward * 200);
					grid[x,y] = null;
					removeScore--;
				}
				yield return new WaitForSeconds (0.1f);
				// Заполняем сетку новыми
				int id = Random.Range(0, colors.Length);
				grid[x,y] = Instantiate(ball, ballPosition[x,y], Random.rotation) as GameObject;
				grid[x,y].GetComponent<MeshRenderer>().material.color = colors[id];
				grid[x,y].GetComponent<Ball>().colorID = id;
				grid[x,y].transform.parent = transform;
			}
		}
		AddTags();
		StartCoroutine (TimeWait());
	}

	// Проверка, есть-ли подряд 3 одинаковых шарика по горизонтали или вертикали
	// Если да - то отмечаем соответствующие
	void UpdateGrid()
	{
		for(int y = 0; y < countY; y++)
		{
			for(int x = 0; x < countX; x++)
			{
				if(grid[x,y] != null)
				{
					if(x > 0 && x < countX-1)
					{
						if(grid[x+1,y] != null && grid[x-1,y] != null)
						{
							if(grid[x,y].GetComponent<Ball>().colorID == grid[x+1,y].GetComponent<Ball>().colorID &&
							   grid[x,y].GetComponent<Ball>().colorID == grid[x-1,y].GetComponent<Ball>().colorID)
							{
								grid[x,y].GetComponent<Ball>().check = true;
								grid[x+1,y].GetComponent<Ball>().check = true;
								grid[x-1,y].GetComponent<Ball>().check = true;
							}
						}
					}
					if(y > 0 && y < countY-1)
					{
						if(grid[x,y+1] != null && grid[x,y-1] != null)
						{
							if(grid[x,y].GetComponent<Ball>().colorID == grid[x,y+1].GetComponent<Ball>().colorID &&
							   grid[x,y].GetComponent<Ball>().colorID == grid[x,y-1].GetComponent<Ball>().colorID)
							{
								grid[x,y].GetComponent<Ball>().check = true;
								grid[x,y+1].GetComponent<Ball>().check = true;
								grid[x,y-1].GetComponent<Ball>().check = true;
							}
						}
					}
				}
			}
		}

		StartCoroutine (WaitDestroyBall());
	}

	// Удаление отмеченных шариков
	IEnumerator WaitDestroyBall()
	{
		for(int y = 0; y < countY; y++)
		{
			for(int x = 0; x < countX; x++)
			{
				if(grid[x,y] != null)
				{
					if(grid[x,y].GetComponent<Ball>().check)
					{
						yield return new WaitForSeconds (0.15f);
						addScore += 3;
						Instantiate(number, grid[x,y].transform.position, Quaternion.identity);
						Destroy(grid[x,y].transform.gameObject);
					}
				}
			}
		}

		// Вызврат тегов, если выделенные курсором не удалены
		if(obj_A) obj_A.tag = "Ball";
		if(obj_B) obj_B.tag = "Ball";
	}

	void Update () 
	{
		// Подсчет очков
		totalScore = addScore + removeScore;

		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		if (Physics.Raycast(ray, out hit))
		{
			if(hit.transform.CompareTag("Ball"))
			{
				// Смена позиции указателя
				if(hash != hit.GetHashCode()) 
				{
					hash = hit.GetHashCode();
					cursor.transform.position = new Vector3(hit.transform.position.x, hit.transform.position.y, 0);
				}

				// Выделение шариков
				if (Input.GetMouseButtonDown(0)) 
				{
					if(id_A == -1)
					{
						cursor.transform.position = new Vector3(15, 0, 0);
						obj_A = hit.transform.gameObject;
						id_A = hit.transform.GetComponent<Ball>().colorID;
						obj_A.tag = "Untagged";
						GameObject clone = Instantiate(cursor, new Vector3(hit.transform.position.x, hit.transform.position.y, 0), Quaternion.identity) as GameObject;
						cursor_A = clone;
					}
					else
					{
						cursor.transform.position = new Vector3(15, 0, 0);
						obj_B = hit.transform.gameObject;
						id_B = hit.transform.GetComponent<Ball>().colorID;
						obj_B.tag = "Untagged";
						GameObject clone = Instantiate(cursor, new Vector3(hit.transform.position.x, hit.transform.position.y, 0), Quaternion.identity) as GameObject;
						cursor_B = clone;
						StartCoroutine (Wait());
					}
				}
			}
		}
	}
}


Ничего особенного здесь нет в принципе, разобраться где-что не составит особого труда. Уточним некоторые переменные: ball - как не удивительно префаб нашего шарика; colors - массив цветов, в которые будут окрашиваться шары; cursor - указатель; number - префаб, который появляется на месте уничтоженного шарика, в нашем варианте это спрайт "+3".

Дополнительно, Вы можете скачать уже готовый проект:
Внимание! У Вас нет прав для просмотра скрытого текста.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика