Buffer icon Проекты
39 заметок с тегом

с#

Позднее Ctrl + ↑

Unity EventManager 2.0

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

using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
using System;

public class EventManager : MonoBehaviour {

    [Serializable]
    public class GameEvent : UnityEvent<object[]> { };

    private static EventManager _instance;
    private Dictionary<string, GameEvent> _eventDictionary;

    void Awake () {
        if (_instance != null)
            return;
        _instance = GetComponent<EventManager> ();
        if (_eventDictionary == null)
            _eventDictionary = new Dictionary<string, GameEvent> ();
    }

    /// <summary>Добавляем "слушателя" для события</summary>
    /// <param name="eventName">Название события</param>
    /// <param name="listener">Метод-обработчик события</param>
    public static void Subscribe (string eventName, UnityAction<object[]> listener) {
        GameEvent thisEvent;
        if (_instance._eventDictionary.TryGetValue (eventName, out thisEvent))
            thisEvent.AddListener (listener);
        else {
            thisEvent = new GameEvent ();
            thisEvent.AddListener (listener);
            _instance._eventDictionary.Add (eventName, thisEvent);
        }
    }

    /// <summary>Удаляем "слушателя" из списка</summary>
    /// <param name="eventName">Название события</param>
    /// <param name="listener">Метод-обработчик события</param>
    public static void Unsubscribe (string eventName, UnityAction<object[]> listener) {
        if (_instance == null)
            return;
        GameEvent thisEvent;
        if (_instance._eventDictionary.TryGetValue (eventName, out thisEvent))
            thisEvent.RemoveListener (listener);
    }

    /// <summary>Отправить событие</summary>
    /// <param name="eventName">Название события</param>
    /// <param name="parameters">Параметр массив</param>
    public static void SendEvent (string eventName, params object[] parameters) {
        GameEvent thisEvent;
        if (_instance._eventDictionary.TryGetValue (eventName, out thisEvent)) {
            thisEvent.Invoke (parameters);
        }
    }
}

Теперь количество параметров динамическое и любого типа. Для простого взаимодействия, вполне работоспособно. Пример использования:

void OnEnable () {
	EventManager.Subscribe ("event_name", MyFunction);
}

void OnDisable () {
	EventManager.Unsubscribe ("event_name", MyFunction);
}

void MyFunction (object[] parameters) {
	Debug.Log (parameters.Length); // количество параметров -> 3 в примере
	Debug.Log (parameters[1]);        // выведет -> 1 
}

...

EventManager.SendEvent ("event_name", "param_string", 1, 2); // вызов события
 1 комментарий    1536   2017   unity   разработка   с#

Автоподстановка полей в UI

Нашёл интересное решение, которое сохранит дорогое время разработки, взяв на себя рутинные операции по присвоению объектов со сцены полям класса. Как-то так.

Например: вы сделали новый объект интерфейса, создали для него класс, добавили соответствующие поля и потом ручками в инспекторе Unity присвоили текстовое поле к Text и кнопку к Button.

А если таких полей 10-15? И окошек столько же... Я думаю мысль понятна.

.lead Вот как раз для таких дел этот скрипт и есть. Только делает он всё сам. Работает это так:

using UnityEngine;
using UnityEngine.UI;

public class DialogWindow : AutoReferencer<DialogWindow> {
   public Text DialogLabel;
   public Button ConfirmButton;
}
Главное, чтобы названия полей и имена объектов совпадали. И типы соответственно.

Ну и сам скрипт. Конечно, вряд ли есть возможность использовать его везде — всё-таки архитектура приложения у всех отличается, однако, полезность какая-то в нём есть.

using System.Linq;
using UnityEngine;

public class AutoReferencer<T> : MonoBehaviour where T : AutoReferencer<T> {

   #if UNITY_EDITOR
   // Этот метод вызывается один раз, когда мы добавляем компонент на объект
   protected new virtual void Reset()
   {
       // Магия рефлексии
       // Для каждого поля в классе/компоненте, мы ищем только пустые или null
       foreach (var field in typeof(T).GetFields().Where(field => field.GetValue(this) == null))
       {
           // Теперь ищем объект с таким же именем
           Transform obj;
           if (transform.name == field.Name)
           {
               obj = transform;
           }
           else
           {
               obj = transform.Find(field.Name); // Или нужно сделать рекурсию, чтобы пойти глубже по иерархии
           }

           // Если находим объект с таким же именем как и поле - пытаемся присвоить его к этому полю
           if (obj!=null)
           {
               field.SetValue(this, obj.GetComponent(field.FieldType));
           }
       }
   }
   #endif
}

