使用 QAbstractItemModel 将 2D C++ 游戏板暴露给 QML [英] Expose 2D C++ Game Board to QML using QAbstractItemModel

查看:57
本文介绍了使用 QAbstractItemModel 将 2D C++ 游戏板暴露给 QML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 C++ 编写一个简单的 Snake 游戏,其中包含一个二维状态向量 (std::vector<std::vector<board::state>>>).现在我想将此板公开给 QML,以便它基本上是某种可以访问模型状态的网格/棋盘.我已经阅读了很多关于这个主题的内容,但仍然无法理解足够的机制来解决我的问题.将文档、教程和博客条目应用于我的问题是我的障碍.

I'm writing a simple Snake game with a game board model in C++ holding a two-dimensional vector of states (std::vector<std::vector<board::state>>). Now I want to expose this board to QML so that it's basically some sort of grid/chess board with access to the states of the model. I've read up a lot on this topic, but still wasn't able to understand the mechanics enough to solve my problem. Applying the docs, tutorials and blog entries to my problem is my hurdle here.

我为我的游戏板模型子类化了 QAbstractItemModel 并实现了必要的功能.现在我想进入 QML 并在那里使用/显示我的模型的内容.

I subclassed QAbstractItemModel for my game board model and implemented the necessary functions. Now I want to make the step into QML and use/show the contents of my model there.

这是我的代码:

board.h

#pragma once

#include <vector>

#include <QAbstractItemModel>

class board : public QAbstractItemModel
{
  Q_OBJECT

public:
  enum class state
  {
    empty,
    snake,
    fruit
  };

  board(int x, int y);

  state get_state(int x, int y) const;
  void  set_state(int x, int y, state state);

  QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
  QModelIndex parent(const QModelIndex& index) const;
  int rowCount(const QModelIndex& parent = QModelIndex()) const;
  int columnCount(const QModelIndex& parent = QModelIndex()) const;
  QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;

private:
  std::vector<std::vector<state>> m_board;
};

Q_DECLARE_METATYPE(board::state)

board.cpp

#include "board.h"

board::board(int x, int y) :
  m_board(x, std::vector<board::state>(y, board::state::empty))
{
}

//----------------------------------------------------------------------------------------------------------------------

board::state board::get_state(int x, int y) const
{
  if((size_t) x >= m_board.size() || x < 0)
    return board::state::empty;

  if((size_t) y >= m_board.at(0).size() || y < 0)
    return board::state::empty;

  return m_board.at(x).at(y);
}

//----------------------------------------------------------------------------------------------------------------------

void board::set_state(int x, int y, state state)
{
  if(get_state(x, y) == state)
    return;

  m_board.at(x).at(y) = state;
}

//----------------------------------------------------------------------------------------------------------------------

QModelIndex board::index(int row, int column, const QModelIndex&) const
{
  if((size_t) row >= m_board.size() || row < 0)
    return QModelIndex();

  if((size_t) column >= m_board.at(0).size() || column < 0)
    return QModelIndex();

  return createIndex(row, column);
}

//----------------------------------------------------------------------------------------------------------------------

QModelIndex board::parent(const QModelIndex& index) const
{
  if((size_t) index.row() >= m_board.size() || index.row() < 0)
    return QModelIndex();

  if((size_t) index.column() >= m_board.at(0).size() || index.column() < 0)
    return QModelIndex();

  return createIndex(index.row(), index.column());
}

//----------------------------------------------------------------------------------------------------------------------

int board::rowCount(const QModelIndex&) const
{
  return m_board.size();
}

//----------------------------------------------------------------------------------------------------------------------

int board::columnCount(const QModelIndex&) const
{
  return m_board.at(0).size();
}

//----------------------------------------------------------------------------------------------------------------------

