Implemented basic speed game

This commit is contained in:
2021-07-17 05:09:39 +02:00
parent 294c2c32e9
commit b7f418c3a9
44 changed files with 2088 additions and 0 deletions

80
qml/App.qml Normal file
View File

@@ -0,0 +1,80 @@
import QtQuick 2.5
Item {
id: app
anchors.fill: parent
opacity: 0.0
Behavior on opacity { NumberAnimation { duration: 500 } }
property var lastPages: []
property int __currentIndex: 0
function init()
{
opacity = 1.0
showPage("Connect.qml")
}
function prevPage()
{
lastPages.pop()
pageLoader.setSource(lastPages[lastPages.length-1])
__currentIndex = lastPages.length-1;
}
function showPage(name)
{
lastPages.push(name)
pageLoader.setSource(name)
__currentIndex = lastPages.length-1;
}
TitleBar {
id: titleBar
currentIndex: __currentIndex
onTitleClicked: {
if (index < __currentIndex)
pageLoader.item.close()
}
}
Loader {
id: pageLoader
anchors.left: parent.left
anchors.right: parent.right
anchors.top: titleBar.bottom
anchors.bottom: parent.bottom
onStatusChanged: {
if (status === Loader.Ready)
{
pageLoader.item.init();
pageLoader.item.forceActiveFocus()
}
}
}
Keys.onReleased: {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back: {
if (__currentIndex > 0) {
pageLoader.item.close()
event.accepted = true
} else {
Qt.quit()
}
break;
}
default: break;
}
}
BluetoothAlarmDialog {
id: btAlarmDialog
anchors.fill: parent
visible: !connectionHandler.alive
}
}

View File

@@ -0,0 +1,72 @@
import QtQuick 2.5
Item {
id: root
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: "black"
opacity: 0.9
}
MouseArea {
id: eventEater
}
Rectangle {
id: dialogFrame
anchors.centerIn: parent
width: parent.width * 0.8
height: parent.height * 0.6
border.color: "#454545"
color: GameSettings.backgroundColor
radius: width * 0.05
Item {
id: dialogContainer
anchors.fill: parent
anchors.margins: parent.width*0.05
Image {
id: offOnImage
anchors.left: quitButton.left
anchors.right: quitButton.right
anchors.top: parent.top
height: GameSettings.heightForWidth(width, sourceSize)
source: "images/bt_off_to_on.png"
}
Text {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: offOnImage.bottom
anchors.bottom: quitButton.top
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
font.pixelSize: GameSettings.mediumFontSize
color: GameSettings.textColor
text: qsTr("This application cannot be used without Bluetooth. Please switch Bluetooth ON to continue.")
}
GameButton {
id: quitButton
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: dialogContainer.width * 0.6
height: GameSettings.buttonHeight
onClicked: Qt.quit()
Text {
anchors.centerIn: parent
color: GameSettings.textColor
font.pixelSize: GameSettings.bigFontSize
text: qsTr("Quit")
}
}
}
}
}

9
qml/BottomLine.qml Normal file
View File

@@ -0,0 +1,9 @@
import QtQuick 2.5
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
width: parent.width * 0.85
height: parent.height * 0.05
radius: height*0.5
}

138
qml/Connect.qml Normal file
View File