Подглядел на The Knights of Unity ;)

Unit тесты в Unity

Что такое юнит-тестирование, зачем оно нужно, стоит ли покрывать весь код тестами — можно узнать из этой хорошей статьи. Я лучше сразу скажу, как создать тест в Unity. Открываем пункт меню Window → Editor Tests Runner. Если у вас ещё нет тестов, то увидите окно, как на скриншоте, если нет этого пункта меню — значит у вас старая версия Unity :)

При нажатии на кнопку — создастся пример теста. Все!
На самом деле — нет.

using UnityEngine;
using UnityEditor;
using NUnit.Framework;

public class NewEditorTest {

	[Test]
	public void EditorTest() {
		//Arrange
		var gameObject = new GameObject();

		//Act
		//Try to rename the GameObject
		var newGameObjectName = "My game object";
		gameObject.name = newGameObjectName;

		//Assert
		//The object has a new name
		Assert.AreEqual(newGameObjectName, gameObject.name);
	}
}

Как видно, у метода есть атрибут [Test], который и указывает, что это будет тестом. Если создать несколько тестов и запустить их через тот же Editor Tests Runner, можно увидеть какие тесты прошли, а какие нет:

В Unity для тестирования используется опенсорсная библиотека NUnit, которая предназначена для работы с .Net языками, в том числе и с C#. На Github есть обширная документация, где расписаны все атрибуты которые можно использовать.

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

Также, у Unity есть ещё специальный ассет для тестирования — Unity Test Tools, но как я понял они его перестали поддерживать.

Кликабельные ссылки в TextMeshPro

Как сделать чтобы ссылки в тексте, стали «ссылками» и при клике на них открывалась страница? Просто! Достаточно открыть примеры из TextMeshPro (TMP) и добавить немного кода.

Пускай у нас есть компонент ТМР с текстом, в котором встречаются ссылки на страницы в сети, например: http://mopsicus.ru, www.unity3d.com, и другие. И нужно чтобы это выглядело примерно так, и было кликабельно:

Для этого, находим ссылки в тексте с помощью регулярного выражения, сокращаем их (по желанию) и приводим к формату для обработки ТМР.

// Check links in text
void CheckLinks () {
	Regex regx = new Regex ("((http://|https://|www\\.)([A-Z0-9.-:]{1,})\\.[0-9A-Z?;~&#=\\-_\\./]{2,})" , RegexOptions.IgnoreCase | RegexOptions.Singleline); 
	MatchCollection matches = regx.Matches (textMessage.text); 
	foreach (Match match in matches) 
		textMessage.text = textMessage.text.Replace (match.Value, ShortLink(match.Value));     	
}

// Cut long url
string ShortLink (string link) {
	string text = link;
	int left = 9; 		
	int right = 16; 		
	string cut = "..."; 	
	if (link.Length > (left + right + cut.Length)) 
		text = string.Format ("{0}{1}{2}", link.Substring (0, left), cut, link.Substring (link.Length - right, right));
	return string.Format("<#7f7fe5><u><link=\"{0}\">{1}</link></u></color>", link, text);
}

Как установить подчеркивание, цвет текста и другое смотрите в документации ТМР. Осталось повесить обработчик нажатия и определить на какую ссылку кликнули:

// Get link and open page
public void OnPointerClick (PointerEventData eventData) {
	int linkIndex = TMP_TextUtilities.FindIntersectingLink (textMessage, eventData.position, eventData.pressEventCamera);
	if (linkIndex == -1) 
		return;
	TMP_LinkInfo linkInfo = textMessage.textInfo.linkInfo[linkIndex];
	string selectedLink = linkInfo.GetLinkID();
	if (selectedLink != "") {
		Debug.LogFormat ("Open link {0}", selectedLink);
		Application.OpenURL (selectedLink);        
	}
}

Исходник на Github

 1 комментарий    3620   2017   unity   разработка   с#

Баг в userInfo NotificationItem [iOS]

Если вы работаете с пуш уведомлениями в iOS и делаете это стандартными средствами Unity, то при обработке уведомления у вас могут возникнуть сложности с цифровыми значениями. Видимо Unity сам преобразует их в Int64. В тоже время, с текстовыми полями все в порядке.
Чтобы получить «нормальное» цифровое значение из пуша и преобразовать в Int, можно воспользоваться таким способом:

...
int my_int_value = 0;
object data = push.userInfo["my_int_value"];
if (data is Int64)
	my_int_value = ConvertInt64ToInt32((Int64)data);
...
// Convert function
int ConvertInt64ToInt32 (Int64 val) {
	return (int)(val & 0xFFFFFFFF);
}

Но как показывает практика, лучше использовать нативный плагин :)

 Нет комментариев    213   2017   ios   unity   с#

SSL/TLS в Unity

А сегодня мы будем шифровать весь трафик. А зачем? А чтобы усложнить жизнь читерам.

Если у вас мультиплеер на авторитарном сервере, естественно все действия должны контролироваться на нем. Но зная, какие команды отправлять, можно легко написать бота, который автоматизирует некоторые процессы, нарушая баланс и игру «правильных» игроков. Конечно, если сильно захотеть, можно сломать все что угодно, но усложнить жизнь взломщикам и читерам тоже можно. Будем использовать SSL.

Ничего изобретать не буду, потому что «все уже придумано до нас» :)
Для теста серверной части (на node.js) и генерации ключей используем эти скрипты. Единственное, чего там нет — это как из сертификата и ключа сделать PFX файл:

openssl pkcs12 -export -in client.crt -inkey client.key -out mycert.pfx

Этот файл понадобится для клиента. Для проверки SSL соединения берем пример с сокетами с MSDN и немного переделываем его. Получаем поток и прокидываем его через SSL.

IEnumerator UseSSL () {
	NetworkStream stream = new NetworkStream (socket);
	SslStream sslStream = new SslStream (stream, false, new RemoteCertificateValidationCallback (CertificateValidationCallback), new LocalCertificateSelectionCallback (CertificateSelectionCallback));
	bool authenticationPassed = true;		
	#if UNITY_EDITOR
		X509Certificate2 cert = new X509Certificate2(certPath, certPassword);
	#elif UNITY_ANDROID || UNITY_IOS
		WWW reader = new WWW (certPath);
		while (!reader.isDone) 
			yield return null;
		X509Certificate2 cert = new X509Certificate2 (reader.bytes, certPassword);
	#endif
	X509Certificate2Collection certs = new X509Certificate2Collection();
	certs.Add (cert);
	sslStream.AuthenticateAsClient (server, certs, SslProtocols.Tls, true);
	authenticationPassed = sslStream.IsAuthenticated;
	if (authenticationPassed) {
              ///
	}
	yield break;
}

Вот и весь квест. Теперь трафик между игровым сервером и клиентом зашифрован.

Ccылка на Github

 2 комментария    1045   2017   git   unity   разработка   с#

Система плагинов для iOS и Android

Простая система плагинов для мобилок. Контроллер инициализирует все плагины и обрабатывает приходящие сообщения. Обмен данными в формате JSON.

Вообще, печаль конечно, что Unity не реализует даже самые простые варианты работы с мобильными платформами, тот же браузер или камера. Может когда-нибудь...

Ссылка на Github

Cериализация в XML и обратно

Обновлено 17.04.17
Продвинутая версия сериализации в XML, работает в несколько раз быстрее штатной

Как преобразовать объект в строку, например для сохранения или передачи, а потом обратно? Есть две простые функции, которые я использовал в одном из Unity проектов:

public static object Deserialize<T> (string toDeserialize) {
	XmlSerializer xmlSerializer = new XmlSerializer (typeof (T));
	StringReader textReader = new StringReader (toDeserialize);
	return xmlSerializer.Deserialize (textReader);
}

public static string Serialize<T> (T toSerialize) {
	XmlSerializer xmlSerializer = new XmlSerializer (typeof (T));
	StringWriter textWriter = new StringWriter ();
	xmlSerializer.Serialize (textWriter, toSerialize);
	return textWriter.ToString ();
}

Использовать очень просто:

Vectror3 vector = new Vector3 (10f, 10f, 10f); // какой-то объект
string data = Serialize<Vector3> (vector); // преобразовываем в строку
vector = (Vector3) Deserialize<Vector3> (data); // обратно из строки в объект
 4 комментария    573   2017   unity   разработка   с#