美文网首页
dota2rpg panorama ui的可拖动控件的实现

dota2rpg panorama ui的可拖动控件的实现

作者: XavierCHN | 来源:发表于2019-05-22 15:29 被阅读0次

    本文通过解析 dota_addons/ui_example里面的inventory_item控件来说明如何在dota2rpg的UI中制作一个可拖动的控件。

    一、可拖动控件的注册和回调的声明

    在dota2中,任何的panel都可以变为可拖动的元素,需要在定义这个panel的时候,将这个panel的draggabletag设置为true。即:

    <Panel draggable="true" />
    

    这个panel就可以变为可拖动的panel。

    他们就可以通过$.RegisterEventHandler( 'DragEnter', panel, OnDragEnter );这类语句来绑定他们的拖动回调函数。
    这些的事件包括:

    $.RegisterEventHandler( 'DragEnter', panel, OnDragEnter );
    $.RegisterEventHandler( 'DragDrop', panel, OnDragDrop );
    $.RegisterEventHandler( 'DragLeave', panel, OnDragLeave );
    $.RegisterEventHandler( 'DragStart', panel, OnDragStart );
    $.RegisterEventHandler( 'DragEnd', panel, OnDragEnd);
    

    当然,这也不代表你一定需要定义这么多东西,如果你只需要响应DragStart和DragEnd,那么根据你的实际需要去写即可。

    二、拖动回调函数

    拖动具体需要哪些函数需要根据制作的实际需要去写。在dota2的可拖动的物品中,需要的函数包括:

    OnDragStart:当拖动开始的时候->需要隐藏物品的提示,创建拖动的图像,然后给拖动开始的格子添加一个是从这里开始拖动的显示效果。
    OnDragLeave:当拖动着panel的鼠标移动离开panel的时候(这个离开可能是初始格子也可以是中途某个经过的格子)->取消这个格子的高亮状态
    DragEnter: 当拖动着的panel鼠标移动到panel上方的时候->高亮这个格子来表示你想要拖动东西到这个格子里;
    OnDragDrop:当拖动着panel的鼠标移动到panel上方并松开的时候->你想要交换拖动的原始格子和目标格子中的物品(目标格子可能为空);
    OnDragEnd:当拖动着panel的鼠标松开的时候-> 无论是拖动到另一个格子上还是拖动到地上了,都会调用这个回调,因此需要在OnDragDrop里面对拖动到另一个格子里的事件做出响应。

    具体的回调函数如下:

    function OnDragStart( panelId, dragCallbacks )
    {
        // m_Item是在全局里保存的当前物品的id,如果不存在则不开始拖动
        if ( m_Item == -1 )
        {
            return true;
        }
    
        // 获取物品名称
        var itemName = Abilities.GetAbilityName( m_Item );
    
        // 隐藏正在显示的物品提示
        ItemHideTooltip();
    
        // 创建用来拖动的那个panel,也就是被拖动的图像,他并不是原来显示在那里的那个物品图标,而是新建的
        var displayPanel = $.CreatePanel( "DOTAItemImage", $.GetContextPanel(), "dragImage" );
        // 在创建的那个panel里面保存一些数据,在其他的回调中可以用
        displayPanel.itemname = itemName; // 物品名称
        displayPanel.contextEntityIndex = m_Item; // 物品序列号ID
        displayPanel.data().m_DragItem = m_Item; // 拖动的物品ID,其实这个data()并不是必要的,甚至这一步指令也不是必要的
        displayPanel.data().m_DragCompleted = false; // 拖动到底是拖动到另一个格子还是拖动到地上的标志位
    
        // 将用来显示的panel赋值给dragCallbacks.displayPanel就可以开始跟随鼠标移动了
        // 这两个offset定义了这个panel到底要在粘上去的位置产生多少偏移
        // 因为displayPanel的创建时在$.GetContextPanel,也就是inventory_item的xml里面创建的,因此也就不需要偏移了
        dragCallbacks.displayPanel = displayPanel;
        dragCallbacks.offsetX = 0; 
        dragCallbacks.offsetY = 0; 
        
        // 这个class会隐藏当前格子的物品图标,就给玩家感觉像是当前的格子里的物品被拖走了
        // 其实他还在原来的位置,只不过被隐藏了
        // 被拖动的是上面创建的那个displayPanel
        $.GetContextPanel().AddClass( "dragging_from" );
        return true;
    }
    
    function OnDragLeave( panelId, draggedPanel )
    {
        // 获取正在拖动的物品的ID
        var draggedItem = draggedPanel.data().m_DragItem;
        // 如果正在拖动的就是当前的物品(也就是第一次离开原始的格子,那么不做响应)
        if ( draggedItem === null || draggedItem == m_Item )
            return false;
    
        // 如果是拖动经过了这个格子,因为OnDragEnter添加了这个class
        // 用来指示这个是当前的目标,离开的时候需要移除这个class
        $.GetContextPanel().RemoveClass( "potential_drop_target" );
        return true;
    }
    
    
    function OnDragEnter( a, draggedPanel )
    {
        // 获取正在拖动的物品ID
        var draggedItem = draggedPanel.data().m_DragItem;
    
        // 如果正在拖动的就是当前的物品(也就是第一次离开原始的格子,那么不做响应)
        if ( draggedItem === null || draggedItem == m_Item )
            return true;
    
        // 拖动经过这个格子了,高亮他,用来指示玩家想要拖动物品到这个格子
        $.GetContextPanel().AddClass( "potential_drop_target" );
        return true;
    }
    
    // 这个是关键的回调,因为这个回调里有发送给服务器的交换物品的API
    function OnDragDrop( panelId, draggedPanel )
    {
        // 获取拖动的物品ID
        var draggedItem = draggedPanel.data().m_DragItem;
        
        // 如果正在拖动的就是当前的物品(也就是第一次离开原始的格子,那么不做响应)
        // 只需要标志一下m_DragCompleted给OnDragEnd使用一下,让他不要
        // 丢物品到地上
        if ( draggedItem === null )
            return true;
        draggedPanel.data().m_DragCompleted = true;
        if ( draggedItem == m_Item ) // 拿起来又放到同一个格子里面
            return true; // 返回,不做交换物品的操作
    
        // 告知服务器玩家想要交换物品
        var moveItemOrder =
        {
            OrderType: dotaunitorder_t.DOTA_UNIT_ORDER_MOVE_ITEM,
            TargetIndex: m_ItemSlot,
            AbilityIndex: draggedItem
        };
        Game.PrepareUnitOrders( moveItemOrder );
    
        return true;
    }
    
    // 拖动结束,这个里面有丢物品到地上的API
    function OnDragEnd( panelId, draggedPanel )
    {
        // 这个方法无论是拖动到另一个格子里,还是拖动到地上,都会响应
        // 如果调用这个之前没有先调用OnDragDrop的话,那么就告知
        // 服务器玩家想要丢东西到地上
        if ( !draggedPanel.data().m_DragCompleted )
        {
            Game.DropItemAtCursor( m_QueryUnit, m_Item );
        }
    
        // 移除被拖动的面板
        draggedPanel.DeleteAsync( 0 );
    
        // 清除拖动来源的格子,也就是调用这个的格子的dragging_from效果
        // UI里的交换物品图标,则会在服务器完成调换格子的操作之后
        // 通知客户端更新
        $.GetContextPanel().RemoveClass( "dragging_from" );
        
        return true;
    }
    

    三、其他的拖动需求分析

    1. 可拖动的窗口,例如背包窗口等,那么OnDragEnter、OnDragDrop和OnDragLeave是不需要的,应该只需要OnDragStart和OnDragEnd,此外,由于没有兄弟panel的问题,因此displayPanel可以使用窗口本体,这样你甚至连OnDragEnd都不需要写具体的内容。
    2. 卡牌游戏的卡牌,卡牌游戏的卡牌,也只需要OnDragStart和OnDragEnd,此外,卡牌在拖动过程中的响应,需要自己使用Schedule不断更新,比如更新卡牌的当前状态,指示当前卡牌目标位置等。

    相关文章

      网友评论

          本文标题:dota2rpg panorama ui的可拖动控件的实现

          本文链接:https://www.haomeiwen.com/subject/jwcuzqtx.html