@@ -0,0 +1,138 @@
import QtQuick 2.5
import Shared 1.0
GamePage {
errorMessage: deviceFinder.error
infoMessage: deviceFinder.info
Rectangle {
id: viewContainer
anchors.top: parent.top
anchors.bottom:
// only BlueZ platform has address type selection
connectionHandler.requiresAddressType ? addressTypeButton.top : searchButton.top
anchors.topMargin: GameSettings.fieldMargin + messageHeight
anchors.bottomMargin: GameSettings.fieldMargin
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - GameSettings.fieldMargin*2
color: GameSettings.viewColor
radius: GameSettings.buttonRadius
Text {
id: title
width: parent.width
height: GameSettings.fieldHeight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: GameSettings.textColor
font.pixelSize: GameSettings.mediumFontSize
text: qsTr("FOUND DEVICES")
BottomLine {
height: 1;
width: parent.width
color: "#898989"
}
}
ListView {
id: devices
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: title.bottom
model: deviceFinder.devices
clip: true
delegate: Rectangle {
id: box
height:GameSettings.fieldHeight * 1.2
width: parent.width
color: index % 2 === 0 ? GameSettings.delegate1Color : GameSettings.delegate2Color
MouseArea {
anchors.fill: parent
onClicked: {
deviceFinder.connectToService(modelData.deviceAddress);
app.showPage("Measure.qml")
}
}
Text {
id: device
font.pixelSize: GameSettings.smallFontSize
text: modelData.deviceName
anchors.top: parent.top
anchors.topMargin: parent.height * 0.1
anchors.leftMargin: parent.height * 0.1
anchors.left: parent.left
color: GameSettings.textColor
}
Text {
id: deviceAddress
font.pixelSize: GameSettings.smallFontSize
text: modelData.deviceAddress
anchors.bottom: parent.bottom
anchors.bottomMargin: parent.height * 0.1
anchors.rightMargin: parent.height * 0.1
anchors.right: parent.right
color: Qt.darker(GameSettings.textColor)
}
}
}
}
GameButton {
id: addressTypeButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: searchButton.top
anchors.bottomMargin: GameSettings.fieldMargin*0.5
width: viewContainer.width
height: GameSettings.fieldHeight
visible: connectionHandler.requiresAddressType // only required on BlueZ
state: "public"
onClicked: state == "public" ? state = "random" : state = "public"
states: [
State {
name: "public"
PropertyChanges { target: addressTypeText; text: qsTr("Public Address") }
PropertyChanges { target: deviceHandler; addressType: AddressType.PublicAddress }
},
State {
name: "random"
PropertyChanges { target: addressTypeText; text: qsTr("Random Address") }
PropertyChanges { target: deviceHandler; addressType: AddressType.RandomAddress }
}
]
Text {
id: addressTypeText
anchors.centerIn: parent
font.pixelSize: GameSettings.tinyFontSize
color: GameSettings.textColor
}
}
GameButton {
id: searchButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: GameSettings.fieldMargin
width: viewContainer.width
height: GameSettings.fieldHeight
enabled: !deviceFinder.scanning
onClicked: deviceFinder.startSearch()
Text {
anchors.centerIn: parent
font.pixelSize: GameSettings.tinyFontSize
text: qsTr("START SEARCH")
color: searchButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
}
}
}

38
qml/GameButton.qml Normal file
View File

