Пользовательский рейтинг игрового контента

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


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

Добавим в проект скрипты:

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

public class RatingStarsComponent : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler {

    [HideInInspector] public Image image;
    [HideInInspector] public int id;
    [HideInInspector] public RatingStars rating;

    void IPointerClickHandler.OnPointerClick(PointerEventData eventData)
    {
        rating.StarClick(id);
    }

    void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
    {
        rating.StarEnter(id);
    }

    void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
    {
        rating.StarExit();
    }
}
Это вспомогательный класс, который нужен чтобы "ловить" действия мышкой.

Еще один скрипт:

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(RatingStars))]
public class RatingStarsEditor : Editor {

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();
        RatingStars t = (RatingStars)target;
        if (GUILayout.Button("Создать / Обновить")) t.Create();
    }
}
#endif
Здесь мы добавляем кнопку в инспекторе редактора.

Подготовка: добавляем на сцену Canvas, затем создаем дочерний пустой объект Rect Transform.

Теперь на пустой объект вешаем скрипт:

using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;

[RequireComponent(typeof(Image))]

public class RatingStars : MonoBehaviour, IPointerExitHandler {

    public Sprite sprite; // спрайт звезды
    public int count = 5; // сколько звезд
    public float size = 50; // размер
    public float offset = 10; // расстояние между звездами
    public Color background = Color.clear; // цвет фона
    public Color starDefault = Color.white; // цвет звезды по умолчанию
    public Color starActive = Color.yellow; // цвет звезды, когда она выбрана
    [HideInInspector] public RatingStarsComponent[] components;
    private bool isClick;

    void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
    {
        StarExit();
    }

    public void StarEnter(int id) // подсветка курсором
    {
        if (isClick) return;

        foreach (RatingStarsComponent comp in components)
        {
            comp.image.color = starDefault;
        }

        for (int i = 0; i < id; i++)
        {
            components[i].image.color = starActive;
        }
    }

    public void StarExit() // проверка на активность
    {
        if (isClick) return;

        RatingStars rating = PointerRaycast<RatingStars>(Input.mousePosition, true);

        if (rating == null)
        {
            foreach (RatingStarsComponent comp in components)
            {
                comp.image.color = starDefault;
            }
        }
    }

    public void StarClick(int id) // выбор оценки
    {
        if (isClick) return;
        isClick = true;

        Debug.Log(this + " --> оценка: " + id + " из " + components.Length);
    }

    private T PointerRaycast<T>(Vector2 position, bool IsRaycastAll) where T : Object // UI рейкаст
    {
        PointerEventData pointerData = new PointerEventData(EventSystem.current);
        List<RaycastResult> resultsData = new List<RaycastResult>();
        pointerData.position = position;
        EventSystem.current.RaycastAll(pointerData, resultsData);
        T target = null;

        if (resultsData.Count > 0)
        {
            if (IsRaycastAll)
            {
                for (int i = 0; i < resultsData.Count; i++)
                {
                    T t = resultsData[i].gameObject.GetComponent<T>();
                    if (t) return t;
                }
            }
            else
            {
                return resultsData[0].gameObject.GetComponent<T>();
            }
        }
        

        return target;
    }

    public void Create() // создание рейтинга
    {
        if (sprite == null) return;
        RectTransform samlpe = new GameObject().AddComponent<RectTransform>();
        samlpe.sizeDelta = new Vector2(size, size);
        RatingStarsComponent clone = samlpe.gameObject.AddComponent<RatingStarsComponent>();
        clone.image = samlpe.gameObject.AddComponent<Image>();
        clone.image.sprite = sprite;
        clone.image.color = starDefault;

        foreach (RatingStarsComponent comp in components)
        {
            if (comp != null) DestroyImmediate(comp.gameObject);
        }

        components = new RatingStarsComponent[count];

        float delta = size + offset;
        float posX = -delta * count / 2f - delta / 2f;

        Image img = GetComponent<Image>();
        img.rectTransform.sizeDelta = new Vector2(delta * count, size);
        img.color = background;

        int j = 1;

        for (int i = 0; i < components.Length; i++)
        {
            posX += delta;
            components[i] = Instantiate(clone) as RatingStarsComponent;
            components[i].image.rectTransform.SetParent(transform);
            components[i].image.rectTransform.anchoredPosition = new Vector2(posX, 0);
            components[i].id = j;
            components[i].rating = this;
            components[i].gameObject.name = "R-ID_" + j;
            j++;
        }

        DestroyImmediate(samlpe.gameObject);
    }
}
Всё что осталось, это настроить скрипт и нажать кнопку "создать" в инспекторе. В пару кликов можно быстро редактировать дизайн рейтинга, просто и удобно. Пользовательскую оценку мы получаем через метод void StarClick, и уже с этим делаем всё что угодно, например, отправляем результат на сервер.

Скачать демо:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.
Тестировалось на: Unity 2018.1.2

Комментариев 3

Офлайн
Добрый день. Помните давным давно вы делали battlecity и я хотел бы узнать как зделать так чтоб когда снаряд попадает в танк то был 50% шанс не пробить его?
Онлайн
Light 17 июня 2018
P_e_t_a_c_h_e_k, через Random.Range можно, целые числа - 0 и 1, не попал и попал.
Офлайн
Light,
Cпасибо
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика