Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   forum.boolean.name > Программирование игр для компьютеров > Unity > Уроки

Ответ
 
Опции темы
Старый 05.04.2016, 13:11   #1
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,376
Написано 491 полезных сообщений
(для 886 пользователей)
ButtonWrapper

Класс-обёртка для удобства использования UI.Button.

Мне для игры понадобилось программно сделать кнопки с номерами уровней.
Сделал GridLayout, в который добавлял кнопки, создавая их из префаба.

Возникла проблема с событием onClick.

Код:
void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root = PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count = 48;
    for (
int k = 0; k < count; ++k) {
        var clone = 
GameObject.Instantiate(buttonPrefab);
        clone.
transform.SetParent(root);
        clone.
transform.localScale = Vector3.one;

        var 
button = clone.GetComponent<Button>();

        
text = button.transform.Find("Text").GetComponent<Text>();
        
text.text = ""+k;

        
UnityAction act = new UnityAction (() => SelectLevel_Start (k));
        
button.onClick.AddListener(act);
    }
} 
Любая кнопка при нажатии стартует уровень 48 - т.е. это значение К при выходе из цикла.
Я с ходу не понял, как можно впихнуть корректный параметр, и сделал обёртку для кнопки.

/* ButtonEventArgs */

public class ButtonEventArgs<TValue> : EventArgs {

    public 
TValue Value { get; private set; }

    public 
ButtonEventArgs(TValue value) {
        
Value = value;
    }

}


/* ButtonWrapper */

public class ButtonWrapper<TParam> {

    public 
event EventHandler<ButtonEventArgs<TParam>> OnClick;
    public 
GameObject gameObject;

    
Text text;
    
Button button;
    
TParam value;


    public 
ButtonWrapper(string caption, TParam value, GameObject buttonPrefab, Transform parent) {

        
this.value = value;

        
gameObject = GameObject.Instantiate(buttonPrefab);
        
gameObject.transform.SetParent(parent);
        
gameObject.transform.localScale = Vector3.one;

        
button = gameObject.GetComponent<Button>();
        
UnityAction act = new UnityAction(Clicked);
        
button.onClick.AddListener(act);

        
text = button.transform.Find("Text").GetComponent<Text>();
        
text.text = caption;

    }

    public 
void SetColor(string hex) {
        if (!
hex.StartsWith("#"))
            
hex = "#" + hex;
        
Color c;
        
ColorUtility.TryParseHtmlString(hex, out c);
        
button.GetComponent<Image>().color = c;
    }

    private 
void Clicked () {
        if (
OnClick != null)
            
OnClick(this, new ButtonEventArgs<TParam>(value));
    }

} 
Что имеем?
1. Событие onClick в виде привычного с# event'a, на которое можно подписаться через +=
2. При клике - событие может содержать любой тип данных, какой мы укажем при создании враппера - достаём данные из параметра ButtonEventArgs.

Итоговый код создания кнопок (добавлена раскраска - 4 разных цвета по 12 кнопок в каждом блоке)

void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root = PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count = 48;
    for (
int k = 0; k < count; ++k) {
        
ButtonWrapper<int> wrap = new ButtonWrapper<int>((k + 1).ToString(), k, prefabButtonLevelNum, root);
        
wrap.OnClick += (sender, e) => {
            
SelectLevel_Start(e.Value);
        };
        
int v = k / 12;
        
string color;
        if (
v == 0)
            
color = "41C64E";
        else if (
v == 1)
            
color = "F9FF67";
        else if (
v == 2)
            
color = "3381FF";
        else
            
color = "FF4B3E";
        
wrap.SetColor(color);
    }
} 
(Offline)
 
Ответить с цитированием
Старый 05.04.2016, 22:18   #2
seaman
Знающий
 
Регистрация: 08.01.2013
Адрес: Самара
Сообщений: 284
Написано 104 полезных сообщений
(для 180 пользователей)
Ответ: ButtonWrapper

Изучай Closure чтобы таких непоняток не было. Решается просто:
void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root = PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count = 48;
    for (
int k = 0; k < count; ++k) {
        var clone = 
GameObject.Instantiate(buttonPrefab);
        clone.
transform.SetParent(root);
        clone.
transform.localScale = Vector3.one;

        var 
button = clone.GetComponent<Button>();

        
text = button.transform.Find("Text").GetComponent<Text>();
        
text.text = ""+k;
        
int j = k;
        
UnityAction act = new UnityAction (() => SelectLevel_Start (j));
        
button.onClick.AddListener(act);
    }
} 
Т.е. делаешь временную переменную, которой присваиваешь текущее значение. И уже ее используешь в лямбда выражении
(Offline)
 
Ответить с цитированием
Эти 2 пользователя(ей) сказали Спасибо seaman за это полезное сообщение:
St_AnGer (05.04.2016), Жека (06.04.2016)
Старый 05.04.2016, 23:17   #3
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,067
Написано 1,185 полезных сообщений
(для 2,828 пользователей)
Ответ: ButtonWrapper

Сообщение от seaman Посмотреть сообщение
Изучай Closure чтобы таких непоняток не было. Решается просто:
void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root = PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count = 48;
    for (
int k = 0; k < count; ++k) {
        var clone = 
GameObject.Instantiate(buttonPrefab);
        clone.
transform.SetParent(root);
        clone.
transform.localScale = Vector3.one;

        var 
button = clone.GetComponent<Button>();

        
text = button.transform.Find("Text").GetComponent<Text>();
        
text.text = ""+k;
        
int j = k;
        
UnityAction act = new UnityAction (() => SelectLevel_Start (j));
        
button.onClick.AddListener(act);
    }
} 
Т.е. делаешь временную переменную, которой присваиваешь текущее значение. И уже ее используешь в лямбда выражении
О! Только вчера до этого открытия своим мозгом допёр и тоже именно для гуя докапывался, что бы на генерируемые кнопки вешать события вот такого плана.
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a
(Offline)
 
Ответить с цитированием
Старый 06.04.2016, 05:08   #4
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,376
Написано 491 полезных сообщений
(для 886 пользователей)
Ответ: ButtonWrapper

Блин, а я внутри лямбды делал переменную, и конечно она не сработала, т.к. код вызывается при нажатии, и я получал тот же count.
Спасибо!
(Offline)
 
Ответить с цитированием
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +4, время: 16:19.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com