четверг, 9 января 2014 г.

Хостинг Unity Web Player

Написал основу за выходные, сейчас тружусь над улучшением сервиса :) 

Хостинг Unity Web Player

Пишем Dissolve Shader [Хабра]

Введение

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

  Путей реализации есть несколько:
  • Alpha
  • CutOff
  • Grab Texture
В итоге у нас получится 3 шейдера, 2 использующие только альфу и которые могут взлететь на моб. девайсах и один с AlphaTest который выглядит посимпатичней но более прожорливый. Благодаря AlphaTest мы можем отключить отсечение невидимых полигонов и не получать наслоение. Но заплатить придётся шейдерной моделью 2.0 и использовать 3.0 из-за чего нельзя будет использовать на моб. девайсах.

Каркас

Общий алгоритм примерно такой:
  • Берём яркость пикселя с маски разрушения или прямо с главной текстуры
  • Сравниваем эту яркость с N
  • Если яркость больше N то альфу пикселя ставим в нолик
В итоге получится не так красиво как на первом видео.
Нам не хватает карт нормалей, и самого крутого. Линий! Алгоритм инлайнов у меня такой:
  • Берём яркость пикселя с маски разрушения, но с оффсетом по UV + LineSize. И ещё один но оффсет уже UV - LineSize
  • Если хотя бы один из пикселей меньше N, то мы устанавливаем цвет пикселя из текстуры для линий
  • Иначе ставим альфу в ноль (Это как замена аналогичной операции в первом алгоритме)
Резюмирую выше сказанное, мы получаем отсечение по маске и если пиксели отсекаем то проверяем нету ли впритык к нему не отсечённые, если они есть мы становимся краем и ставим себе определённый цвет.

Ближе к телу коду

Если прибавить ко всему выше сказанному наложение нормалек и смещение текстуры линии ещё по синусоидальному времени, получится вот такое вот полотно.
 Shader "HolyMonkey/Dissolve/Bumped" {

 Properties {
     _MainColor ("Main Color", Color) = (1,1,1,1)
  _MainTex ("Base (RGB)", 2D) = "white" {}
  _Mask("Mask To Dissolve", 2D) = "white" {}
  _LineTexture("Line Texture", 2D) = "white" {}
  _Range ("Range", Range(0,3)) = 0
  _LineSize ("LineSize", Float) = 0.001
  _Color ("Line Color", Color) = (1,1,1,1)
  _BumpMap ("Normalmap", 2D) = "bump" {}
  _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
 }
 
 SubShader {
  Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
  LOD 300
  ZWrite On
  Cull Off
  CGPROGRAM 
  #pragma target 3.0
  #include "UnityCG.cginc"
  #pragma surface surf Lambert alphatest:_Cutoff
        

  sampler2D _MainTex;
  sampler2D _LineTexture;
  sampler2D _BumpMap;
  sampler2D _Mask;
  half4 _Color;
  half4 _MainColor;
  float _Range;
  float _LineSize;
             
  struct Input {
   float2 uv_MainTex;
   float2 uv_BumpMap;
                        float2 uv_Detail;
  };
            
  void surf (Input IN, inout SurfaceOutput o) {
   half4 c = tex2D (_MainTex, IN.uv_MainTex);
   half4 m = tex2D (_Mask, IN.uv_MainTex);
          half4 lc =  tex2D (_Mask, IN.uv_MainTex - _LineSize);
          half4 lc2 = tex2D (_Mask, IN.uv_MainTex + _LineSize);
   half4 lc3 = tex2D(_LineTexture, IN.uv_MainTex + _SinTime) * _Color;    
    
          o.Albedo = c *  _MainColor;
          o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
          o.Alpha = 1;
      
   float factor = m.rgb.x + m.rgb.y + m.rgb.z;
   if(factor >= _Range)
   {
      float factor2 = lc.rgb.x + lc.rgb.y + lc.rgb.z;
      float factor3 = lc2.rgb.x + lc2.rgb.y + lc2.rgb.z;
      if(factor2 < _Range || factor3 < _Range)
      {
         o.Albedo = lc3;
      }
      else
      {
                  o.Alpha = 0.0;
               }
            }
  }
  ENDCG
 } 
 Fallback "Diffuse"
}
Ещё можно поиграться с не которыми аспектами и получить следующее
Shader "HolyMonkey/Dissolve/Culling-Mobile" {

 Properties {
     _MainColor ("Main Color", Color) = (1,1,1,1)
  _MainTex ("Base (RGB)", 2D) = "white" {}
  _Mask("Mask To Dissolve", 2D) = "white" {}
  _LineTexture("Line Texture", 2D) = "white" {}
  _Range ("Range", Range(0,3)) = 0
  _LineSize ("LineSize", Float) = 0.001
  _Color ("Line Color", Color) = (1,1,1,1)
  _BumpMap ("Normalmap", 2D) = "bump" {}
 }
 
 SubShader {
  Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
  LOD 300
  CGPROGRAM 
  #pragma target 2.0
  #include "UnityCG.cginc"
  #pragma surface surf Lambert alpha 
        

  sampler2D _MainTex;
  sampler2D _LineTexture;
  sampler2D _BumpMap;
  sampler2D _Mask;
  half4 _Color;
  half4 _MainColor;
  float _Range;
  float _LineSize;
             
  struct Input {
   float2 uv_MainTex;
   float2 uv_BumpMap;
            float2 uv_Detail;
  };
            
  void surf (Input IN, inout SurfaceOutput o) {
   half4 c = tex2D (_MainTex, IN.uv_MainTex);
   half4 m = tex2D (_Mask, IN.uv_MainTex);
      half4 lc =  tex2D (_Mask, IN.uv_MainTex - _LineSize);
      half4 lc2 = tex2D (_Mask, IN.uv_MainTex + _LineSize);
   half4 lc3 = tex2D(_LineTexture, IN.uv_MainTex + _SinTime) * _Color;    
    
      o.Albedo = c *  _MainColor;
      o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
      o.Alpha = 1;
      
   float factor = m.rgb.x + m.rgb.y + m.rgb.z;
   if(factor >= _Range)
   {
      float factor2 = lc.rgb.x + lc.rgb.y + lc.rgb.z;
      float factor3 = lc2.rgb.x + lc2.rgb.y + lc2.rgb.z;
      if(factor2 < _Range || factor3 < _Range)
      {
         o.Albedo = lc3;
      }
      else
      {
                  o.Alpha = 0.0;
               }
            }
  }
  ENDCG
 } 
 Fallback "Diffuse"
}
Shader "HolyMonkey/Dissolve/NotTransparent" {

 Properties {
     _MainColor ("Main Color", Color) = (1,1,1,1)
  _MainTex ("Base (RGB)", 2D) = "white" {}
  _BackTexture ("Back Texture", 2D) = "white" {}
  _Mask("Mask To Dissolve", 2D) = "white" {}
  _LineTexture("Line Texture", 2D) = "white" {}
  _Range ("Range", Range(0,3)) = 0
  _LineSize ("LineSize", Float) = 0.001
  _Color ("Line Color", Color) = (1,1,1,1)
  _BumpMap ("Normalmap", 2D) = "bump" {}
 }
 
 SubShader {
  LOD 300
  ZWrite On
  Cull Off
  
  CGPROGRAM 
  #pragma target 2.0
  #include "UnityCG.cginc"
  #pragma surface surf Lambert
        

  sampler2D _MainTex;
  sampler2D _LineTexture;
  sampler2D _BumpMap;
  sampler2D _Mask;
  sampler2D _BackTexture;
  half4 _Color;
  half4 _MainColor;
  float _Range;
  float _LineSize;
             
  struct Input {
   float2 uv_MainTex;
   float2 uv_BumpMap;
            float2 uv_Detail;
  };
            
  void surf (Input IN, inout SurfaceOutput o) {
   half4 c = tex2D (_MainTex, IN.uv_MainTex);
   half4 m = tex2D (_Mask, IN.uv_MainTex);
      half4 lc =  tex2D (_Mask, IN.uv_MainTex - _LineSize);
      half4 lc2 = tex2D (_Mask, IN.uv_MainTex + _LineSize);
   half4 lc3 = tex2D(_LineTexture, IN.uv_MainTex + _SinTime) * _Color;    
   half4 bc  = tex2D(_BackTexture, IN.uv_MainTex);
      o.Albedo = c *  _MainColor;
      o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
      
   float factor = m.rgb.x + m.rgb.y + m.rgb.z;
   if(factor >= _Range)
   {
      float factor2 = lc.rgb.x + lc.rgb.y + lc.rgb.z;
      float factor3 = lc2.rgb.x + lc2.rgb.y + lc2.rgb.z;
      if(factor2 < _Range || factor3 < _Range)
      {
         o.Albedo = lc3;
      }
      else
      {
         o.Albedo = bc;
         o.Normal = float3(1,1,1);
      }
            }
  }
  ENDCG
 } 
 Fallback "Diffuse"
}

Заключение

Написать получилось мало, но думаю дедуктивный код это искупает. Там используются простые вещи, и на хабре есть статьи с их ним разбором. Исходники со всеми нужными ресурсами Вы можете скачать из репозитория на GitHub

четверг, 26 декабря 2013 г.

My Dissolve Shader for Unity3D

Эх, помню три года назад когда только начинал путь в программирование и разработку игр, увидел демонстрацию такого же шейдера. Он меня привёл в поросячий восторг, и я считал что чтобы сделать его, нужно быть гуру. Но оказалось всё на много проще, сидел, делать нечего, вспомнил про эту штуку, сделал за 10 минут :c

Первый вариант


      Второй вариант



понедельник, 23 декабря 2013 г.

Вещи которые вы возможно не знали о Unity3D [Хабра]

О чём телега

Я бы хотел описать возможности, ну и частично особенности которые присутствуют при разработке на данном движке. С Unity3D работаю уже около трёх лет, так что мыслей накопилось достаточно для средненькой такой статьи. Местами буду через чур мудрить, и рассказывать о том что вам вряд ли пригодится, но по большой части рассказ будет о очень простых и часто используемых вещах (если знать о них конечно).

Режим редактора для дебага.

Знаете такие моменты, когда у нас есть приватное поле у компонента, и нам нужно посмотреть его значение? Я раньше всегда либо выводил его значение в лог, или по быстрому отмечал нужными атрибутами его или вовсе делал public. Но есть способ на много круче.

image
Для демонстрации я создал простой компонент:
using UnityEngine;
using System.Collections;

public class PrivateFieldTest : MonoBehaviour {

 private int Score;

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

 image

Start, it's trap

Метод старт может работать как корутин. С Update допустим не прокатит такой ход конём.
using UnityEngine;
using System.Collections;

public class StartIsCorutineTest : MonoBehaviour {

 IEnumerator Start () 
 {
  print("I'm wait 5 second!");
  yield return new WaitForSeconds(5);
  print("I'm done!");
 }

}
Это достаточно полезно когда в старте что нибудь может вызывать не которые задержки. Конечно, можно вызывать корутин прямо из старта, но так согласитесь лаконичней.

SendMessage. Или штука которая делает вам приятно.

Я долго думал, стоит ли добавлять описание данной возможности, ибо многие и так об этом знают, но у меня оказалась пара коллег для которых данная штука оказалась открытием. Фишка то в чём. Допустим, у нас есть некий компонент и ему нужно срочно сообщить другому компоненту что-то важное. Какой обычно подход:
gameObject.GetComponent().TakeDamage(60);
Очень увлекательно, слегка массивный синтаксис (но все же мы видели C++ по новым канонам). Но главная проблема кроется в том, что мы должны знать тип компонента на который получаем ссылку. SendMessage помогает избавиться от этого нюанса, и ему нужно только знать имя метода и аргументы.
gameObject.SendMessage("TakeDamage", 60);
Проблема конечно, что не возвращает то что должен возвращать вызываемый метод. Но мы можешь создать план капкан, и получить свой маленький JS который к слову есть но это на самом деле не JS. Допустим передав в аргументах call back функцию для принятия значений для возврата. Это стильно, модно, молодёжно и не должно порицаться в современном и толерантном обществе. А ещё могут быть проблемы если весит компонент у которого есть
метод с таким же названием.

 image

Scale объекта ломает батчинг мешей

Последнее время участились вопросы на эту тему. Частично это так, сюда бы отлично зашла картинка выше. Велик шанс что он сломается, но может и не сломаться. Тут много факторов, обратимся к офф справке: - Для Dynamic Batching как правило, объекты должны иметь одинаковый Scale, исключением является то что если все объекты не равномерно масштабированы то они могут склеиться. Что это значит я сам не до конца понял, по этому просто не масштабирую. Если батчинг сломается, то это приведёт к увеличению Draw Call, что очень плохо.

 image

Выбранный объект можно закрепить в инспекторе

Полезно когда у нас очень много объектов в сцене, и нам нужно поместить ссылку на один из них в какой нибудь компонент, который уже на одном из объектов. Как правило, хватает выделения объект с компонентом, и перетаскивание объекта из иерархии. Но может появиться проблема когда оба объекта достаточно далеки друг от друга (в иерархии) из-за чего может начаться нервный тик. Но есть маленький замочек который фиксирует инспектор на определённом объекте.

image
Так же можно открыть много вкладок с инспектором и зажимать нужные на нужных объектах.

WWW Утечка памяти.

У класса WWW есть очень серьёзное проблема, которая даёт о себе знать когда нужно загрузить много текстур. В справке в пример приводится следующий код:
    IEnumerator Start() {
        WWW www = new WWW(url);
        yield return www;
        renderer.material.mainTexture = www.texture;
    }
Проблема в том, что если получать текстуру через поле то создаётся новая текстура, каждый раз, которая ещё и не хочет и из памяти выгружаться. В итоге я применил такой код, который позволяет от этого избавиться:
//код урезан в дедуктивных целях. 
public override void OnWWWFinish(WWW www)
 {
        Texture2D tex = new Texture2D(4, 4, TextureFormat.RGB24, false, false);
        www.LoadImageIntoTexture(tex);
        tex.Compress(false);
        www.Dispose();
 }

Прощальные слова

За один присест вспомнить удалось не много, а описать с ходу и того меньше. Но надеюсь описал действительно интересный вещи, в основном конечно по редактору. Правильно ли сделал что начал с мелочей? Я не знаю. Понравится, напишу ещё, не понравится, всё равно напишу.

воскресенье, 3 ноября 2013 г.

Парсинг сайтов на C#.

Для одного из своих приложений мне понадобилось достаточно много информации которой полно на различных wiki но всё это собирать вручную достаточно геморно. По этому я решил вооружится C# и распарсить всё что мне надо.  Мы будем использовать хорошую библиотеку Html Agility Pack которая берёт на себя большую часть работы и предоставляет для нас удобное DOM дерево с которым мы можем работать. Она поддерживает XPath запросы, LINQ и много всего вкусного.

воскресенье, 27 октября 2013 г.

Механизм исключений C#.

Не преодолев желания налабать что нибудь в блог, решил написать не большую статейку об исключениях в C#. Я не буду писать много воды и перейду сразу к делу. Логически я разделю статью на две части, обработка и выброс исключений. Собственно больше о них сказать мне не чего.


Не много об исключениях

Для начала определимся что такое исключения, и для чего они вообще нам нужны. На сколько мне известно, в достаточно старых языках таких как Си, для контроля за работай программы использовали примитивные способы, на подобие возврата кода ошибки из функции. Удобного в этом мало, да и попросту забыть проконтролировать код ошибки очень просто из-за чего может начаться не контролируемое поведения программы и бессонная ночь отловки бага :) Конечно в Си с помощью макросов и прочего есть не что подобное. Но в более поздних языках это представлено более лаконично. И так, смоделируем ситуацию, допустим у нас есть некий метод  который принимает пользовательский объект для последующей работы с ним. А теперь представим что мы в эту функции передали выражение со значением null. Что будет? Если метод попробует начать работу с пустышкой, система выбросит NullReferenceException, но по хорошему метод перед работай проверить правильность всех аргументов и сам выбросить нужные исключения. Выброшенная ошибка будет распространятся вверх по цепочки вызовов пока её не отловят или она не достигнет верхней точки и программа не завершится аварийно. Думаю теперь понятно зачем нужен механизм исключений?

Отлов исключений  
Для отлова исключений в C# предусмотрено три ключевых слова, все они относятся к одной системе и работает на подобии оператора if-else. Это try\catch\finaly. В тело оператора try помещается код, за которым нужно будет так сказать проследить. Catch блок обработки определённого исключения а finaly блок который будет вызван после обработки исключения для того что бы освободить ресурсы. Но по меньше лирики перейдём к коду :


....
try
{
    SomeObject _so;
    _so.SomeMethod();
}
catch(NullReferenceExcepetion e)
{
     //Обработка NullReferenceExeption
}
catch(Exception e)
{
    //Обработка вообще всех исключений
}
finaly
{
   //Блок который вызовется после обработки исключения
   // (если исчклюения не было блок не вызовется)
}
....
    
Из комментариев думаю всё ясно. Так же если вызвать некий метод в блоке try, то обработка исключения выполнится. Так же я бы хотел рассказать не много об особенности работы оператора catch. Как вы уже заметили в скобках нужно указывать класс исключения, да-да, все исключения это просто классы унаследованные от класса Exception. И все кто понимает механизм полиморфизма и ковариантности, догадаются что если объявить блок catch отлавливающий исключения Exepetion то он будет срабатывать на ВСЕ ошибки. Но, если мы хотим знать что мы именно поймали, стоит сверху от отловки Exception ловить более узкоспециализированные исключения. Так же из всех операторов catch выбирается один, тот который первый подошел. Так что не стоит указывать выше всех отлов Excepetion так как всё что ниже просто будет игнорироваться.

"Выброс" своих исключений

Для инициирования своих исключений в C# предусмотрен оператор throw. Справа от оператора указывается выражения которое имеет тип производный от System.Exception.
Проще говоря нужно указать ссылку на класс который пронаследован от System.Exception. Так же в .Net Framework пред усмотрена куча классов для этих дел, но при желание вы можете создать свои.  В общем синтаксис примерно такой:

public class ThrowTest2

 {
   static int GetNumber(int index)
   {
      int[] nums = { 300, 600, 900 };
      if(index > nums.Length)
      {
          throw new IndexOutOfRangeException();
      }
     return nums[index];
   }
   static void Main()
   {
      int result = GetNumber(3);
   }
 

Резюмируя

В заключения хочу сказать что можно найти ещё кучу всего интересного об исключениях в C#, я же взялся рассказать базовые возможности. Свои замечания пишите в комментариях, постараюсь поправить статью если что не так :)