我开发了一种扫雷式游戏,作为练习QML的一种方式。
我遇到的问题是内存使用情况会根据网格的大小快速增长(> 700 Mb)。 我的尺寸可以达到150 * 150(更大的网格会因为它的win32而崩溃)。
网格是这样构建的(来自Expose 2D C++ Game Board to QML using QAbstractItemModel的位)(该图像用于显示" bomb.png",显示地雷数量的文本):
Flickable {
id: flickable
width: parent.width
height: parent.height
enabled: false
Column {
id: cols
Repeater {
id: colRepeater
model: mapSize //defined by user
Row {
id: rows
property int y_pos: index
Repeater {
id: rowRepeater
model: mapSize
Tile{
id: tile
x_coord: index
y_coord: y_pos
...
}
}
}
}
}
这是我的Tile对象:
Rectangle {
id: root
...
Rectangle {
id: tileDecoration
visible: false
anchors.centerIn: parent
opacity:0
Image{
id: tileImage
anchors.fill: parent
visible: false
source: ""
}
Text{
id: minesText
anchors.centerIn: parent
}
}
}
编辑:我的问题是如何改善内存使用?对于扫雷而言,我没想到它需要那么多。我的目标也是在Android上进行测试,因此内存使用量有限。
答案 0 :(得分:2)
在看QtQuick& QML性能,我的关键规则一般是尽可能少做。不要创建您现在不一定需要向用户显示的项目和绑定。
我不能给你一个确切的数字(因为毕竟,确实没有一个 - 它的项目和硬件依赖),但我建议QtQuick最适合显示数千个数字的数字屏幕上一次显示的项目。更重要的是,事情将开始在资源使用和性能方面略微粗略。
无论好坏,QML都是一种非常容易通过表达来隐藏成本的语言。在使用它时,你需要考虑在编写这段代码时发生的事情;您创建的每个Item都是一个QQuickItem子类,它被分配,您设置的每个属性都是C ++函数调用。对于绑定,JavaScript引擎需要先评估该绑定(还要评估其依赖的其他内容),然后才能调用该函数来设置属性值。
查看您的Flickable片段,您有一个包含Repeater行的Repeater列。你说你允许高达150x150的网格。让我们对这个数字做一些粗略的数学运算。对于每个图块,您(至少)有两个矩形,一个图像和一个文本项,每个Tile.qml实例总共提供4个项目。此外,每个行都有一个固定的行和一个中继器。
这意味着网格上单行的总创建成本为:
2 + (4 * mapSize)
件商品,每行共有602件商品。然后,你有了它,乘以150,总共花费 90,300项来创建该网格。
每一行都有许多绑定:我在tile中计数9(Tile本身为7,不包括有点特殊的“id”赋值,再加上两个x& y coords),再加上另外两个Row和Repeater绑定,即每行2 + (9 * mapSize)
个绑定 - 每行1352个绑定,整个网格 202,800个绑定。
总而言之,这是一个非常大量的项目和绑定,可以一次使用。
这些项目中的每一项都有很大的成本 - 例如,每个项目本身就有一百个字节,为每个项目创建的绑定的额外分配,项目实际创建的QSGNode实例的分配在屏幕上获取内容,并且使用Repeater也意味着您在每个委托的JavaScript堆上都有一些额外的分配...这可以添加到非常大的内存,具体取决于您在一次。
所以,要直接回答你的问题,简单的答案就是“不要创造那么多东西”。我不确定你是如何实现这一目标的,但这里有几个可能性:
Image { Text { anchors.centerIn: parent } }
作为你的瓷砖GridView
来帮助您解决此问题,但它会对您的设计提供一些限制。对于最后一个选项,它会像这样。您将研究子类化QQuickItem
(在C ++方面),并拥有Q_PROPERTY(QQmlComponent* tileDelegate READ tileDelegate WRITE setTileDelegate NOTIFY tileDelegateChanged)
。在setTileDelegate setter中,调用QQuickItem::polish
,然后覆盖QQuickItem::updatePolish
然后创建(或只是重新定位)提供的tileDelegate组件的实例,偏离屏幕上当前显示的位置,直到你的“GameViewThing” “完全被瓷砖覆盖了。
我在这里有点模糊 - 对不起 - 但是一个完整的例子将是相当多的代码,所以我希望我已经提供了足够的信息和你可以自己开始的方向。