QtQuick, Dynamic Images and C++

别说谁变了你拦得住时间么 提交于 2019-12-03 08:00:43

First way to do that would be creating a Rectangle for each game pixel in QML, which might be fancy for a 8x8 board, but not for a 100x100 board, since you need to write the QML code manually for each pixel.

Thus I'd go for images created in C++ and exposed to QML. You call them via an image provider to allow asynchronous loading. Let Life do the logic only.

The image is called from QML like this:

Image {
    id: board
    source: "image://gameoflife/board"
    height: 400
    width: 400
}

Now gameoflife is the name of the image provider and board the so-called id you can use later.

Register gameoflife in you main.cpp

LifeImageProvider *lifeIP = new LifeImageProvider(life);
engine.addImageProvider("gameoflife", lifeIP);

where engine is your main QQmlApplicationEngine and life an instance of your Life game engine.

LifeImageProvider is your class to create pixeldata. Starts somehow like

class LifeImageProvider : public QQuickImageProvider
{
public:
    LifeImageProvider(Life *myLifeEngine);
    QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);

private:
    Life *myLifeEngine_;
};

The important method is requestPixmap, which is called from QML. You need to implement it.

To refresh the game board when Life sends a stateChanged() signal, expose life as a global object to QML:

context->setContextProperty("life", &life);

You can bind the signal to QML

Image {
    id: board
    source: "image://gameoflife/board"
    height: 400
    width: 400
}

Connections {
    target: life
    onStateChanged: {
        board.source = "image://gameoflife/board?" + Math.random()
        // change URL to refresh image. Add random URL part to avoid caching
    }
}

Just for fun, and at the risk of downvotes for a completely tangential answer, here's a GameOfLife implemented entirely in QML, just put it in a .qml file and run it with qmlscene. Works on Qt 5.3.0, and runs surprisingly (to me) fast on an old Core 2 Duo lappy. I'm sure it'll never be as fast/efficient as a C++ QQuickImageProvider based solution though, but it does make the point it's possible to do quite a lot in QML without resorting to C++.

import QtQuick 2.2

Rectangle {
  id: main
  width: 640
  height: 640
  color: '#000088'

  Timer {
    interval: 1000/60
    running: true
    repeat: true
    onTriggered: {advance();display();}
  }

  Component {
    id: cellComponent
    Rectangle {
      objectName: 'cell'
      property int row: 0
      property int col: 0      
      x: main.width/2+width*col
      y: main.height/2+height*row      
      width: 5
      height: 5
      radius: 2
      smooth: true
      color: '#ffcc00'
    }
  }

  property var cells: null

  Component.onCompleted: {
    cells=[[-1, 0],[-1, 1],[ 0,-1],[ 0, 0],[ 1, 0]];
    display();
  }

  function display() {
    // Just completely regenerate display field each frame
    // TODO: might be nicer to do differential updates, would allow birth/death animations   

    // Nuke all previously displayed cells
    for (var i=0;i<children.length;i++) {
      if (children[i].objectName=='cell') {
        children[i].destroy();
      }
    }

    // Show current set of cells
    for (var i=0;i<cells.length;i++) {
      var c=cellComponent.createObject(
        main,
        {'row':cells[i][0],'col':cells[i][1]}
      );
    }
  }

  function advance() {

    // Build a hash of the currently alive cells and a neighbour count (includes self)
    var a=new Object;
    var n=new Object;
    for (var i=0;i<cells.length;i++) {
      var p=cells[i]
      var r=p[0];
      var c=p[1];
      if (!(r in a)) a[r]=new Object;
      a[r][c]=1;
      for (var dr=r-1;dr<=r+1;dr++) {
        for (var dc=c-1;dc<=c+1;dc++) {
          if (!(dr in n)) n[dr]=new Object;
          if (!(dc in n[dr])) n[dr][dc]=0;
          n[dr][dc]+=1;
        }
      }
    }

    // For all live cells, assess viability
    var kill=[];
    var stay=[];
    for (var r in a) {
      for (var c in a[r]) {
        if (n[r][c]-1<2 || n[r][c]-1>3)
          kill.push([Number(r),Number(c)]);
        else
          stay.push([Number(r),Number(c)]);
      }
    }

    // For neighbours of live cells, assess potential for births
    var born=[];
    for (var r in n) {
      for (var c in n[r]) {
        if (!((r in a) && (c in a[r]))) {
          if (n[r][c]==3)
            born.push([Number(r),Number(c)]);
        }
      }
    }   

    cells=stay.concat(born)
  }
}

And for a pure QML version using GLSL (via a recursive QML ShaderEffect) to compute the Game of Life rules on GPU see here.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!