пятница, 24 января 2014 г.

VisualWebSiteOptimizer баг с вариантами.

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

Thank you for contacting us. I have checked the test and would like to tell you that there is an iframe element when the webpage is loaded in the VWO editor which is causing the CSS selector path to be different when the visitor lands on the webpage directly and hence VWO is not able to find the element and apply the changes (please check the screenshot 1 attached). 

So I would recommend that you either insert an ID on the DIV element after the opening body tag so that the CSS selector path is calculated from that element or you can create an MVT test and add the CSS selector path (font.actiontitle>center) manually (as shown in the screenshot2) and then disable all the variations except for the control and the last variation in the test.
  Я как нормальный человек понял мало. Но взглянул на лэндинг которые сверстали фрилансеры, я впал в не большую фрустрацию с большим желанием выпить ещё одну чашку кофе. Дело в том, что на КАЖДЫЙ тег приходился свой собственный id, самое забавное что это не генерированная странница а свёрстанная вполне реальным человеком с вполне функциональными руками (ну по крайней мере в ухе ими поковыряться можно). Из-за такой "пёрстости" страницы, ломались селекторы. Проблема решалась заклчением всей страницы в див с id = VWOWrapper, под всей страницей я имею ввиду конечно-же всё от ‹ body › и до ‹ /body › .
Надеюсь Вам это помогло!

Кастомизация Unity Web Player [Preloader]


четверг, 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 и много всего вкусного.