QVariant board::data(const QModelIndex& index, int role) const
{
  if(!index.isValid())
    return QVariant();

  if(role != Qt::DisplayRole)
    return QVariant();

  if((size_t) index.row() >= m_board.size() || index.row() < 0)
    return QVariant();

  if((size_t) index.column() >= m_board.at(0).size() || index.column() < 0)
    return QVariant();

  return qVariantFromValue(get_state(index.row(), index.column()));
}

ma​​in.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickView>

#include <board.h>

int main(int argc, char *argv[])
{
  QGuiApplication app(argc, argv);



  board game_board(10, 10);
  game_board.set_state(4, 9, board::state::snake);
  game_board.set_state(3, 10, board::state::fruit);

  QQuickView view;
  view.setResizeMode(QQuickView::SizeRootObjectToView);
  QQmlContext *ctxt = view.rootContext();
  ctxt->setContextProperty("myGameBoard", &game_board);

  QQmlApplicationEngine engine;
  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

  return app.exec();
}

ma​​in.qml

import QtQuick 2.6
import QtQuick.Window 2.2

Window {
  visible: true

  MouseArea {
    anchors.fill: parent
    onClicked: {
      Qt.quit();
    }
  }

  Text {
    text: qsTr("Hello World")
    anchors.centerIn: parent
  }

  GridView {
    model: myGameBoard
    delegate: Rectangle {
      width: 30
      height: 30
      color: blue
    }
  }
}

我确定我遗漏了很多东西,或者只是明显错误.此外,我知道将 C++ 模型暴露给 QML 是经常被问及的问题,但我仍然无法做到.

I'm sure there's a lot of stuff that I'm missing or is just blatantly wrong. Also I'm aware that exposing C++ models to QML is frequently asked and well covered, but I still couldn't manage.

陈述几个更具体的问题:

To state a few more specific questions:

  • 如何在 QML 中显示模型中的数据?我已经看到这是使用角色名称完成的,但我的行没有其列的角色名称
  • 我的模型是否正确实施?
  • 我需要使用什么 QML 组件来制作瓷砖/国际象棋棋盘,如果我需要一个自定义组件,它需要哪些属性才能从模型中读取数据?

感谢观看!

推荐答案

我有一个适合我的解决方案.这是我所做的和一些代码:

I have a solution that works for me. Here is what I did and some code:

  • 我使用 Q_ENUM 作为状态枚举以使其在 QML 中可用
  • 将 rootContext 连接到 QML 引擎
  • 编写了一个自定义 QML 组件,其中包含一个带有转发器的列,其中包含一个带有转发器的行
  • 保存了外层转发器和内层转发器的索引,编制了访问数据的索引
  • 一些清理

代码

Board.qml

import QtQuick 2.0

Column {
  Repeater {
    model: myGameBoard.columnCount()

    Row {
      property int y_pos: index

      Repeater {
        id: repeatr
        model: myGameBoard.rowCount()

        Cell {
          x_cord: index
          y_cord: y_pos

          size: 20
        }
      } //Repeater
    } //Row
  } //Repeater
} //Column

Cell.qml

import QtQuick 2.0

Rectangle {
  id: cell
  property int x_cord: 0
  property int y_cord: 0
  property int size: 10

  width: size
  height: size

  color: getCorrectColor()

  Connections {
    target: myGameBoard
    onDataChanged: {
      cell.color = cell.getCorrectColor()
    }
  }

  function getCorrectColor() {
    switch(myGameBoard.data(myGameBoard.index(cell.x_cord, cell.y_cord)) + 0) {
    case 0 :
      return "honeydew"
    case 1 :
      return "black"
    case 2 :
      return "orangered"
    default:
      return "yellow"
    }
  }
}

C++方面基本保持不变,只是在board.h的状态枚举上使用了Q_ENUM.

C++ side remained basically the same, except for using Q_ENUM on the state enum on board.h.

感谢您的帮助和对中继器的提示!

Thanks for helping and the hints to the repeater!

这篇关于使用 QAbstractItemModel 将 2D C++ 游戏板暴露给 QML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