SDL中文论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 6064|回复: 4
打印 上一主题 下一主题

[animation] 浮动标签(floating label)

[复制链接]

21

主题

36

帖子

334

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
334
跳转到指定楼层
楼主
发表于 2020-9-3 18:23:01 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
游戏中使用浮动标签的几个例子
1:在地图上标注字符,像调试时用的格子坐标。
2:鼠标落在窗口控件、状态报告上,出现工具提示。
3:鼠标落在地图上单位,左下角/右下角显示单位信息。
4:攻击时显示的失血值,攻击方式(减速、中毒)。
5:多人对战时显示的即时消息。

组织浮动标签
同一时刻,会有若干个浮动标签,这些浮动标签被二级分类。
1:按层进行分类。这个层的定义标准和窗口层次意义相近。游戏主界面时,它只有一层;弹出一对话框时,增加一层;关闭该对话框时,恢复回只剩游戏主界面这一层。任一浮动标签一定属于一层,也只能属于某一层。
2:在同一层内,浮动标签被“公平”存放。
3:系统对每个浮符标签赋给一个id(一个整形值),这个id从1开始,不断增加,数值和层无关。


显示浮动标签
在要显示时,具体说是在flip时,把所有浮动标签绘制向(draw)frame buffer。

flip后,从frame buffer擦除(undraw)掉所有floating_label。

以上这些函数都在display::draw中的draw_wrap中执行。
回复

使用道具 举报

21

主题

36

帖子

334

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
334
沙发
 楼主| 发表于 2020-9-3 18:23:26 | 只看该作者

游戏运行时浮动标签层变换情况

启动界面
show_title头一次建立层,但在该函数退出后,该层消失。游戏回复到没有层。

dialog_manager类从font::floating_label_context派生,一旦构造dialog_manager对象则相当于建立了一浮动标签层。(常见对话框的显示函数都会构造dialog_manager对象)

play_controller有一个成员变量:labels_manager_,这个变量类型是font::floating_label_context,一旦play_controller被构造,相当于建立了一浮动标签层。前面虽建立过浮动标签层,但用完即被删。play_controller作为一个较长久对象,它可以认为是游戏主程序时最底的浮动标签层。
回复 支持 反对

使用道具 举报

21

主题

36

帖子

334

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
334
板凳
 楼主| 发表于 2020-9-3 18:23:49 | 只看该作者

程序

为组织所有浮动标签,程序使用了两个全局变量。
  1. std::map<int, floating_label> labels;
复制代码
labels存储所有浮动标签,在这里浮动标答没有层的概念,创建一个就加入一个,删除一个就擦除一个。int部分就是浮动标签id。
  1. std::stack<std::set<int> > label_contexts;
复制代码
label_contexts是浮动标签分层组织变量。由它可看出浮动标签被分层组织。因为是先进后出,在第一分法上采用栈,对于同一层的则“公平”存储,采有std::set。set中的int就是某个id,可以以它为关键字从labels中搜出某个floating_label。

显示/擦除标签函数
  1. void draw_floating_labels(surface screen)
复制代码
绘制当前层所有浮动标签到screen图面。screen图面可能是frame buffer,可能是前景图面,display::draw时是frame buffer。
  1. void undraw_floating_labels(surface screen)
复制代码
从screen图面擦除当前层所有浮动标签。screen图面可能是frame buffer,可能是前景图面,display::draw时是frame buffer。

floating_label_context
floating_label_context类用于构造/拆除符动标签层。构造函数对应构造层,析构函数对应拆除层。
floating_label_context析构函数不仅仅只构造层。在构造层新前层会把最顶旧层的所有符动标标绘向前景图面。
floating_label_context析构函数不仅仅拆除层,在拆除层前会删除该层所有浮动标签,拆除后把新顶层的所有符动标签从前景图面擦除。

FAQ
A:标签显示后无法擦除
Q:可通造成的一个原因,draw_floating_labels被连续调用了两次。
举个例子,程序一进入时就让显示一个标签A,程序接下显示战役目标对话框,由此标签A没法消除。对于这个无法消除原因是:
1)显示标签A时,display::flip(),成对调用了draw_floating_labels和undraw_floating_labels。也就是在flip退出前,图面frame buffer中是没有标签的;
2)显示战役对话框。要调用于twindow::draw(),这函数要调用draw_floating_labels但不调用undraw_floating_labels,这使得frame buffer出现了标签;
3)display::draw被调用,它会调用display::flip(),这flip()调用draw_floating_labels,结果它把有标签的当前背景,它画在是在那背影上继续画(看到就是半透明的变成不透明),而undraw_floating_labels时,它恢复背景,而这背景已是有twindow::draw()画过标签的背景。
??当在“菜单”--“战役目标”,这时弹出战役目标对话框后,是可以擦除标签,奇怪!(按下“菜单”是会新增一浮动标签层,但一选中“战役目标”该层被拆掉”,这里不是层的问题。难道是frame buffer和主图面,这时是直接把标签画向主图面的?)
回复 支持 反对

