C
Qt Quick ウルトラライトチェスの例
Qt Quick UltraliteでC++シングルトン・オブジェクトを使う方法を示す。
概要
これはC++オブジェクトをQt Quick Ultraliteに統合する方法を示す簡単な例です。これは、意図的にすべてのルールに準拠していない簡略化されたチェスゲームを実装しています。2人のプレーヤーが手を打ち、反対側のプレーヤーの駒を捕獲することができます。ゲームは、右上にプレイヤーの手番、右下に最後の手からの経過時間を表示する。

ゲームロジックはC++のシングルトンオブジェクトに実装され、いくつかのプロパティとメソッドがシングルトンからQMLコードに公開されます。QMLコードは、マウスイベントをシングルトンに接続し、碁盤の現在の状態を表示する。
プロジェクトの構成
CMakeプロジェクトファイル
C++シングルトン・オブジェクトのプロパティやメソッドをQMLファイルで使う前に、qmlinterfacegenerator 。これはQmlProjectのInterfaceFiles.filesプロパティを使い、ヘッダーファイル上でツールを実行します。
...
InterfaceFiles {
files: ["chessmodel.h"]
}
...シングルトンオブジェクト
ChessModel クラスはチェスボードの管理を担当します。Qul::Singleton を継承し、publicメソッド、シグナル、プロパティをQMLに公開しています。
...
Qul::Property<bool> whiteTurn;
Qul::Property<int> secondsSinceMove;
Qul::Signal<void(int col, int row)> validMove;
Qul::Signal<void()> invalidMove;
...
/// Return the current column position of a specific piece
/// (or -1 if it is not in the chess board).
/// This function is public and can be called from the QML
int col(int piece) { return position[piece].value().first; }
/// Return the current row position of a specific piece
/// (or -1 if it is not in the chess board).
/// This function is public and can be called from the QML
int row(int piece) { return position[piece].value().second; }
/// Return true if it is allowed to drop the piece on thie case
/// This function is public and can be called from the QML
bool canDrop(int col, int row)
{
if (col < 0 || col > 7 || row < 0 || row > 7)
return false;
return squares[col][row].value().canDrop;
}
/// Set the current active piece
/// (if piece is negative, there shall not be any active piece)
/// This function is public and can be called from the QML
void setActivePiece(int piece)
...
void release(int piece, int col, int row)
...アプリケーションUI
chess.qml ファイルはチェス盤とチェスの駒を定義します。2つのRepeaters を使って、Rectangle アイテムを使ってチェス盤を作り、駒を配置します:
チェスボードを担当するReapeator
...
// The chess board: simply 64 squares of different colors
Repeater {
model: 64
Rectangle {
property int row: Math.floor(index/8);
property int col: index % 8;
z: 0;
x: col * squareSize
y: (7 - row) * squareSize
height: squareSize
width: squareSize
color: {
var even = ((row + col) % 2) == 0;
if (!ChessModel.canDrop(col, row))
return even ? "#d18b47" : "#ffce9e";
if (row == hoverRow && col == hoverCol)
return even ? "#d18bff" : "#ffceff";
return even ? "#ff8b47" : "#ffaa88";
}
}
}
...フィールドの色は、canDrop() メソッドを使ってChessModel にバインドされる。これは、特定の駒の可能な動きをハイライトする。ある駒についてcanDrop() が返す値が変わるたびに、色のバインディングが再評価される。
チェスの駒を担当するReapeator
ChessModel を使って各駒の位置を取得します。また、setActivePiece() とrelease() メソッドを呼び出して、ユーザーのアクションをモデルに知らせます。
...
// The chess pieces: There are 32 chess pieces, the first 16 are white, and the
// last 16 are black.
Repeater {
model: 32
Item {
id: pieceText;
visible: ChessModel.col(modelData) >= 0;
x: squareSize * ChessModel.col(modelData);
y: squareSize * (7 - ChessModel.row(modelData));
// Note: with QUL, the item in a repeater might not get the same order relative to
// the item, so we use the z order to ensure that pieces are on top
z: 1
height: squareSize
width: squareSize
Text {
color: index < 16 ? "#eee" : "#444"
text: {
var p = index % 16;
switch (p) {
case 0:
return "♚";
case 1:
return "♛";
case 2:
case 3:
return "♜";
case 4:
case 5:
return "♝";
case 6:
case 7:
return "♞";
}
return "♟";
}
x: (pieceTouch.pressed ? pieceTouch.mouseX - pieceTouch.pressedX : 0);
y: (pieceTouch.pressed ? pieceTouch.mouseY - pieceTouch.pressedY : 0);
height: squareSize
width: squareSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
MouseArea {
id: pieceTouch;
anchors.fill: pieceText;
property real pressedX: 0
property real pressedY: 0
onPressed: {
pressedX = mouse.x
pressedY = mouse.y
}
onPressedChanged: {
if (pressed) {
ChessModel.setActivePiece(modelData);
} else {
ChessModel.release(modelData, hoverCol, hoverRow);
ChessModel.setActivePiece(-1);
}
}
onMouseXChanged: hoverCol = (pieceText.x + pieceTouch.mouseX - pieceTouch.pressedX + squareSize / 2) / squareSize;
onMouseYChanged: hoverRow = 7 - (pieceText.y + pieceTouch.mouseY - pieceTouch.pressedY - squareSize / 3) / squareSize;
}
}
}
...また、ChessModel のアクションをinvalidMove とvalidMove のシグナルにバインドします:
...
ChessModel.onInvalidMove: invalidLabel.visible = true
ChessModel.onValidMove: {
invalidLabel.visible = false;
console.log("valid move ", col, row);
}ファイル