forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Уроки (http://forum.boolean.name/forumdisplay.php?f=145)
-   -   ButtonWrapper (http://forum.boolean.name/showthread.php?t=20251)

Жека 05.04.2016 13:11

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

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

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

Код:
PHP код:

void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count 48;
    for (
int k 0count; ++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 - т.е. это значение К при выходе из цикла.
Я с ходу не понял, как можно впихнуть корректный параметр, и сделал обёртку для кнопки.

PHP код:

/* 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 captionTParam valueGameObject buttonPrefabTransform 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(hexout 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 кнопок в каждом блоке)

PHP код:

void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count 48;
    for (
int k 0count; ++k) {
        
ButtonWrapper<intwrap = new ButtonWrapper<int>((1).ToString(), kprefabButtonLevelNumroot);
        
wrap.OnClick += (sendere) => {
            
SelectLevel_Start(e.Value);
        };
        
int v 12;
        
string color;
        if (
== 0)
            
color "41C64E";
        else if (
== 1)
            
color "F9FF67";
        else if (
== 2)
            
color "3381FF";
        else
            
color "FF4B3E";
        
wrap.SetColor(color);
    }



seaman 05.04.2016 22:18

Ответ: ButtonWrapper
 
Изучай Closure чтобы таких непоняток не было. Решается просто:
PHP код:

void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count 48;
    for (
int k 0count; ++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);
    }


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

St_AnGer 05.04.2016 23:17

Ответ: ButtonWrapper
 
Цитата:

Сообщение от seaman (Сообщение 305259)
Изучай Closure чтобы таких непоняток не было. Решается просто:
PHP код:

void InitLevelTasks()
{
    
levelChooser = new LevelChooser(imageForLevelTask);
    
Transform root PanelLevelsList;
    foreach (
Transform child in root) {
        
Destroy(child.gameObject);
    }
    
int count 48;
    for (
int k 0count; ++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);
    }


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

О! Только вчера до этого открытия своим мозгом допёр :) и тоже именно для гуя докапывался, что бы на генерируемые кнопки вешать события вот такого плана.

Жека 06.04.2016 05:08

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


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

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot