请选择 进入手机版 | 继续访问电脑版

GA地精研究院

 找回密码
 立即注册
查看: 480|回复: 4

[研究] 头目1.5UI系列教程的演示文件里一些问题和修正

[复制链接]
发表于 2017-7-31 21:44:37 | 显示全部楼层 |阅读模式
本帖最后由 chansey 于 2017-7-31 22:51 编辑

最近看了头目的一篇教程:
1.5UI系列教程第一部——使用触发器来完全控制游戏界面元素~~让我们来制造一个永远开启状态的背包栏吧~~
http://bbs.islga.org/read-htm-tid-1038317.html
教程本身没有错误,我这里主要说的是演示文件里的一些问题(可能当时没问题,但在最新的3.16.0.55505里有问题)。

直接运行HookUpSample.SC2Map的话,会出现如下错误:
       0 UI: 框架[UIContainer/ConsoleUIContainsser/InventoryPanel/ContainerPanel00]在[\Unnamed\Unnamed\GameUI]中无法被关联。
       0 UI: 框架[UIContainer/ConsoleUIConsstainer/InventoryPanel/ContainerPanel00/Close]在[\Unnamed\Unnamed\GameUI]中无法被关联。

原因是ContainerPanel00元素没有创建,查看InventoryPanel/ContainerPanelTemplate节点,果然发现 延迟创建描述符 <DescFlags val="CreationDeferred"/>
经测试,发现ContainerPanel00这个元素,只有当玩家选中英雄后才会创建,默认是不会创建的。

而原本头目演示文件里的代码是:
Init
    事件
        TriggerAddEventMapInit()
    局部变量
    条件
    动作
        TriggerExecute(SetDialogEventNames,true,false)
        TriggerExecute(HookUpCommandButtons,true,false)
        TriggerExecute(HookUpInventoryPanel,true,false)
        UnitSelect(测试英雄 [61.06, 61.24],1,true)

即:先Hook,再Select,这显然有问题,因为Hook的时候还没Select,所以此时ContainerPanel00元素不存在。
(另一个问题是:TriggerExecute wait = false 可能会出现不同步问题,这里暂不去管他)

经修改后:
Init
    事件
        TriggerAddEventMapInit()
    局部变量
    条件
    动作
        UnitSelect(测试英雄 [61.06, 61.24],1,true)
        TriggerExecute(SetDialogEventNames,true,false)
        TriggerExecute(HookUpCommandButtons,true,false)
        TriggerExecute(HookUpInventoryPanel,true,false)

即:先Select,再Hook。
原本以为大功告成,结果发现出现同样的错误。。。

最后通过查看native.galaxy后,发现UnitSelect这个函数是异步的
// Notes:
// - UnitSelect and UnitGroupSelect set the local selection state,
//   which must be then transmitted across the network.
//
//   However, UnitIsSelected and UnitGroupSelected query the synchronous selection state,
//   which means that if you set the selection, then immediately query it, the results will
//   not match.
//
// - Unit selection state cannot be set during map initialization events.

也就是说:
当你在Trigger上调用了UnitSelect后,底层并不会立即修改游戏的Select状态(当然也不会修改UI状态),而是通过网络发送了Select事件。
游戏真正的Select状态的改变,需要等到网络数据返回后,即:
GameLogic handle Select事件 from Network -> 设置游戏Select状态 -> Post UI and Trigger 事件
UI handle UI事件 游戏Select状态改变 from GameLogic -> 修改UI状态
Trigger handle Trigger事件 游戏Select状态改变 from GameLogic -> 触发UnitSelected事件
(值得一提的是:让电脑玩家去SelectUnit是没有任何效果的)

因此,HookUp ContainerPanel00元素的操作,应该在UnitSelected事件里实现,而不是MapInit事件里实现。

最终解决方法:
1. 修改Init
Init
    事件
        TriggerAddEventMapInit()
    局部变量
    条件
    动作
        TriggerExecute(SetDialogEventNames,true,false)
        TriggerExecute(HookUpCommandButtons,true,false)
        TriggerExecute(HookUpInventoryButtons,true,false)
        UnitSelect(测试英雄 [61.06, 61.24],1,true)

Init里只HookUpCommandButtons和HookUpInventoryButtons,这两个元素是默认就存在。

