Результати пошуку за запитом: обучение c*
Flappy Bird за 30 хвилин
Автор: Олег Загородній
Введение
Не так давно была популярной такая мобильная игра, как Flappy Bird. Причем по разным причинам из магазинов она пропала также неожиданно, как и появилась. Однако, учитывая ее популярность, сразу образовалось множество клонов. Не столь качественных, конечно. Но почему их было много? Все из-за того, что сама игра делается довольно просто и быстро.
И в этой небольшой статье мы, разумеется, рассмотрим, как же сделать такую игрушку, как Flappy Bird. Все ресурсы (спрайты, шрифты) принадлежат непосредственно их авторам.
Ресурсы
Все, что нам понадобится из ресурсов игры – это несколько спрайтов и шрифт, как в оригинале.
Спрайты найдены на просторах интернета.
Шрифт был скачан по ссылке: http://www.dafont.com/04b-19.font?text=Flappy+Number
Подготовка игровых объектов
Сначала сделаем префаб игрока, то есть птичку. Для этого создаем на сцене пустой объект с именем Bird. Внутрь него помещаем объект-спрайт с именем Body и в свойство Sprite компонента Sprite Renderer помещаем спрайт нашей птички (из папки Sprites).
Теперь на саму птичку (объект Bird) прикрепляем компонент Circle Collider 2D и задаем его радиусу значение 0.45.
Также необходимо прикрепить компонент Rigidbody 2D. Здесь, пожалуйста, не перепутайте. Нам необходим именно 2D компонент, а не обычный Rigidbody. Ему задаем значение гравитации (Gravity Scale) равным 2.45 и запрещаем передвижение по оси X, чтобы наша птичка неожиданно не улетела куда-то в сторону.
Теперь создаем C# скрипт c именем BirdHelper и тоже прикрепляем его к птичке (объект Bird).
После всего этого перетягиваем объект Bird из окна Hierarchy в окошко Project, создав таким путем префаб птички. То есть в итоге на префабе Bird должно быть четыре компонента: Transform, Circle Collider 2D, Rigidbody 2D и скрипт Bird Helper.
С главным героем пока что покончили.
Приступим теперь к единственным препятствиям в игре – трубам. Мы с Вами поступим очень хитро. Так как в процессе игры каждая преграда – это пара труб (одна сверху, другая – снизу), их длину можно было бы регулировать и кодом в момент создания. Ведь если нижняя труба короткая, то верхняя – длинная. Но мы пойдем более простым путем. Наша преграда будет сразу состоять из двух длинных труб, и мы просто-напросто будем их ставить выше или ниже. Как на картинке ниже, где светлая рамка – границы дисплея.
Что ж, дабы заделать префаб преграды, создаем на сцене пустой объект с именем Pipes и помещаем внутрь него два объекта-спрайта с именами TopPipe и BottomPipe. В Каждому из них в свойство Sprite компонента Sprite Renderer перетаскиваем спрайт Pipe (из папки Sprites).
Объекту BottomPipe ставим положение по оси Y -4.5 (отрицательное).
С объектом TopPipe проделываем аналогичные манипуляции, но позиция по оси Y будет 4.5, и еще необходимо повернуть его на 180 градусов вокруг оси Z.
Почти готово. Осталось только настроить коллайдеры и прикрепить скрипт. Начнем с коллайдеров. Прикрепим на объект Pipes компонент Box Collider 2D. А лучше сразу три.
Первый настроим таким образом, как на картинке. Просто немного подкорректируем размер и зададим позиции по оси X значение -4.5. Как вы, думаю, уже догадались - это будет коллайдер для нижней трубы.
Следующий Box Collider 2D настроим аналогично предыдущему, только позиция по оси X будет 4.5.
Теперь последний коллайдер. Он, на самом деле, будет триггером, и с помощью него мы сможем отследить, когда же игрок преодолел текущее препятствие. Вот такие настройки должны быть у этого коллайдера (уже триггера).
И под конец создаем скрипт с именем PipesHelper и прикрепляем его на объект Pipes.
Теперь перетягиваем объект Pipes из окна Hierarchy в окно Project, создав таким путем префаб для препятствий.
Остался только фон. Создаем пустой объект с именем Background. Помещаем в него два объекта-спрайта с именами Part1 и Part2 и в свойство Sprite компонента Sprite Renderer помещаем спрайт Background.
Объекту Part1 задаем размеры X: 2.6, Y: 2.6. С объектом Part2 выполняем такие же действия, но еще сдвигаем его вправо на 7.2 юнитов по оси X.
После этого в объект Background помещаем еще два объекта-спрайта с именами Ground1 и Ground2. Им назначаем спрайты Ground из папки Sprites.
Вот так должны выглядеть настройки объектов Ground1 и Ground2.
Напоследок необходимо добавить коллайдер для земли и создать анимацию движения фона.
Прикрепляем компонент Box Collider 2D на объект Background.
Для того, чтобы создать анимацию, выделяем Background в окне Hierarchy и в окошке Animation нажимаем кнопку Create. Назовем ее BackgroundFloating.
Вся задача данной анимации – передвижение фона влево, чтобы создать эффект, как будто игрок на самом деле летит вправо. После того, как анимация будет создана, на объект Background автоматически прикрепится компонент Animator и будет создан Animator Controller. Нам осталось только перейти в окно Animator и установить значение скорости анимации 0.2.
Под конец создаем скрипт GameHelper и цепляем его на игровую камеру. На этом, пожалуй, все игровые приготовления завершены.
Непосредственно сам процесс разработки
Начнем, я так думаю, из скрипта главного персонажа. То есть BirdHelper’a. Реализация полета птички, как в оригинале, довольно проста. Под действием силы гравитации она будет постоянно падать, а при нажатии клавиши, допустим, Space, мы применим к ней силу по направлению вверх, используя метод AddForce на компоненте Rigidbody2D.
using UnityEngine;
public class BirdHelper : MonoBehaviour
{
public float force;
private new Rigidbody2D rigidbody;
void Awake()
{
rigidbody = GetComponent<Rigidbody2D>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);
rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);
}
}
С помощью метода MoveRotation мы совершаем поворот птицы в зависимости он величины и знака значения текущего ее ускорения. Поле force у нас открытое и, разумеется, отобразится в окне Inspector. Напишем там 8. Это будет сила “прыжка”.
Со скриптом главного героя почти всё. Вернемся к нему немножко позже.
Перейдем к трубам. Их задача - двигаться на игрока, ведь сам по себе он лишь летает по вертикальной оси в процессе игры и так и не сдвинется по горизонтальной. Движение препятствий можно реализовать статическим методом MoveTowards структуры Vector3.
using UnityEngine;
public class PipesHelper : MonoBehaviour
{
[SerializeField]
private float speed;
void Start()
{
Vector2 position = transform.position;
position.y = Random.Range(-1.5F, 2.5F);
transform.position = position;
Destroy(gameObject, 6.0F);
}
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, transform.position - transform.right, speed * Time.deltaTime);
}
}
При появлении препятствие будет выбирать случайную позицию по оси Y, но такую, чтобы не было видно конца верхней или нижней трубы. Затем каждый кадр будет двигаться влево. Для этого, задавая конечную точку движения, мы вычитаем из текущей позиции препятствия вектор, направленный вправо от него. Также через 6 секунд объект Pipes будет уничтожен, чтобы не нагружать устройство, так как он свою задачу уже выполнил.
К полю speed, представляющему скорость движения препятствия, мы применили атрибут SerializeField, чтобы оно было отображено в Inspector’e, ведь мы его закрыли.
Перед тем, как перейти к скрипту GameHelper, добавим на игровую сцену объект-текст с именем ScoreText и настраиваем, как на рисунке ниже.
Это будет текст для отображения количества очков игрока.
Еще давайте добавим объект-кнопку с именем RestartButton. Она будет появляться, когда игрок проиграет, то есть при столкновении с каким-либо препятствием. В свойство SourceImage компонента Image нашей кнопки перетащите спрайт Button из папки Sprites. Вот настройки кнопки.
А вот настройки текста внутри кнопки RestartButton.
Непосредственно объект кнопки нужно деактивировать. Иерархия объектов на сцене будет выглядеть следующим образом.
Какая же задача скрипта GameHelper? Он будет отвечать за генерацию новых препятствий, подсчет очков и их отображение. А еще за перезапуск уровня. Давайте глянем код.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class GameHelper : MonoBehaviour
{
[SerializeField]
private Text scoreText;
private GameObject pipes;
public Button restartButton;
[HideInInspector]
public int score;
void Awake()
{
pipes = Resources.Load<GameObject>("Pipes");
}
void Start()
{
StartCoroutine(GeneratePipes());
}
void Update()
{
scoreText.text = "Score: " + score;
}
IEnumerator GeneratePipes()
{
Vector2 position;
while(true)
{
position = transform.position;
position.x += 6.0F;
Instantiate(pipes, position, Quaternion.identity);
yield return new WaitForSeconds(2.0F);
}
}
public void Restart()
{
# if UNITY_5_2
Application.LoadLevel(Application.loadedLevel);
#endif
Time.timeScale = 1.0F;
}
}
В первую очередь не забудьте подключить пространства имен UnityEngine.UI для работы с элементами пользовательского интерфейса и System.Collections, ведь там находится необходимый нам интерфейс IEnumerator, который мы будем использовать для карутины.
Метод Reset отвечает за перезапуск уровня при нажатии соответствующей кнопки. Значит, настройки компонента Button объекта ResetButton необходимо немножко подправить. Так как сцена у нас не тяжелая, то мы можем позволить себе просто перезагружать ее полностью. Но, так как свойство timeScale статическое, его значение стоит снова установить в 1.0, чтобы при перезагрузке сцены время шло своим чередом.
Метод GeneratePipes является карутиной и его задача – бесконечным циклом создавать препятствия с задержкой между каждыми в 2.0 секунды. Запускается карутина методом StartCaroutine при старте игры. А в методе Awake подгружается из папки Resources модель препятствия.
В поля scoreText и restartButton, которые будут отображены в окне Inspector, необходимо перетащить соответствующие объекты текста очков и кнопки рестарта игры.
Каждый кадр в свойство text объекта scoreText будет передавать текущее количество очков, которое хранится в поле score.
Почти закончили. Осталось немного дополнить скрипт BirdHelper.
using UnityEngine;
public class BirdHelper : MonoBehaviour
{
public float force;
private new Rigidbody2D rigidbody;
private GameHelper;
void Awake()
{
rigidbody = GetComponent<Rigidbody2D>();
gameHelper = Camera.main.GetComponent<GameHelper>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);
rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);
}
void OnCollisionEnter2D (Collision2D collision)
{
gameHelper.restartButton.gameObject.SetActive(true);
Time.timeScale = 0.0F;
}
void OnTriggerExit2D (Collider2D other)
{
gameHelper.score++;
}
}
Метод OnTriggerExit2D сработает в момент выхода игрока из триггера, который находится внутри препятствия и добавит нам одно очко.
OnCollisionEnter2D будет вызван при столкновении с любым коллайдером, а это значит, что игрок проиграл. Время остановится и активируется кнопка перезапуска игры.
На этом, пожалуй, всё. Скачивайте проект, изучайте, дополняйте, переделывайте либо просто удаляйте.
Спасибо большое всем за внимание. Удачи в начинаниях и творческих успехов!
Індексація стовпців у Redis
Автор: Редакция ITVDN
Введение
В отличие от memcache, Redis может быть использован в качестве постоянного хранилища, а не только как временный кэш. Так случилось, что Redis — это невероятно быстрая база данных, дающая поразительно лучшую производительность Вашему приложению в случае правильной настройки. В качестве предостережения хотелось бы добавить, что при работе с Redis в качестве основного хранилища таится много рисков, и эти риски значительно увеличиваются в случае некорректной настройки. Настоятельно рекомендуем предварительно провести тщательное исследование Redis перед тем, как заменять Вашу текущую базу данных в пользу Redis.
Несмотря на преимущество Redis в виде невероятной скорости работы, дающейся Вашему приложению, есть факт, который стоит обязательно учесть – Redis – это фундаментальное хранилище ключей/значений, и он не поддерживает индексы. И Вы можете столкнутся с непредвиденным «челленджем» при попытке проиндексировать Ваши значения. Однако, Вы можете обойти эти ограничения с помощью удивительно полезных предоставляемых Redis типов данных. В этой статье буду рассмотрены способы использования каждых наборов и отсортированных наборов для каждого индекса, и как сортировать записи по датам, а также извлекать строки в пределах диапазона дат.
О комплексных типах Redis
Redis поддерживает несколько комплексных типов — списки, «сеты», отсортированные «сеты» и хэши. В текущей статье не будут упомянуты типы, за исключением «сетов» и отсортированных «сетов». Собственно, «сеты» — это коллекция уникальных неупорядоченных значений. Между тем, отсортированные сеты немного отличаются от обычных сетов. Они позволяют хранить значения, к примеру, с баллами и, таким образом, все члены отсортированного набора можно будет запросить с помощью баллов или диапазона баллов. Это может быть очень удобным при хранении, например, даты. Происходит конвертация каждого одиночного элемента (даты) и хранится в его «тике». Таким образом получается упорядоченный набор значений. Затем можно использовать команду ZRANGEBYSCORE REDIS для получения значений, которые подходят под определённый диапазон.
Redis, как хранилище ключевых значений, может также хранить элемент с любым типом данных в базе данных до тех пор, пока этот элемент будет иметь уникальный ключ, определённый для него, вне зависимости от того, какого типа этот элемент – строковый или числовой.
Использование в коде
Код не так страшен, как может показаться с первого взгляда. Например, целый объект сохраняется с указателем ко всем индексам. Подход таков, что объект может быть сохранён, как скалярная строка с id в различных индексах.
В коде есть класс «Person», позволяющий сохранять объекты класса «Person» с параметрами, например, имя, пол, страна проживания и дата рождения. Класс написан достаточно просто, на мелкие детали не придётся обращать внимание. Также в коде есть статический класс «RedisAdaptor», который содержит в себе вспомогательные функции для сохранения класса «Person» и вызова его объектов.
Основная программа
В основной программе имеется цикл, который прогоняется ежедневно на протяжении указанного года – 1971 и создаёт новый объект класса «Person» с указанием даты рождения. В случае с полом мы оставляем один единственный объект с указанием женского или мужского пола. Для страны (учитывая, что определены только 3 страны) каждый объект класса «Person» проживает в Индии, США и Англии. В качестве имени используется строка, генерируемая случайным образом.
static void Main(string[] args)
{
const int YEAR = 1971;
// We create one Person object for every single day in the given year.
for (int month = 1; month <= 12; ++month)
{
for (int day = 1; day <= 31; ++day)
{
try
{
// Get any random name:
string name = Util.GetAnyName();
// And a DoB:
DateTime dob = new DateTime(YEAR, month, day);
// As for the gender, let's alternate:
Gender gender = Gender.FEMALE;
if (day % 2 == 0)
{
gender = Gender.MALE;
}
// And the country, let's round-robin between all three:
Country country = Country.INDIA;
if (day % 3 == 1)
{
country = Country.USA;
}
else if (day % 3 == 2)
{
country = Country.GB;
}
// Create a new Person object:
Person person = new Person(name, gender, country, dob);
//Console.WriteLine ("Created new Person object: {0}", person);
// We call the function that will store a new person in Redis:
RedisAdaptor.StorePersonObject(person);
}
catch (Exception)
{
// If the control reaches here, it means the date was illegal.
// So we just shrug your shoulders and move on to the next date.
continue;
}
}
}
// At this point, we have 365 Person objects as a sorted set in our Redis database.
// Next, let's take a date range and retrieve Person objects from within that range.
DateTime fromDate = DateTime.Parse("5-May-" + YEAR);
DateTime toDate = DateTime.Parse("7-May-" + YEAR);
List persons = RedisAdaptor.RetrievePersonObjects(fromDate, toDate);
Console.WriteLine("Retrieved values in specified date range:");
foreach (Person person in persons)
{
Console.WriteLine(person);
}
// Next, let's select some folks who are female AND from the USA.
// This calls for a set intersection operation.
List personsSelection = RedisAdaptor.RetrieveSelection(Gender.FEMALE, Country.USA);
Console.WriteLine("Retrieved values in selection:");
foreach (Person person in personsSelection)
{
Console.WriteLine(person);
}
}
В классе «Redis Adaptor» есть одиночная функция, используемая для хранения и индексации значений, прошедших через него, и функция для запрашивания значений по диапазонам дат, полу и стране. Также в программе имеется несколько статических полей и констант для данных.
Учтите, что индексация уже произошла в процессе сохранения. В этом участке мы проиндексировали пол, страну и дату рождения.
static class RedisAdaptor
{
const string REDIS_HOST = "127.0.0.1";
private static ConnectionMultiplexer _redis;
// Date of birth key:
const string REDIS_DOB_INDEX = "REDIS_DOB_INDEX";
// Gender keys:
const string REDIS_MALE_INDEX = "REDIS_MALE_INDEX";
const string REDIS_FEMALE_INDEX = "REDIS_FEMALE_INDEX";
// Country keys:
const string REDIS_C_IN_INDEX = "REDIS_C_IN_INDEX";
const string REDIS_C_USA_INDEX = "REDIS_C_USA_INDEX";
const string REDIS_C_GB_INDEX = "REDIS_C_GB_INDEX";
static RedisAdaptor()
{
// First, init the connection:
_redis = ConnectionMultiplexer.Connect(REDIS_HOST);
}
public static void StorePersonObject(Person person)
{
// We first JSONize the object so that it's easier to save:
string personJson = JsonConvert.SerializeObject(person);
//Console.WriteLine ("JSONized new Person object: {0}", personJson);
// And save it to Redis.
// First, get the database object:
IDatabase db = _redis.GetDatabase();
// Bear in mind that Redis is fundamentally a key-value store that does not provide
// indexes out of the box.
// We therefore work our way around this by creating and managing our own indexes.
// The first index that we have is for gender.
// We have two sets for this in Redis: one for males and the other for females.
if (person.Gender == Gender.MALE)
{
db.SetAdd(REDIS_MALE_INDEX, personJson);
}
else {
db.SetAdd(REDIS_FEMALE_INDEX, personJson);
}
// Next, we index by country.
if (person.Country == Country.INDIA)
{
db.SetAdd(REDIS_C_IN_INDEX, personJson);
}
else if (person.Country == Country.USA)
{
db.SetAdd(REDIS_C_USA_INDEX, personJson);
}
else if (person.Country == Country.GB)
{
db.SetAdd(REDIS_C_GB_INDEX, personJson);
}
// Next, we need to create an index to be able to retrieve values that are in a particular
// date range.
// Since we need to index by date, we use the sorted set structure in Redis. Sorted sets
// require a score (a real) to save a record. Therefore, in our case, we will use the
// DoB's `ticks' value as the score.
double dateTicks = (double)person.DoB.Ticks;
db.SortedSetAdd(REDIS_DOB_INDEX, personJson, dateTicks);
}
public static List RetrievePersonObjects(DateTime fromDate, DateTime toDate)
{
// First. let's convert the dates to tick values:
double fromTicks = fromDate.Ticks;
double toTicks = toDate.Ticks;
// And retrieve values from the sorted set.
// First, get the database object:
IDatabase db = _redis.GetDatabase();
RedisValue[] vals = db.SortedSetRangeByScore(REDIS_DOB_INDEX, fromTicks, toTicks);
List opList = new List();
foreach (RedisValue val in vals)
{
string personJson = val.ToString();
Person person = JsonConvert.DeserializeObject(personJson);
opList.Add(person);
}
return opList;
}
public static List RetrievePersonObjects(Gender gender)
{
// First, get the database object:
IDatabase db = _redis.GetDatabase();
string keyToUse = gender == Gender.MALE ? REDIS_MALE_INDEX : REDIS_FEMALE_INDEX;
RedisValue[] vals = db.SetMembers(keyToUse);
List opList = new List();
foreach (RedisValue val in vals)
{
string personJson = val.ToString();
Person person = JsonConvert.DeserializeObject(personJson);
opList.Add(person);
}
return opList;
}
public static List RetrievePersonObjects(Country country)
{
// First, get the database object:
IDatabase db = _redis.GetDatabase();
string keyToUse = REDIS_C_IN_INDEX;
if (country == Country.USA)
{
keyToUse = REDIS_C_USA_INDEX;
}
else if (country == Country.GB)
{
keyToUse = REDIS_C_GB_INDEX;
}
RedisValue[] vals = db.SetMembers(keyToUse);
List opList = new List();
foreach (RedisValue val in vals)
{
string personJson = val.ToString();
Person person = JsonConvert.DeserializeObject(personJson);
opList.Add(person);
}
return opList;
}
public static List RetrieveSelection(Gender gender, Country country)
{
// First, get the database object:
IDatabase db = _redis.GetDatabase();
string keyToUseGender = gender == Gender.MALE ? REDIS_MALE_INDEX : REDIS_FEMALE_INDEX;
string keyToUseCountry = REDIS_C_IN_INDEX;
if (country == Country.USA)
{
keyToUseCountry = REDIS_C_USA_INDEX;
}
else if (country == Country.GB)
{
keyToUseCountry = REDIS_C_GB_INDEX;
}
RedisKey[] keys = new RedisKey[] { keyToUseGender, keyToUseCountry };
RedisValue[] vals = db.SetCombine(SetOperation.Intersect, keys);
List opList = new List();
foreach (RedisValue val in vals)
{
string personJson = val.ToString();
Person person = JsonConvert.DeserializeObject(personJson);
opList.Add(person);
}
return opList;
}
}
Каждый ключ константы определяется в статическом классе, как, например, REDIS_DOB_INDEX, REDIS_MALE_INDEX, REDIS_FEMALE_INDEX и остальные, всё это – ключи к индивидуальным сетам в хранилище Redis.
Однажды, когда мы сохранили значения и создали индексы для сетов в Redis, мы сможем запрашивать их, используя различные версии перегруженных функций RetrievePersonObjects с параметрами – диапазоном дат, полом и страной.
Запрос по полу достаточно прост: основываясь на определении пола, мы «погружаемся» в один из двух гендерных сетов и получаем запрошенное значение. Точно та же процедура используется в отношении индекса стран.
Чтобы извлечь значения диапазона дат, используется метод SortedSetRangeByScore в объекте базы данных. Он принимает три аргумента: первый – имя сортированного сета, минимальные и максимальные значения.
Ещё одна интересная фича в работе с сетами Redis – великолепные семантические сеты. С их помощью можно определять два и более сетов, в результате чего БД Redis делает объединение, пересечение или определяет разность сетов. В последней секции кода посмотрите на функцию снизу – «RetrieveSelection», которая декларирует два параметра – пол и страна. Эта функция соответственно возвращает два параметра – пол и страну.
Источник: http://www.codeproject.com/Articles/1072137/Indexing-Columns-in-Redis
Створення панелі умінь. Частина 1. Робота з Unity UI.
Автор: Олексій Мухняк
Введение
Данной статей начинается серия публикаций о создании панели умений (skills panel) для вашей игры. Панели умений встречаются во многих RPG играх и не только.
По мере создания панели умений будет рассмотрено много вспомогательных компонентов, таких как работа с пользовательским интерфейсом или Unity UI (user interface); использование компонентов Event System, Grid Layout Group, Outline; настройка Canvas; использование интерфейсов IPointerDownHandler, IPointerEnterHandler, IPointerExitHandler; и многое другое. В результате получится вот такая игра:
Поскольку материал вышел довольно объёмным, то пришлось разделить его на части. Сегодня особое внимание уделим работе с Unity UI и настройкам Canvas.
Создание сцены и настройка Canvas:
Сначала нужно создать пустой 3d проект и добавить новую камеру для UI (GameObject > Camera), дадим ей имя UICamera. Изменим проекцию (projection) камеры на orthographic (размер объектов не зависит от расстояния). Выставим Clear Flags > Depth only, это даст прозрачную камеру без лишних фоновых добавлений. Поскольку UI должен обрисовываться поверх всего, то нужно изменить глубину камеры (Depth = 1).
Далее создадим Canvas (GameObject > UI > Canvas), вмести с элементом Canvas создастся Event System, он необходим для обработки событий, которые происходят на Canvas (более подробно рассмотрим позже). Что же такое Canvas и для чего он нужен. Canvas переводится как «Холст» и представляет собой абстрактное пространство для настройки и отрисовки UI. Все UI элементы должны быть потомками Canvas. Рекомендуется использовать следующие настройки (они позволят автоматически подстраиваться под расширение экрана, очень удобно для мобильных устройств):
Выбираем «Render Mod > Screen Space – Camera»UI. В поле «Render Camera» выбираем созданную нами UICamera. Далее остается изменить Canvas Scaler Sctipt, а именно поле «UI Scale Mode > Scale With Screen Size», что дает автоматическое изменение UI в зависимости от расширения экрана.
На этом настойки Canvas закончены.
Работа с Unity UI:
Работать с UI элементами удобно в 2d режиме (это не обязательно, но значительно упростит процесс разработки UI), переключаться между 2d и 3d режимами можно «Scence > 2D».
Все элементы UI содержат следующий компонент «Rect Transform» (в том числе и Canvas). Именно «Rect Transform» контролирует расположение UI относительно расширения экрана.
Рассмотрим возможности «Rect Transform» на простом примере. Сначала создадим кнопку (правой кнопкой мыши на элемент Canvas > UI > Button). Первое, что бросается в глаза - это синий круг и четыре треугольника в центре кнопки.
Четыре треугольника называют «Якорь», поскольку в зависимости от его расположения и настроек изменяют свое поведение UI элементы при изменении расширения экрана. Синий круг – это «центр» кнопки (или другого UI элемента), относительно него происходит вращение (Rotation) или масштабирование (Scale) элемента.
Место расположения «центра» кнопки хранится в специфическом формате и за него отвечает поле «Pivot». Где хранится расположение «центра» в соотношении (координаты «центра» x и y поделить на ширину и высоту соответственно) относительно нижнего левого угла. То есть соотношение сторон зеленого и красного квадратов на рисунке ниже.
Например, «Pivot X = 0.5, Y = 0.5» означает, что центр кнопки находится посередине кнопки. «Pivot X = 0, Y = 0» означает, что центр кнопки находится в левом нижнем углу.
Далее рассмотрим работу «Якоря», он может работать в 2-х режимах.
Режим 1 - это когда все четыре треугольника находятся в одной точке. Тогда компонент «Rect Transform» имеет следующие поля: Pos X, Pos Y, Pos Z, Width, Height. Как Вы уже догадались, Width, Height хранят значение ширины и высоты UI элемента (в нашем случае кнопки).
Pos X, Pos Y, Pos Z отвечают за расстояние между центром «Якоря» и «центром» UI элемента.
Возникает вопрос, а зачем нам нужен этот «Якорь». Ответ прост, при изменении размеров родительского UI элемента «Якорь» изменяет расстояние к «центру» текущего UI элемента пропорционально этим изменениям. «Якорь», как и «центр», хранит свои координаты в соотнесённом виде в поле Anchors.
Для режима 1 «Anchors > Min» и «Anchors > Max» совпадают. Очень важно запомнить, что в этом режиме изменяется только расстояние между «Якорем» и «центром», а поля Width, Height остаются низменными (поскольку Canvas настроен в режиме «Scale With Screen Size», то с изменением расширения экрана все UI элементы изменят свои размеры за счет автоматического изменения параметра Scale в самом Canvas).
Режим 2 - это когда четыре треугольника разделены. И первое, что бросается в глаза - это изменения наших полей Pos X, Pos Y, Pos Z, Width, Height на Left, Top, Pos Z, Right, Bottom соответственно. Теперь четыре треугольника образовывают мнимый квадрат и расстояние от стороны этого квадрата к нашему UI элементу хранится в полях на Left, Top, Right, Bottom (функция Pos Z осталась неизменяемой и отвечает за глубину объекта).
Изменяя значения этих полей, меняется размер UI элемента, а не «Якоря». За «Якорь» по-прежнему отвечает поле Anchors, но теперь «Anchors > Min» и «Anchors > Max» не всегда будут совпадать.
Теперь поле «Anchors > Min» отвечает за расстояние между Left и Bottom сторонами родительского и текущего UI элемента, а «Anchors > Max» - за расстояние между Top и Right сторонами.
Теперь при изменении родительского объекта наш UI элемент изменит свои размеры (поскольку Canvas настроен в режиме «Scale With Screen Size», то с изменением расширения экрана все UI элементы изменят свои размеры за счет автоматического изменения параметра Scale в самом Canvas, и это даст дополнительные изменения всем элементам в режиме 2).
Чтобы постоянно не таскать «Якорь» и «центр» вручную, разработчики Unity сделали несколько стандартных настроек. В них можно перейти, нажав на следующий элемент:
Откроется следующие окно, где можно задать расположение «Якоря».
Если хотите вместе с изменениями «Якоря» изменить положение «центра», то следует зажать клавишу «Shift» (появляется синий кружочек который и отвечает за «центр»).
Также есть возможность сразу передвинуть UI элемент на нужную позицию, для этого нужно зажать клавишу «Alt» (можно зажимать «Alt» и «Shift» одновременно).
Работа с Grid Layout Group.
Для начала создадим тестовую сцену, создадим Canvas и добавим Plane (таким же способом, как кнопку). Plane должен располагаться приблизительно следующим образом:
Grid Layout Group можно добавить с помощью «Add Component»:
Теперь все дочерние объекты Plane будут автоматически подстраиваться (изменять размеры и свое расположение) в зависимости от настроек Grid Layout Group. Для наглядного примера добавим 4 Image элемента дочерними к Plane.
Чтобы отличать Image, загрузим туда различные спрайты.
Image1, Image2, Image3, Image4 было присвоено картинки цифр 1, 2, 3, 4 соответственно. Теперь, когда знаем, где какой элемент, начнем изучение Grid Layout Group.
Padding отвечает за отступы дочерних элементов от сторон родительского объекта.
Как видно на рисунке, 4-й Image не влезает в выделенные границы, поэтому его было перенесено на новую строку. Поле Spacing отвечает за расстояние между дочерними объектами.
Поле Cell Size отвечает за размеры дочерних объектов.
Поле «Start Corner» отвечает за порядок размещения дочерних объектов.
Upper Left размещает дочерние объекты слева на право и сверху вниз.
Upper Right размещает дочерние объекты справа налево и сверху вниз.
Lower Left размещает дочерние объекты слева направо и снизу вверх.
Lower Right размещает дочерние объекты справа налево и снизу вверх.
Поле «Start Axis» указывает, в какую сторону будут начинать размещаться дочерние элементы (по умолчанию стоит «Horizontal»).
То есть если дочерние объекты не влезают в ширину родительского, то происходит переход на новую строку.
Для Vertical наоборот, если дочерние объекты не влезают в высоту родительского, то происходит переход на новую строку.
Поле «Child Alignment» отвечает за автоматическое выравнивание дочерних объектов. Данное поле не сложное и напоминает выравнивание текста в Word.
Остается последнее поле «Constraint», оно дает возможность создавать фиксированное количество строк или столбцов (по умолчанию стоит «Flexible», то есть нет фиксирования).
Как видите, элемент Grid Layout Group очень простой, но иногда может значительно упростить работу и UI.
Заключение.
В этой части было особое внимание уделено работе с новым Unity UI, он стал намного удобнее и качественнее. Теперь нет нужды пользоваться посторонними плагинами, нужно лишь немного разобраться и можно создать любой интерфейс, адаптирующийся под расширение экрана (очень полезно для мобильных устройств).
На этом первая часть статьи закончена, попробуйте поиграться с различными UI элементами (не забывайте изменять расширение экрана для экспериментов).
Изменить расширение экрана можно в окне «Games», как показано на рисунке ниже:
На этом все, всем удачи и до новых встреч.
Коротко про форми
Автор: Редакция ITVDN
Введение
Формы – не самая простая тема в изучении HTML. Перемещаться по странице с контентом – это одно, а вот заполнять поля формы – совсем другое. Вот почему хороша идея добавить несколько элементов к форме.
Labels
Все поля формы должны иметь уникальный слой. За это отвечает тег label, что в свою очередь связывается с элементом формы через атрибут for.
<form>
<label for="yourName">Your Namelabel>
<input name="yourName" id="yourName">
Плюсом использования labels также есть то, что они кликабельны, делая активным текущее поле формы. Атрибуты name и id обязательны: name нужен форме для управления полем, а id - для связи label с ним.
Группы элементов и их заголовки
Можно сгруппировать несколько однотипных полей имен (полное имя, фамилия и тд.) или адресов (Адрес1, адрес2, регион, страна, индекс и тд.), используя тег fieldset. Тегом legend определяется оглавление сгруппированных объектов.
<form action="somescript.php">
<fieldset>
<legend>Namelegend>
<p>First name
<input name="firstName">p>
<p>Last name
<input name="lastName">p>
fieldset>
<fieldset>
<legend>Addresslegend>
<p>Address
<textarea name="address">textarea>p>
<p>Postal code
<input name="postcode">p>
fieldset>
Большинство браузеров по умолчанию отображает группу элементов в рамке с названием сверху слева. Конечно, при желании все изменяется при помощи CSS.
Выбор варианта
Элемент optgroup объединяет в группу варианты выбора. Также нужен атрибут label. Браузеры автоматически сделают выпадающие списки из таких групп.
<select name="country">
<optgroup label="Africa">
<option value="gam">Gambiaoption>
<option value="mad">Madagascaroption>
<option value="nam">Namibiaoption>
optgroup>
<optgroup label="Europe">
<option value="fra">Franceoption>
<option value="rus">Russiaoption>
<option value="uk">UKoption>
optgroup>
<optgroup label="North America">
<option value="can">Canadaoption>
<option value="mex">Mexicooption>
<option value="usa">USAoption>
optgroup>
select>
Поля доступа
Поля форм (группы элементов) нуждаются в возможности доступа без использования указателей (мыши). На помощь приходят такие элементы, как tab stops и access keys. Атрибуты access key и tabindex добавляются в теги input и legend.
<input name="firstName" accesskey="f" tabindex="1">
Источник: http://www.htmldog.com/guides/html/advanced/forms/
Використання метатегів
Автор: Редакция ITVDN
Введение
Метатеги никак не влияют на отображение содержимого веб-страниц, но их активно используют поисковые машины для сбора информации про сайт. Тег meta можно записать внутри элемента head безмерное количество раз с такими атрибутами, как charset, name, http-equiv и content. При применении атрибута name или http-equiv content обязателен.
Names
Значения атрибута name разнообразные. Это и author, и description, и keywords. Author явно указывает на автора HTML- страницы, description обычно используется поисковыми машинами для отображение описания страницы в выдаче поиска.
Поисковики активно используют метатеги для определения некоего доверия веб-страницам. Вот почему так важно использовать теги meta. Так, например, keywords очень широко используется (иногда слишком). Хотя, современные поисковые машины генерируют релевантный контент самостоятельно.
HTTP эквиваленты
Атрибут http-equiv может быть использован вместо name и сгенерирует HTTP название, что будет отправлено на сервер. Атрибут должен редко применятся, его значения:
Content-type – оправляются зашифрованные данные;
Default-style – предпочитаются стили с указанных источников;
Refresh –определяется, как часто (в секундах) страница будет обновляться или пересылать на другую. Не очень разумно.
Очень удобно использовать запись
вместо аналогичного
для определения кодировки страницы.
<head>
<title>Air conditioners? YEAH conditioners!title>
<meta charset="utf-8">
<meta http-equiv="refresh" content="3">
<meta name="description" content="This is my really, really, REALLY exciting web page about air conditioners">
Источник: http://www.htmldog.com/guides/html/intermediate/metatags/
Використання форм у HTML
Автор: Редакция ITVDN
Введение
Формы используются для сбора информации, внесенной пользователем. Введенные данные взаимодейстуют с веб-приложениями, например, или когда нужно отправлять информацию в Интернет.
Формы сами по себе не очень полезные. Вместе с языком программирования их используют для обработки информации, введенной пользователем. Эти разнообразные скрипты нуждаются в других языках, отличающихся от HTML и CSS.
Теги form, input, textarea, select и option – базовые теги для форм в HTML.
Form
Тег form формирует такой себе «бланк». Если используется пользовательская форма для отправки данных, то нужно описать атрибут action для указания, куда контент будет отправлен.
Атрибут method указывает форме, как данные будут отправляться на сервер, также имеет дефолтное значение get, а также post, что фактически незаметно передает информацию о форме.
Get применяется для более коротких участков неконфиденциальной информации с сайта. Например, поиск будет отображаться в адресе страницы результатов поиска. Значение post - для более продолжительных, более защищенных материалов, таких как контактные формы, например.
Вот элемент формы будет выглядеть примерно так:
<form action="processingscript.php" method="post">
form>
Input
Тег Input - чуть ли не важнейшее в формах. Он может принимать огромное число значений, самые распространенные:
<input type=”text”> или просто <input> - стандартное текстовое поле. Также может иметь атрибут value, что превращает исходный текст в textbox.
<input type=”password”> - похожий на textbox, однако символы скрыты от пользователя.
<input type=”checkbox”> - кнопка с флажком, пользователь может задать режим вкл/выкл. Также может иметь атрибут checked ( <input type=”checkbox” checked> ), делает флажок «включенным».
<input type=”radio”> - похожий на checkbox, пользователь может выбрать только одну радиокнопку из группы. Также может иметь атрибут checked.
<input type=”submit”> - кнопка, что отправляет форму. Пользователь может изменять исходный текст формы через атрибут value, например
<input type="submit" value="Ooo. Look. Text on a button. Wow">
Обратите внимание на то, что тег input как и img, и br не имеет закрывающегося тега.
Textarea
Textarea – по сути, большое многострочное текстовое поле. Через атрибуты rows и cols задается число строк и столбцов соответственно, хотя можно управлять размером поля через CSS.
<textarea rows="5" cols="20">A big load of texttextarea>
Select
Тег Select в паре с option создает выпадающий список.
<select>
<option>Option 1option>
<option>Option 2option>
<option value="third option">Option 3option>
select>
Выбранное значение отправляется при подтверждении формы. Этим значением будет текст, заключенный в тег option, но будет отослано значение атрибута value, если он явно задан. Так, из примера выше, если выбран первый пункт, «Option 1» будет отправлено, если же третий -
Тег option может иметь атрибут selected, аналогично как checked для checkbox и радиокнопок. Например, <option selected>Rodentoption> будет изначально выбран вариант “Rodent”.
Names
Все вышеописанные теги будут красиво размещаться на странице, но, если подключить скрипт для обработки формы – все они будут проигнорированы. Так случится потому, что поля формы должны иметь уникальные имена. Так что нужно добавить атрибут name во все поля:
<input type="text" name="talkingsponge">
Пример формы:
<form action="contactus.php" method="post">
<p>Name:p>
<p>
<input type="text" name="name" value="Your name">p>
<p>Comments: p>
<p>
<textarea name="comments" rows="5" cols="20">Your commentstextarea>p>
<p>Are you:p>
<p>
<input type="radio" name="areyou" value="male">
Malep>
<p>
<input type="radio" name="areyou" value="female">
Femalep>
<p>
<input type="radio" name="areyou" value="hermaphrodite">
An hermaphroditep>
<p>
<input type="radio" name="areyou" value="asexual">
Asexualp>
<p>
<input type="submit">p>
form>
Источник: http://www.htmldog.com/guides/html/beginner/forms/
Фундамент внутрішньої оптимізації. 5 залізних факторів
Автор: Андрій Афанасьєв
Введение
В начале данной статьи хочу поздравить Вас и себя со своей первой публикацией в 2016 году. Искренне желаю встречать на своем пути побольше ценной и интересной информации, а также людей, которые будут делиться бесценным опытом и помогут таким образом в Вашем профессиональном росте:)
А теперь можно приступить непосредственно и к теме данной публикации. Я не сомневаюсь, что многие уже успели перелопатить массу книг и статей о том, какой должна быть качественная внутренняя оптимизация, на что она влияет, какие тренды нас ждут в наступившем году, поскольку такой информации в Интернете целые массивы. Но меня это не остановило. Я уверен, что данный материал обязательно откроет что-то новое, позволит расставить акценты и сосредоточить внимание на самом главном. Тем более, кто дочитает эту статью до конца, получит небольшой, но приятный бонус:)
От слов к делу
Самый встречаемый вопрос, особенно среди новичков, звучит так: ”Внутренняя оптимизация - такой трудоемкий процесс, так много факторов и пунктов проверки. Подскажи, на что стоит обращать внимание в первую очередь?”. Отвечаю. Пожалуй, на всё. Наша жизнь состоит из мелочей и SEO-оптимизация тому не исключение. Практически невозможно сложить красивую картинку из пазла на 100 или даже 1000 элементов, если упустить часть деталей.
Поэтому остается только сосредоточиться, сесть за компьютер, поставить возле себя чашку ароматного кофе или чая, открывать чек-лист, кропотливо и внимательно анализировать проект по каждому из пунктов. В результате Вы получите полноценный труд в виде технического задания, которое, с большой вероятностью, Вам самим понравится и после внедрения обеспечит эффективное дальнейшее SEO-продвижение.
Но, несмотря на важность всех мелочей и нюансов, я подготовил материал, в котором выделю 5 моментов критической важности, то, без чего достигнуть крепких ТОПов и заполучить целевой трафик невозможно, с кратким комментарием по их решению. Плюс все будет лаконично и конструктивно, ведь вычитать «простынь» под силу не каждому :)
Наших 5 железных факторов
1. Семантика&Структура
Моментом №1 в SEO анализе и оптимизации является тот, насколько структура сайта позволяет его продвигать. Довольно часто возникает такая ситуация, что клиент хочет или требует оптимизацию сайта под семантику, к которой сайт совершенно не готов структурно. На сайте может не быть нужной вложенности, категорий и подкатегорий, страниц отзывов и прайсов, без которых эффективной оптимизации под интересную для клиента семантику не получится.
Поэтому, крайне важно параллельно выбирать и анализировать семантику, а также давать рекомендации и правки по структуре сайта. Семантика + структура - очень серьезная связка. Лучше согласовать и внедрить все моменты на берегу, чем когда Вы уже выйдете в открытое плаванье и придется грести веслами обратно. Но также важно помнить, что не любая семантика, которую предлагает или на которой настаивает клиент является правильной. В силу незнания он может быть убежден в одном, но в реальности дела могут обстоять иначе. Нивелируйте это своим погружением в бизнес и опытом.
2. Настройка карты сайта, ЧПУ и хлебных крошек.
Если на предыдущем этапе все ОК, двигаемся дальше. Думаю, каждому из нас хоть раз приходилось заблудиться на улице. В этом случае так хочется у первого встречного прохожого здесь и сейчас получить толковую консультацию, как попасть на улицу, которую мы ищем. А если мы еще и торопимся на жизненно важную встречу, то градус ситуации накален вдвойне. Шучу)
К чему все это? Правильная навигация важна и для поисковых роботов. Он не будет тратить время на то, чтобы сканировать страницы с непонятным назначением, уровнем вложенности и неизвестной глубины. Ему необходимо видеть эту структуру и навигацию. Поэтому на сайте обязательно должны быть настроены:
Человекопонятные url-адреса ЧПУ
Все url-адреса на сайте должны быть приведены к ЧПУ-виду и иметь вид http://url/. Кириллица должна трансформироваться в латиницу. В качестве разделителя слов в ЧПУ следует использовать дефис "-". Также будет неоспоримым плюсом, если в url-адресах будет соблюдаться полная вложенность, т.е. http://url/>/.
Пример не ЧПУ адреса может выглядеть так: site.domain/index.php?id=75. Примером ЧПУ адреса для этой же страницы может быть site.domain/itvdn.
После настройки ЧПУ следует также сделать 301 редирект с не-ЧПУ урлов на ЧПУ. В нашем случае это 301 редирект с site.domain/index.php?id=75 на site.domain/itvdn.
Настройка ЧПУшек и соответствующих редиректов, как правило, решается подключением стандартных плагинов. Если речь идет о самописной системе, скорее всего нужно будет «допиливать» соответствующий функционал.
Реализация «хлебных крошек» Breadcrumbs
Под «хлебными крошками» подразумевается навигационная цепочка, которая отражает вложенность структуры и чаще всего располагается над или под основным текстовым заголовком страницы.
Реализация Breadcrumbs полезна не только для того, чтобы показать поисковику глубину структуры сайта, но и с точки зрения юзабилити. Посетителю будет всегда удобно выйти на уровень или два уровня выше, допустим в каталог, если со страницей товара он полностью ознакомился, но хочет посмотреть что-то еще.
Карта сайта sitemap.xml
Как правило, данные карты генерируются стандартными плагинами в зависимости от того, на какой CMS сделан сайт. Либо есть универсальные решения, например, специальная CMS для генерации карт, которая интегрируется в корень сайта и парсит всю его структуру, в результате чего и генерируется карта сайта. Адрес, по которому должна быть доступна карта, желательно делать site.domain/sitemap.xml. Но и это еще не все. Необходимо после генерации карты скормить ее поисковому роботу в GWT в разделе «Файлы Sitemap».
Если с содержанием карты будет что-то не в порядке, Вы увидите соответствующие предупреждения об этом, которые потребуется исправить. Хочу еще подчеркнуть, что генерация карты сайта происходит в самом конце внедрения ТЗ оп оптимизации, когда устранены дубли, настроены правильные ЧПУ и т.д.
3. Диагностика и устранение дублей.
Дубли – это откровенное зло. Было бы Вам приятно, чтобы кто-то копировал Ваше поведение, одевался также, носил идентичную стрижку? Думаю, что вряд ли. И поисковые роботы в этом молодцы. Они такое рубят, занижают рейтинг целевых страниц и сайта в целом, если в его содержимом содержатся дубли. Каким образом их диагностировать? Для этого есть несколько методов. Самый простой - это воспользоваться данными по повторяющимся метатегам в разделе «Оптимизация HTML». Если дубли присутствуют, Вы увидите данные в таком виде:
Диагностировать дубли можно и с помощью программы NetpeakSpider. Такой вариант даже лучше в том плане, что он сканирует актуальную структуру сайта, а страницы в GWT сканируются периодически и информация может быть не первой свежести на момент, когда Вы ищите дубли.
Дальше сам собой напрашивается вопрос о том, как нам избавиться от них. Если дубль полный, т.е является 100% клоном страницы, которую дублирует, стоит проставить 301 редирект с дубля на основную страницу и отправить данный дубль на принудительную переиндексацию в разделе «Просмотреть как Googlebot».
Существуют и другие методы борьбы с полными дубликатами, например, закрытие их в robots.txt и принудительное удаление через GWT. Но 301 редиректы считаются наиболее эффективным и правильным методом.
В случае частичных дублей, например, когда дублируется только
и/или
и/или заголовки
необходимо переписывать метатеги на дублях вручную, если их много, либо формировать и внедрять шаблоны метатегов, которые в результате генерации будут уникальными для каждой страницы. Пример шаблона автогенерации метатегов может выглядеть, например, так:
После активной борьбы по искоренению дублей Вы в идеале сорвете джек-пот в таком виде:
Резюмируя данный пункт, предлагаю раз и навсегда сказать «Нет!» дублям, вовремя проверять их и давать отпор! Едем дальше…
4. Борьба с аффилиатами
Под аффилиатами подразумевается другой сайт клиента с такими же контактными данными, названием компании, структурой и тематикой. Поисковые системы “за естественную конкуренцию” и ведут борьбу с монополизацией рынка. Поэтому, склеивают аффилиаты, накладывая фильтры и занижая рейтинг сразу всех сайтов.
По статистике 2 из 3 клиентов приходят к нам с аффилиатами. В их качестве зачастую выступают не самостоятельные сайты на отдельных CMS и доменах, а площадки, сгенерированные на платформах типа Prom.ua или Allbiz.
Это плохая практика и что в таком случае требуется делать? Алгоритм простой:
Аргументируем клиенту всю ситуацию, запрашиваем доступы к аффилиатам, проверяем их.
Добавляем каждый аффилиат в Google Webmaster Tools.
Настраиваем 301 редиректы со всех страниц аффилиатов на главную страницу основного сайта.
Отправляем на принудительную переиндексацию аффилиаты.
Если нет возможности проставить 301 редирект, нужно воспользоваться опцией в GWT в разделе «Удалить URL-адреса».
Если внутри платформы нет возможности добавить сайт в панель вебмастеров (а такое случается часто), нужно просто согласовывать полное удаление сайта-аффилиата и ждать, когда он выпадет из индекса.
Такой простой алгоритм действий позволил нам вывести не один сайт из-под фильтра. Вот один из кейсов нашей компании, где мы непосредственно применяли данную методику и вытащили сайт с самого дна поиска на первые позиции.
Также бытует мнение, что достаточно изменить название компании, контактную информацию - и ситуация может измениться. Этот метод не работает. Не тратьте даже время на покупку новых телефонов и не ломайте голову, как бы себя по-новому еще назвать.
5. On-Page оптимизация
Подразумевает заточку и оптимизацию конкретных целевых страниц под семантическое ядро. Здесь важны следующие моменты:
Написание и размещение оптимизированных метатегов.
Подготовка и размещение полезного структурированного качественного текстового контента, при этом оптимизированного под нужные поисковые запросы.
Помимо контента текстового следует использовать качественные (желательно “не тяжелые” по объему памяти) изображения на всю ширину страницы. Это еще в тренде. Не помешают также тематические видеоролики, которые точно повлияют на поведенческие факторы ПФ (снизят показатели отказов и увеличат время нахождения пользователя на странице).
И многие другие интересные вещи, которые Вы узнаете дальше.
На десерт
Как я и обещал в начале данной статьи, Вас ожидает сюрприз. Под этой статьей находится pdf-файл «Полный чек-лист seo-оптимизатора от Abweb».
Запомните, что только титаническая работа позволит получить что-то стоящее не только в сфере SEO. Жду Ваших комментариев, до следующих публикаций!
Полный чек-лист seo-оптимизатора от Abweb
Робота з посиланнями у HTML & CSS
Автор: Редакция ITVDN
Введение
Буквы «H» и «T» в слове «HTML» означают «hypertext» – по сути, систему связанного текста.
DOCTYPE html>
<html>
<head>
<title>My first web pagetitle>
head>
<body>
<h1>My first web pageh1>
<h2>What this ish2>
<p>A simple page put together using HTMLp>
<h2>Why this ish2>
<p>To learn HTMLp>
<h2>Where to find the tutorialh2>
<p><a href="http://www.htmldog.com">HTML Doga>p>
body>
html>
Атрибут href в тега <a> определяет адрес ссылки.
Так если, например, Вы имеете другой файл "flyingmoss.html" в той же директории вашего проекта, то код будет следующим:
<a href="flyingmoss.html">The miracle of moss in flighta>
С помощью ссылок также можно отправить пользователю часть той же страницы. Необходимо добавить атрибут id, например,
<h2 id="moss">Mossh2>
и потом нужная ссылка будет такой:
<a href="#moss">Go to mossa>
Переходя по ней, пользователь перейдет на место в странице с указанным ID.
Источник: http://www.htmldog.com/guides/html/beginner/links/
Робота з Touch в Unity3D
Автор: Олег Загородній
Введение
Ни для кого не секрет, что в мобильных играх, в отличие от компьютерных, практически единственным “устройством ввода” является палец. То есть, все действия, которые пользователь выполняет в игре, совершаются благодаря прикосновениям к экрану, или же тачам (англ. touch – прикосновение). В этой статье мы с Вами рассмотрим, как можно правильно обработать тачи, разберем, в чем разница между глобальными и локальными тачами, а также реализуем обработку некоторых популярных жестов, которыми Вы оперируете не только в играх, но и в повседневном пользовании смартфоном – swipe и zoom. Разумеется, все это мы будем делать, используя исключительно встроенный функционал Unity3D, без внешних плагинов и ассетов.
Наведем справки
Перед тем, как начать, рассмотрим, какие возможности нам предоставляет библиотека для работы с тачами. В документации Unity видим, что разработчики движка рекомендуют использовать класс Input для получения доступа к данным об акселерометре и мульти-таче мобильного устройства. Это нас вполне устраивает.
В обязательном порядке необходимо подключить пространство имен UnityEngine.EventSystems, ведь именно оттуда родом большинство интерфейсов и классов, которые нам сегодня понадобятся. Например, IPointerClickHandler, IDragHandler и многие другие. В конце концов, классы BaseEventData и PointerEventData, из которых мы будем доставать все необходимые данные о событиях, проживают по тому же адресу.
Что ж, не стоит волноваться, если Вы видите эти имена впервые. Моя задача - сдружить вас и наставить на путь плодотворной разработки.
Если у Вас уже имеется опыт работы с данными классами, надеюсь, смогу поведать о каких-либо интересных спецификах работы с ними и еще некоторыми компонентами.
Ближе к делу или “Что такое глобальные и локальные тачи?”
Немного теории.
Чтобы правильно реализовать все, что мы задумали, сначала разберемся, что такое глобальные и локальные тачи. Если вкратце, то глобальные тачи – это прикосновения к экрану устройства в любой точке. То есть, мы будем говорить, что необходимо обработать глобальный тач, если для игрового процесса не важно, где именно игрок ткнет пальцем в экран. Думаю, все видели в играх заставку после загрузки уровня с большими буквами “Tap to start” либо что-то в этом роде. Бывают настолько простые игры, что, по сути, все управление игроком производится исключительно такими вот глобальными тачами. Например, в Flappy Bird, 2 Cars и многих других.
Разумеется, не всегда все так просто. Случается, нам необходимо обработать тач в определенной области экрана, или по какой-либо кнопке, или по объекту. Такие тачи мы с Вами будет именовать локальными, так как они должны производиться в некой локальной области. Причем принципы реализации обработки тачей по 2D объектам или же элементам UI и обработки тачей по 3D объектам на сцене немного отличаются. Эти нюансы мы также рассмотрим.
То есть перед тем, как перейти к воплощению идеи в жизнь, хорошенько подумайте, где и какой вид тача будет использоваться, ведь от этого зависит, как далеко в лес мы пойдем и как много дровишек нарубим.
Практика.
Подготовим рабочее место, как на картинке. Также создадим новый скрипт HandleScript и прикрепим его на куб.
Как же считать прикосновение к экрану? Если в случае с кликами мыши в классе Input есть методы GetMouseButton (...Up, ...Down), то для тачей соответствующие методы отсутствуют. Здесь все даже проще. Разработчики предоставляют свойство touchCount (только для чтения), в котором хранится количество тачей в текущем кадре. То есть, чтобы считать глобальный тач, нам необходима всего одна строчка в методе Update:
using UnityEngine;
using UnityEngine.EventSystems;
public class HandleScript : MonoBehaviour
{
void Update ()
{
if (Input.touchCount > 0) Debug.Log("Global touch!");
}
}
Как только пользователь задумает ткнуть пальцем в экран (чего мы, по сути, и ждем), выражение в блоке условия вернет true и на консоль вылетит наше сообщение. Что может быть проще?
Важно: проверить данный способ обработки на компьютере не получится, так как в свойство touchCount записывается именно кол-во тачей. Клики мыши не в счет.
Примечание: К примеру, при портировании на Windows Phone сообщения консоли, разумеется, отображаться не будут. Так что стоит реакцию на тач сделать более явной. Допустим:
if (Input.touchCount > 0) transform.localScale *= 1.1f;
Я специально буду обращать Ваше внимание на многие нюансы (для кого-то известные и очевидные, а для кого-то - нет), чтобы избавить от мелких и неприятных ошибок и сберечь парочку нервных клеток.
Попробуйте сбилдить на свой смартфон и немного потапать в разных частях экрана.
Что касается локальных тачей, то здесь есть несколько вариантов обработки. Также они зависят и от типа объекта – 2D или 3D.
Начнем, пожалуй, с 2D объектов. Давайте добавим на сцену какой-либо спрайт и сразу прилепим ему компонент 2D Box Collider (о нем чуть ниже).
Еще добавим к этому спрайту наш скрипт HandleScript, но немного его подкорректируем. В Unity есть перечень методов, которые являются обработчиками определенных событий. Например, метод OnCollisionEnter вызывается, когда два твердых (Rigidbody) объекта соприкасаются, если вкратце. Так вот, среди вышеупомянутого перечня методов есть такой себе OnMouseDown метод. Он вызывается, как вы уже, наверное, догадались, в момент нажатия левой кнопки мыши непосредственно на объекте.
Здесь есть три важных момента:
Метод вызывается непосредственно в момент нажатия кнопки, а не отпускания или полного клика.
Метод реагирует только на левую кнопку мыши.
Срабатывает только при клике непосредственно на объект.
Так как мы с Вами обрабатываем тачи, а не клики, то справедливы для нас будут только первый и третий пункты. То есть работу с человеческими пальцами данный метод тоже поддерживает. А может, и не только человеческими...
Как же будет выглядеть наш код?
using UnityEngine;
using UnityEngine.EventSystems;
public class HandleScript : MonoBehaviour
{
void OnMouseDown()
{
transform.localScale *= 1.1f;
}
}
Обратите внимание на сигнатуру метода и запомните. Ведь если сделать малейшую опечатку, событие касания/клика обрабатываться не будет. Проверить работу этого способа уже проще. Учитывая, что метод OnMouseDown реагирует как на мышь, так и на пальцы, билдить проект на смартфон не обязательно. Из этой серии есть еще метод OnMouseUp, который вызывается при отпускании пальца/кнопки.
Важно: все события, связанные с кликами, движок Unity считывает, неявно используя Raycast’ы (лучи). Именно поэтому мы добавили на наш спрайт компонент 2D Box Collider. Если Вы еще не знаете, в чем суть работы Raycast’ов, обязательно почитайте в документации Unity.
Важно: получается, указанные методы срабатывают при тапе/клике именно по коллайдеру объекта, а не по мэшу(Mesh)/спрайту.
Попробуйте сбилдить и потапать на спрайт ящика либо зеленого куба.
Теперь отключите их коллайдеры и проверьте, обрабатывается ли событие. Как видите, этот способ позволяет обработать тап как по 2D, так и по 3D объекту. Просто?
Следующий способ обработать локальный тач тоже работает для обоих типов объектов, но немного отличается конфигурацией компонентов. Здесь мы с Вами будем уже работать с таким пространством имен, как UnityEngine.EventSystems. Если оно у Вас еще не подключено, самое время это сделать. Там, как я уже говорил, находятся необходимые нам интерфейсы и классы. Теперь обязательно добавьте на сцену объект EventSystem. Он находится во вкладке UI контекстного меню создания объекта.
Итак, чтобы считать тап по 2D объекту (не элементу UI), необходимо обязательно прикрепить к камере компонент Physics 2D Raycaster. Для данного компонента обязательным является присутствие компонента Camera. Поэтому совсем неудивительно, что мы цепляем его именно на объект Main Camera.
То есть после всех наших манипуляций игровая сцена должна выглядеть, как на картинке.
Теперь, пожалуй, в код. Данный метод более универсальный, чем предыдущий, так как позволяет получить множество информации о состоянии курсора. Неважно, кто им управляет – палец или мышь.
Нам понадобится интерфейс IPointerDownHandler из подключенного пространства имен. После реализации единственного его метода, получаем код, не менее простой, чем раньше.
using UnityEngine;
using UnityEngine.EventSystems;
public class HandleScript : MonoBehaviour, IPointerDownHandler
{
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log(eventData.position);
}
}
Разумеется, ошибиться с сигнатурой метода у вас не выйдет, так как Visual Studio сразу ругнется за нереализованный интерфейс. А в параметре eventData типа PointerEventData будет храниться вся информация о курсоре на момент срабатывания метода, а это очень полезно.
Что же будет происходить? Здесь тоже все просто. В момент касания движок Unity пустит луч в сцену и в случае, когда тот пройдет сквозь коллайдер нашего спрайта, сработает метод обработчик OnPointerDown и в параметр eventData запишется вся информация о курсоре. Для считывания тачей также есть следующие интерфейсы:
IPointerUpHandler, IPointerClickHandler, IPointerEnterHandler и IPointerExitHandler.
Мне кажется, по их именам предельно ясно, какой из них какое событие позволяет обработать. Все, что Вам необходимо – наследоваться от нужного интерфейса, реализовать единственный его абстрактный метод и удивиться, как просто это работает. Главное, когда будете работать, не забудьте о компоненте Physics 2D Raycaster и объекте EventSystem, которые упоминались выше.
Как вы уже, наверное, заметили, на 3D объекты данный способ не распространяется. Как это исправить? Элементарно. На объект-камеру необходимо также прикрепить компонент Physics Raycaser. Вот и все. Остальная суть остается та же.
Попробуйте запустить проект. Заметили? Движок реагирует на клики мыши тоже. Теперь портируйте на ваш смартфон и удостоверьтесь, что все работает верно.
Чтобы обработать тачи по элементам UI, Вам необходим будет компонент Graphic Raycaster. Но для него обязательным является компонент Canvas. Это, думаю, тоже вполне логично. Если прикрепить его на объект Canvas, то методы рассмотренных нами интерфейсов позволят также обработать тачи по кнопкам, панелям, тогглам и т.д.
Итог по разделу о глобальных и локальных тачах.
Давайте немного подсуммируем все, что только что было рассмотрено.
Все тачи и клики обрабатываются неявным пусканием лучей из экрана в сцену. То есть определяется касание к коллайдерам объектов.
При использовании интерфейсов из пространства имен UnityEngine.EventSystems обязательно надо добавить объект EventSystem.
Physics 2D Raycaster – компонент для обработки тачей/кликов по 2D объектам.
Physics Raycaster – компонент для обработки тачей/кликов по 3D объектам.
Graphic Raycaster – компонент для обработки тачей/кликов по элементам UI. В отличие от предыдущих присутствует на объекте Canvas по умолчанию.
Обработка Swipe жестов
Как же выловить эти Swipe жесты и как определить их направление? На самом деле, это совсем несложно. Есть несколько интерфейсов, которые позволят нам это сделать:
IDragHandler, IBeginDragHandler, IEndDragHandler.
Причем, они отлично подходят как для работы с Drag, так и Swipe жестами. Давайте почистим нашу сцену и приведем ее примерно вот к такому виду:
У нас на объекте Canvas есть красная панель размером на весь экран и внутри нее имеется еще одна небольшая панель. Мы с Вами будем считывать Swipe’ы по красной панели и в зависимости от их направления двигать зеленую. Все до безобразия просто. Аналогичные действия Вы потом сможете проделать не только с элементами UI, как в этом примере, но и с 2D и 3D объектами. Сейчас же будем использовать панели, так как это получится более наглядно.
Скрипт HandlerScript прикрепляем к внешней (красной) панели.
using UnityEngine;
using UnityEngine.EventSystems;
public class HandleScript : MonoBehaviour, IDragHandler, IBeginDragHandler
{
public void OnBeginDrag(PointerEventData eventData)
{
}
public void OnDrag(PointerEventData eventData)
{
}
}
Вот как изначально должен выглядеть наш скрипт. Мы должны наследоваться он интерфейсов IDragHandler и IBeginDragHandler. Этого будет достаточно, чтобы считать Swipe. Скажу даже больше. Мы будем использовать только метод из второго интерфейса.
Важно: необходимо обязательно реализовать интерфейс IDragHandler (пусть даже методом с пустым телом), чтобы срабатывали методы из интерфейсов IBeginDragHandler и IEndDragHandler.
Дабы определить направление Swipe’а, мы будет использовать свойство delta в параметре eventData метода OnBeginDrag. В это свойство записывается разница позиций курсора между текущим и предыдущим кадрами. Мы просто напросто в момент начала Swipe’а глянем, какое значение этой дельты, и из этого уже определим, какое направление жеста.
Возможно, у Вас возник вопрос. Откуда возьмется эта дельта, если метод OnBeginDrag сработает сразу, как только игрок начнет вести пальцем по экрану? Дело вот в чем. Этот метод вызывается только после того, как игрок сдвинет палец на какое-то пороговое значение расстояния от начальной точки. За это время успевает накопиться некоторая информация о происшедшем событии. То есть за этот небольшой промежуток времени мы можем определить, куда пользователь собирается вести свой палец.
Мы всего лишь должны построить правильную условную конструкцию для определения направления и, исходя из условия, двигать зеленую панель в соответствующем направлении. В будущем это может быть ваш игрок в раннере или что-либо иное...
using UnityEngine;
using UnityEngine.EventSystems;
public class HandleScript : MonoBehaviour, IDragHandler, IBeginDragHandler
{
Transform green; // здесь будет ссылка на компонент Transform зеленой панели.
void Start()
{
green = transform.GetChild(0); // получаем ссылку на Transform зеленой панели.
}
public void OnBeginDrag(PointerEventData eventData)
{
if (Mathf.Abs(eventData.delta.x) > Mathf.Abs(eventData.delta.y))
{
if (eventData.delta.x > 0) Debug.Log("Right");
else Debug.Log("Left");
green.position += new Vector3(eventData.delta.x, 0, 0);
}
else
{
if (eventData.delta.y > 0) Debug.Log("Up");
else Debug.Log("Down");
green.position += new Vector3(0, eventData.delta.y, 0);
}
}
public void OnDrag(PointerEventData eventData)
{
}
}
Что мы сделали? Сначала проверили, дельта по какой оси больше – X или Y? Если по оси X, значит движение будет по горизонтали, если же по Y – значит, по вертикали. А там еще раз проверили направление. Как видите, это делается элементарно просто, а работает безотказно. Обязательно проверьте, обрабатывается ли жест Swipe у вас на смартфоне.
Если же Вам необходимо получить непосредственно угол Swipe’а, можно использовать формулу , которая позволяет вычислить синус угла прямоугольного треугольника из отношения противоположной и прилегающей сторон.
Обработка Zoom’а
Вернем сцену к первоначальному состоянию. Чтобы считать жест zoom’а, нам необходимо обработать глобальные тачи, поэтому наш скрипт HandleScript можно прикрепить даже на камеру.
А код наш будет выглядеть вот так:
using UnityEngine;
public class HandleScript : MonoBehaviour
{
public float sensitivity;
Vector2 f0start;
Vector2 f1start;
void Update()
{
if (Input.touchCount < 2)
{
f0start = Vector2.zero;
f1start = Vector2.zero;
}
if (Input.touchCount == 2) Zoom();
}
void Zoom()
{
if (f0start == Vector2.zero && f1start == Vector2.zero)
{
f0start = Input.GetTouch(0).position;
f1start = Input.GetTouch(1).position;
}
Vector2 f0position = Input.GetTouch(0).position;
Vector2 f1position = Input.GetTouch(1).position;
float dir = Mathf.Sign(Vector2.Distance(f1start, f0start) - Vector2.Distance(f0position, f1position));
transform.position = Vector3.MoveTowards(transform.position, transform.position + transform.forward, dir * sensitivity * Time.deltaTime * Vector3.Distance(f0position, f1position));
}
}
В чем суть его работы? Если пользователь прикасается к экрану двумя пальцами, координаты двух тачей записываются в соответствующие поля f0start и f1start. Именно с ними будут сравниваться все остальные позиции тачей, пока игрок не уберет от экрана пальцы. То есть мы просто сравниваем расстояние между двумя тачами. Если текущее расстояние меньше, чем начальное, то камера будет двигаться назад, если же больше – вперед.
Условная конструкция в методе Zoom для того, чтобы позиции тачей записались в свойства только один раз в момент касания к экрану.
Чувствительность zoom’а вы можете регулировать значением поля sensitivity прямо в окне Inspector. Попробуйте усовершенствовать этот скрипт сами. Допустим, сделать, чтобы сравнивались позиции не текущих и начальных тачей, а позиции тачей в этом и предыдущем кадре. Сравните результат.
Заключение
Как видите, обработка тачей и рассмотренных нами жестов реализуется очень просто. Все, что Вам необходимо – внимательность и фантазия. Если что-то не работает, обязательно проверьте, присутствуют ли необходимые компоненты на всех объектах, вызываются ли нужные методы и так далее.
Совсем не обязательно искать внешние библиотеки, плагины и ассеты для того, чтобы работать с тачами в Unity. Разработчики предоставляют, по сути, всё, что вам может понадобится. Результат же зависит от Вашей фантазии.
Не забывайте заглядывать в документацию Unity, там есть много всего интересного. Хотя, среди того, что мы сегодня рассмотрели, есть вещи, о которых там не упоминают.
Благодарю за внимание и желаю всем творческих успехов!
Робота з текстом: Адреси, Визначення, 2-спрямованість та редагування
Автор: Редакция ITVDN
Введение
Должно быть, Вы уже знаете, что существует огромное количество текстовых тегов. Вы знакомы с параграфами, заголовками и даже аббревиатурами. Но существует множество других, более непонятных и сложных тегов. Непонятных, потому что вы вряд ли найдете их в интернете, и не потому что они не нужны. Если Вы найдете текст с объяснениями, то используйте новые для себя элементы для создания более качественного, лучшего, более значимой HTML-страницы.
Адреса
Тег address используется специально для контактных данных или всей веб-страницы (единожды). Это не значит, что address нужно обязательно применять для всех старых адресов.
<h3>Author contact detailsh3>
<address>
<ul>
<li>0123-456-7890li>
<li>author_dude@noplaceinteresting.comli>
<li>http://www.noplaceinteresting.com/contactme/li>
ul>
address>
Определения
Тег dfn используется для выделения первого термина. Как и тег abbr, атрибут title может быть использован для описания термина.
<p>Bob's <dfn title="Dog">caninedfn> mother and <dfn title="Horse">equinedfn> father sat him down and carefully explained that he was an <dfn title="A mutation that combines two or more sets of chromosomes from different species">allopolyploiddfn> organism.p>
Двунаправленный текст
Тег bdo может использовать обратное направление текста, следовательно, может отображать языки, что читаются справа налево. Атрибут dir может принимать значения ltr (слева направо) или rtl (справа налево).
<bdo dir="rtl">god lmthbdo>
Редакции
Теги ins и del используются для отображения соответственно вставленных и удаленных частей. Они не имеют ограничений применения в тексте, также могут быть применены для целых разделов контента и, как правило, используются в «Track Changes» - дословно «отслеживание изменений».
Теги редакции также имеют атрибуты datetime, чтобы показать, когда была произведена правка, а также cite – причины изменений.
<p>I have decided to <del datetime="2013-01-26">decreasedel><ins cite="http://www.icecreamforall.com/changeofpolicy/">increaseins> the amount of free ice cream that the State will provide for its citizens.p>
Как и в обычных текстовых редакторах, элемент ins применяют для подчеркивания текста, а для зачеркивания – del.
Источник: http://www.htmldog.com/guides/html/intermediate/text2/