@@ -0,0 +1,38 @@
import QtQuick 2.5
import "."
Rectangle {
id: button
color: baseColor
onEnabledChanged: checkColor()
radius: GameSettings.buttonRadius
property color baseColor: GameSettings.buttonColor
property color pressedColor: GameSettings.buttonPressedColor
property color disabledColor: GameSettings.disabledButtonColor
signal clicked()
function checkColor()
{
if (!button.enabled) {
button.color = disabledColor
} else {
if (mouseArea.containsPress)
button.color = pressedColor
else
button.color = baseColor
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
onPressed: checkColor()
onReleased: checkColor()
onClicked: {
checkColor()
button.clicked()
}
}
}

43
qml/GamePage.qml Normal file
View File

@@ -0,0 +1,43 @@
import QtQuick 2.5
import "."
Item {
anchors.fill: parent
property string errorMessage: ""
property string infoMessage: ""
property real messageHeight: msg.height
property bool hasError: errorMessage != ""
property bool hasInfo: infoMessage != ""
function init()
{
}
function close()
{
app.prevPage()
}
Rectangle {
id: msg
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: GameSettings.fieldHeight
color: hasError ? GameSettings.errorColor : GameSettings.infoColor
visible: hasError || hasInfo
Text {
id: error
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
minimumPixelSize: 5
font.pixelSize: GameSettings.smallFontSize
fontSizeMode: Text.Fit
color: GameSettings.textColor
text: hasError ? errorMessage : infoMessage
}
}
}

51
qml/GameSettings.qml Normal file
View File

@@ -0,0 +1,51 @@
pragma Singleton
import QtQuick 2.5
Item {
property int wHeight
property int wWidth
// Colors
readonly property color backgroundColor: "#2d3037"
readonly property color buttonColor: "#202227"
readonly property color buttonPressedColor: "#6ccaf2"
readonly property color disabledButtonColor: "#555555"
readonly property color viewColor: "#202227"
readonly property color delegate1Color: Qt.darker(viewColor, 1.2)
readonly property color delegate2Color: Qt.lighter(viewColor, 1.2)
readonly property color textColor: "#ffffff"
readonly property color textDarkColor: "#232323"
readonly property color disabledTextColor: "#777777"
readonly property color sliderColor: "#6ccaf2"
readonly property color errorColor: "#ba3f62"
readonly property color infoColor: "#3fba62"
// Font sizes
property real microFontSize: hugeFontSize * 0.2
property real tinyFontSize: hugeFontSize * 0.4
property real smallTinyFontSize: hugeFontSize * 0.5
property real smallFontSize: hugeFontSize * 0.6
property real mediumFontSize: hugeFontSize * 0.7
property real bigFontSize: hugeFontSize * 0.8
property real largeFontSize: hugeFontSize * 0.9
property real hugeFontSize: (wWidth + wHeight) * 0.03
property real giganticFontSize: (wWidth + wHeight) * 0.04
// Some other values
property real fieldHeight: wHeight * 0.08
property real fieldMargin: fieldHeight * 0.5
property real buttonHeight: wHeight * 0.08
property real buttonRadius: buttonHeight * 0.1
// Some help functions
function widthForHeight(h, ss)
{
return h/ss.height * ss.width;
}
function heightForWidth(w, ss)
{
return w/ss.width * ss.height;
}
}

194
qml/Measure.qml Normal file
View File

@@ -0,0 +1,194 @@
import QtQuick 2.5
GamePage {
id: measurePage
errorMessage: deviceHandler.error
infoMessage: deviceHandler.info
property real __timeCounter: 0;
property real __maxTimeCount: 60
property string relaxText: qsTr("FAST!\nWhen you are ready, press Start. You have %1s time to increase speed so much as possible.\nGood luck!").arg(__maxTimeCount)
function close()
{
deviceHandler.stopMeasurement();
deviceHandler.disconnectService();
app.prevPage();
}
function start()
{
if (!deviceHandler.measuring) {
__timeCounter = 0;
deviceHandler.startMeasurement()
}
}
function stop()
{
if (deviceHandler.measuring) {
deviceHandler.stopMeasurement()
}
app.showPage("Stats.qml")
}
Timer {
id: measureTimer
interval: 1000
running: deviceHandler.measuring
repeat: true
onTriggered: {
__timeCounter++;
if (__timeCounter >= __maxTimeCount)
measurePage.stop()
}
}
Column {
anchors.centerIn: parent
spacing: GameSettings.fieldHeight * 0.5
Rectangle {
id: circle
anchors.horizontalCenter: parent.horizontalCenter
width: Math.min(measurePage.width, measurePage.height-GameSettings.fieldHeight*4) - 2*GameSettings.fieldMargin
height: width
radius: width*0.5
color: GameSettings.viewColor
Text {
id: hintText
anchors.centerIn: parent
anchors.verticalCenterOffset: -parent.height*0.1
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: parent.width * 0.8
height: parent.height * 0.6
wrapMode: Text.WordWrap
text: measurePage.relaxText
visible: !deviceHandler.measuring
color: GameSettings.textColor
fontSizeMode: Text.Fit
minimumPixelSize: 10
font.pixelSize: GameSettings.mediumFontSize
}
Text {
id: text
anchors.centerIn: parent
anchors.verticalCenterOffset: -parent.height*0.15
font.pixelSize: parent.width * 0.45
text: deviceHandler.speed.toFixed(0)
visible: deviceHandler.measuring
color: GameSettings.textColor
}
Item {
id: minMaxContainer
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width*0.7
height: parent.height * 0.15
anchors.bottom: parent.bottom
anchors.bottomMargin: parent.height*0.16
visible: deviceHandler.measuring
Text {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: deviceHandler.minSpeed.toFixed(0)
color: GameSettings.textColor
font.pixelSize: GameSettings.hugeFontSize
Text {
anchors.left: parent.left
anchors.bottom: parent.top
font.pixelSize: parent.font.pixelSize*0.8
color: parent.color
text: "MIN"
}
}
Text {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: deviceHandler.maxSpeed.toFixed(0)
color: GameSettings.textColor
font.pixelSize: GameSettings.hugeFontSize
Text {
anchors.right: parent.right
anchors.bottom: parent.top
font.pixelSize: parent.font.pixelSize*0.8
color: parent.color
text: "MAX"
}
}
}
Image {
id: bobbycar
anchors.horizontalCenter: minMaxContainer.horizontalCenter
anchors.verticalCenter: minMaxContainer.bottom
width: parent.width * 0.2
height: width
source: "images/logo.png"
smooth: true
antialiasing: true
SequentialAnimation{
id: bobbycarAnim
running: deviceHandler.alive
loops: Animation.Infinite
alwaysRunToEnd: true
PropertyAnimation { target: bobbycar; property: "scale"; to: 1.2; duration: 500; easing.type: Easing.InQuad }
PropertyAnimation { target: bobbycar; property: "scale"; to: 1.0; duration: 500; easing.type: Easing.OutQuad }
}
}
}
Rectangle {
id: timeSlider
color: GameSettings.viewColor
anchors.horizontalCenter: parent.horizontalCenter
width: circle.width
height: GameSettings.fieldHeight
radius: GameSettings.buttonRadius
Rectangle {
height: parent.height
radius: parent.radius
color: GameSettings.sliderColor
width: Math.min(1.0,__timeCounter / __maxTimeCount) * parent.width
}
Text {
anchors.centerIn: parent
color: "gray"
text: (__maxTimeCount - __timeCounter).toFixed(0) + " s"
font.pixelSize: GameSettings.bigFontSize
}
}
}
GameButton {
id: startButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: GameSettings.fieldMargin
width: circle.width
height: GameSettings.fieldHeight
enabled: !deviceHandler.measuring
radius: GameSettings.buttonRadius
onClicked: start()
Text {
anchors.centerIn: parent
font.pixelSize: GameSettings.tinyFontSize
text: qsTr("START")
color: startButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
}
}
}

40
qml/SplashScreen.qml Normal file
View File

@@ -0,0 +1,40 @@
import QtQuick 2.5
import "."
Item {
id: root
anchors.fill: parent
property bool appIsReady: false
property bool splashIsReady: false
property bool ready: appIsReady && splashIsReady
onReadyChanged: if (ready) readyToGo();
signal readyToGo()
function appReady()
{
appIsReady = true
}
function errorInLoadingApp()
{
Qt.quit()
}
Image {
anchors.centerIn: parent
width: Math.min(parent.height, parent.width)*0.6
height: GameSettings.heightForWidth(width, sourceSize)
source: "images/logo.png"
}
Timer {
id: splashTimer
interval: 1000
onTriggered: splashIsReady = true
}
Component.onCompleted: splashTimer.start()
}

49
qml/Stats.qml Normal file
View File

@@ -0,0 +1,49 @@
import QtQuick 2.5
GamePage {
Column {
anchors.centerIn: parent
width: parent.width
Text {
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: GameSettings.hugeFontSize
color: GameSettings.textColor
text: qsTr("RESULT")
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: GameSettings.giganticFontSize*3
color: GameSettings.textColor
text: (deviceHandler.maxSpeed - deviceHandler.minSpeed).toFixed(0)
}
Item {
height: GameSettings.fieldHeight
width: 1
}
StatsLabel {
title: qsTr("MIN")
value: deviceHandler.minSpeed.toFixed(0)
}
StatsLabel {
title: qsTr("MAX")
value: deviceHandler.maxSpeed.toFixed(0)
}
StatsLabel {
title: qsTr("AVG")
value: deviceHandler.avgSpeed.toFixed(1)
}
StatsLabel {
title: qsTr("DISTANCE")
value: deviceHandler.distance.toFixed(3)
}
}
}

32
qml/StatsLabel.qml Normal file
View File

@@ -0,0 +1,32 @@
import QtQuick 2.5
import "."
Item {
height: GameSettings.fieldHeight
width: parent.width
property alias title: leftText.text
property alias value: rightText.text
Text {
id: leftText
anchors.left: parent.left
height: parent.height
width: parent.width * 0.45
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
font.pixelSize: GameSettings.mediumFontSize
color: GameSettings.textColor
}
Text {
id: rightText
anchors.right: parent.right
height: parent.height
width: parent.width * 0.45
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
font.pixelSize: GameSettings.mediumFontSize
color: GameSettings.textColor
}
}

47
qml/TitleBar.qml Normal file
View File

@@ -0,0 +1,47 @@
import QtQuick 2.5
Rectangle {
id: titleBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: GameSettings.fieldHeight
color: GameSettings.viewColor
property var __titles: ["CONNECT", "MEASURE", "STATS"]
property int currentIndex: 0
signal titleClicked(int index)
Repeater {
model: 3
Text {
width: titleBar.width / 3
height: titleBar.height
x: index * width
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: __titles[index]
font.pixelSize: GameSettings.tinyFontSize
color: titleBar.currentIndex === index ? GameSettings.textColor : GameSettings.disabledTextColor
MouseArea {
anchors.fill: parent
onClicked: titleClicked(index)
}
}
}
Item {
anchors.bottom: parent.bottom
width: parent.width / 3
height: parent.height
x: currentIndex * width
BottomLine{}
Behavior on x { NumberAnimation { duration: 200 } }
}
}

BIN
qml/images/bt_off_to_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
qml/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

55
qml/main.qml Normal file
View File

@@ -0,0 +1,55 @@
import QtQuick 2.7
import QtQuick.Window 2.2
import "."
Window {
id: wroot
visible: true
width: 720 * .7
height: 1240 * .7
title: qsTr("Bobbycar")
color: GameSettings.backgroundColor
Component.onCompleted: {
GameSettings.wWidth = Qt.binding(function() {return width})
GameSettings.wHeight = Qt.binding(function() {return height})
}
Loader {
id: splashLoader
anchors.fill: parent
source: "SplashScreen.qml"
asynchronous: false
visible: true
onStatusChanged: {
if (status === Loader.Ready) {
appLoader.setSource("App.qml");
}
}
}
Connections {
target: splashLoader.item
onReadyToGo: {
appLoader.visible = true
appLoader.item.init()
splashLoader.visible = false
splashLoader.setSource("")
appLoader.item.forceActiveFocus();
}
}
Loader {
id: appLoader
anchors.fill: parent
visible: false
asynchronous: true
onStatusChanged: {
if (status === Loader.Ready)
splashLoader.item.appReady()
if (status === Loader.Error)
splashLoader.item.errorInLoadingApp();
}
}
}

1
qml/qmldir Normal file
View File

@@ -0,0 +1 @@
singleton GameSettings 1.0 GameSettings.qml