2. 单独在UnitSelected事件里,为InventoryContainerPanel做绑定
HookUpInventoryContainerPanel
    事件
        TriggerAddEventUnitSelected(null,c_playerAny,true)
    局部变量
    条件
        Comparison((UnitAbilityExists((EventUnit()),"TestInventory")),==,true)
        Comparison(inventoryContainerPanelHooked,==,false)
    动作
        DialogControlHookupStandard(c_triggerControlTypePanel,"UIContainer/ConsoleUIContainer/InventoryPanel/ContainerPanel00")
        SetVariable(inventoryContainerPanel,(DialogControlLastCreated()))
        DialogControlHookupStandard(c_triggerControlTypeButton,"UIContainer/ConsoleUIContainer/InventoryPanel/ContainerPanel00/Close")
        DialogControlSetVisible((DialogControlLastCreated()),(PlayerGroupAll()),false)
        DialogControlSetEnabled((DialogControlLastCreated()),(PlayerGroupAll()),false)
        SetVariable(inventoryContainerPanelHooked,true)

注意:
1. ContainerPanel00虽然是延迟创建,但只要创建了之后就不会删除,所以这里设置了一个inventoryContainerPanelHooked变量优化性能
2. DescFlags 只是描述符,修改它是没有用的
3. Hook Up Inventory Container Panel 这个触发器必须在 Display Inventory Container Panel  这个触发器的上面,因为虽然是同一个事件,但我们希望 Hook Up Inventory Container Panel 这个触发器先触发,因此顺序很重要
4. native.galaxy里说,UnitSelect这个函数不能在 map initialization events里调用,但我仍然调用了,目前没有发现问题。
5. 目前这个改法只适合单机,如果要多人的话还需要做数组处理
6. 还有一种做法:在MapInit的时候创建一个带物品栏的临时单位,并Select,然后wait 0.01秒钟,Remove掉,但这个方法不是很优雅。

修改后的演示在附件里。
HookUpSampleFix.SC2Map (115.65 KB, 下载次数: 15)
发表于 2017-8-1 19:35:23 | 显示全部楼层
那个演示太早了。自由之翼那会儿根本没有CreationDeferred这个flag。用在现在当然不好使。

不过你这个修改很不错,好评。

点评

能收到头目夸奖,让我感到无比荣幸  详情 回复 发表于 2017-8-2 01:01
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-8-2 01:01:31 | 显示全部楼层
麦德三世 发表于 2017-8-1 19:35
那个演示太早了。虫群之心那会儿根本没有CreationDeferred这个flag。用在现在当然不好使。

不过你这个修 ...

能收到头目夸奖,让我感到无比荣幸
回复 支持 反对

使用道具 举报

发表于 2017-8-2 18:34:05 | 显示全部楼层
哇,好赞!!!
回复 支持 反对

使用道具 举报

发表于 2019-1-4 20:09:41 | 显示全部楼层
帮人搜索UI教程帖子,顺便看到楼主的帖子了,有几句话说一下。
这帖子发的很早,不过很奇怪我居然没看过。也正是因为这个,我自己在做动态物品系统的时候重新把楼主研究的东西又研究了一遍,而且为了解决它(因为是多人,需要考虑同步问题)做了很多尝试。
最终我还是选择了那个不太“优雅”的做法。主要是考虑多人游戏时,如果在玩家选中指定单位时运行触发器,那么没选中的玩家就可能会出错(触发器是同步的,而且衔接对话框项无法指定玩家),事实上完全无法做到让所有人同时选中一个单位,无论是玩家手动,还是触发器控制。当然我没有测试这种情况:如果某个玩家选择了单位,其他玩家电脑上是否也随之创建控件呢?希望有两台电脑的朋友们亲自测试一下。为了减少bug的可能。我最终选择为每个玩家创建一个单位,让他们选中,等待一个周期后再衔接控件。但是这个方法真的很不“优雅”,因为哪怕是一个游戏周期,在UI界面上也是能被玩家明显看到的,这种感觉就好比嗓子里卡了个鱼刺,虽然没啥大毛病,但就是让你心里很不爽。最后实在没法子,只好创建了一个优先级很高的黑色蒙版,遮蔽所有界面,等单位被移除后再删除它。
这里顶起这个帖子,并不是为了挖坟,主要是希望有这方面经验的大佬以及有兴趣研究这一问题的朋友们都来提一下建议,或许就能获得一个“优雅”的解决方案也说不真。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2019-1-17 14:36 , Processed in 0.061040 second(s), 8 queries , Gzip On, Redis On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表