使用道具 举报

21

主题

36

帖子

334

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
334
地板
 楼主| 发表于 2020-9-3 18:24:48 | 只看该作者

征兵时部队浮动信息

征兵要选择武将,为让玩家直观看到此个组合能达到效果,需要显示组合后该部队的各项数值,这个各项数值随着选中武将变化而变动,叫浮动信息。

浮数信息是以浮动标签形式实现的。游戏中是下图的右侧部分。


实现浮动信息逻辑
让自底向上说明这个逻辑。

要得出浮动信息需要部队对象,要创建一个临时unit对象就要兵种、组队三武将、归属城市信息。hero面板只能提供武将列表中哪些个索引被选择,recruit面板则几乎有全部信息,为此让recurit面板完成创建unit对象和最后显示,具体操作:
  1. @dlg:hero面板所有对框话指针。要由它知道hero菜单信息和计算浮动数据显示在的起始坐标。
  2. void recruit_pane::on_hero_list_draw(gui::dialog* dlg)
  3. {
  4.         std::vector<size_t> checked_heros = dlg->get_menu().checked();
  5.         if (checked_heros.empty()) {
  6.                 resources::screen->hide_unit_tooltip();
  7.                 return;
  8.         }
  9.         std::vector<hero*> heros = city_.fresh_heros();
  10.         std::sort(heros.begin(), heros.end(), compare_leadership);

  11.         const unit_type* t = (*unit_types_)[index_];
  12.         std::vector<const hero*> v;
  13.         v.push_back(heros[checked_heros[0]]);
  14.         if (checked_heros.size() > 1) {
  15.                 v.push_back(heros[checked_heros[1]]);
  16.         }
  17.         if (checked_heros.size() > 2) {
  18.                 v.push_back(heros[checked_heros[2]]);
  19.         }
  20.         type_heros_pair pair(t, v);
  21.         unit temp(units_, heros_, pair, city_.cityno(), false);
  22.         gui::dialog::dimension_measurements dim = dlg->get_layout();
  23.         resources::screen->show_unit_tooltip(temp, dim.interior.x + dim.interior.w + 5, dim.interior.y - 28);
  24. }
复制代码
那on_hero_list_draw是如何被调用?——使用回调,在hero面板的draw_contents()调用该函数。

要在hero_list_pane::draw_contents()调用,需要注意:
1、hero对话框中(其它对话框也一样),菜单内部操作不能刷新面板,要通过一种机制告知对话框上层,对话框上层检测到需要刷新了,然后去控制剧新面板。
2、一直来实现效果:菜单选中行发生该变时会刷新面板。用第1点机制来说:菜单选中行发生变动,菜单的selected_变量改变值;对话框上层一直在循环,循环发现菜单的selected_发生改变,于是去调用面板set_selection();面板的set_selection()把自己设为dirty状态,widgets机制再使此个结果调用面板的draw_contents()。
3、菜单中选择框选中、不选中操作不会引起菜单行发生变动。

以上3点可以得出,要让选择框选中和不选中导致调用面板的draw_contents()就须要改变对话框上层认为该去调用set_selection()条件。
  1. if ((menu_->selection() != info.selection) || menu_->checks_dirty() || info.first_time) {
  2.         menu_->clear_checks_dirty();
  3.         info.selection = menu_->selection();
  4.         int selection = info.selection;
  5.         if (selection < 0) {
  6.                 selection = 0;
  7.         }
  8.         if (!preview_panes_.empty()) {
  9.                 for (pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
  10.                         (**i).set_selection(selection);
  11.                         ......
复制代码
menu_->checks_dirty()是新加条件。当有选择框发生选择改变时它返回true。

注:因为选择框变动导致的面板set_selection,这时菜单选择项并未改变,面板set_selection要知道这个情况,改为即使菜单选中项没改变也要去设置面板为dirty状态。
回复 支持 反对

使用道具 举报

21

主题

36

帖子

334

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
334
5#
 楼主| 发表于 2020-9-3 18:25:37 | 只看该作者

显示攻击性文字

unit_frame::redraw
  |--game_display::float_label
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|丽谷软件|libsdl.cn

GMT+8, 2025-5-2 05:07 , Processed in 0.063210 second(s), 22 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

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