Commit f4293dbb authored by Holger Kaelberer's avatar Holger Kaelberer
Browse files

balancebox: initial version using Box2D 2.0

Basic dynamics are working well. So far only with dummy levels. Levels
and some graphics improvements will come soon ...
parent 3e8de5df
......@@ -73,7 +73,7 @@ else()
endif()
find_package(Qt5 ${QT_REQUIRED_VERSION} REQUIRED
Qml Quick Gui Multimedia Core Svg Xml XmlPatterns LinguistTools)
Qml Quick Gui Multimedia Core Svg Xml XmlPatterns LinguistTools Sensors)
find_package (KF5 QUIET COMPONENTS
DocTools
......
......@@ -11,6 +11,7 @@ align4-2players
alphabet-sequence
babymatch
babyshapes
balancebox
ballcatch
braille_alphabets
braille_fun
......
import GCompris 1.0
ActivityInfo {
name: "balancebox/Balancebox.qml"
difficulty: 2
icon: "balancebox/balancebox.svg"
author: "Holger Kaelberer <holger.k@elberer.de>"
demo: true
title: qsTr("Keep the balance")
description: qsTr("Practice fine grained movements, balancing a ball/")
// intro: "Click on the word matching the picture."
goal: qsTr("")
prerequisite: "None"
manual: qsTr("Navigate the ball to the goal without making it fall into the holes.")
credit: ""
section: "computer keyboard mobile"
}
/* GCompris - Goal.qml
*
* Copyright (C) 2014 Holger Kaelberer
*
* Authors:
* Holger Kaelberer <holger.k@elberer.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import Box2D 2.0
import "../../core"
import "balancebox.js" as Activity
Item {
id: goal
property alias body: itemBody
property alias world: itemBody.world
Image {
id: goalImage
width: goal.width
height: goal.height
source: Activity.baseUrl + "/door.png"
anchors.centerIn: parent
}
Body {
id: itemBody
target: goal
bodyType: Body.Static
world: physicsWorld
sleepingAllowed: false
fixedRotation: true
fixtures: Circle {
id: goalFixture
categories: Circle.Category3
radius: goalImage.width / 2
density: 0
friction: 0
restitution: 0
sensor: true
}
}
}
/* GCompris - BalanceContact.qml
*
* Copyright (C) 2014 Holger Kaelberer
*
* Authors:
* Holger Kaelberer <holger.k@elberer.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import Box2D 2.0
import "../../core"
import "balancebox.js" as Activity
BalanceItem {
id: item
property var pressed: false
property int orderNum
property alias text: itemText.text
imageSource: pressed ? Activity.baseUrl + "/button-pressed.svg"
: Activity.baseUrl + "/button-normal.svg"
GCText {
id: itemText
anchors.fill: item
anchors.centerIn: item
width: item.width
height: item.height
font.pixelSize: width / 2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
//Component.onCompleted: console.log("XXX: " + width + "/" + height
// + " - " + font.pixelSize);
}
}
/* GCompris - BalanceItem.qml
*
* Copyright (C) 2014 Holger Kaelberer
*
* Authors:
* Holger Kaelberer <holger.k@elberer.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import Box2D 2.0
import "../../core"
Item {
id: item
property alias world: itemBody.world
property alias imageSource: itemImage.source
property alias body: itemBody
property alias bodyType: itemBody.bodyType
property alias linearDamping: itemBody.linearDamping
property alias fixtures: itemBody.fixtures
property alias sensor: itemFixture.sensor
property alias categories: itemFixture.categories
property alias collidesWith: itemFixture.collidesWith
property alias density: itemFixture.density
property alias friction: itemFixture.friction
property alias restitution: itemFixture.restitution
signal beginContact(Item item, Item other)
signal endContact(Item item, Item other)
Image {
id: itemImage
width: item.width
height: item.height
source: item.imageSource
anchors.centerIn: parent
}
Body {
id: itemBody
target: item
bodyType: Body.Static
sleepingAllowed: false
fixedRotation: true
linearDamping: 0
fixtures: Circle {
id: itemFixture
radius: itemImage.width / 2
onBeginContact: item.beginContact(getBody().target, other.getBody().target)
onEndContact: item.endContact(getBody().target, other.getBody().target)
}
}
}
/* GCompris - balance.qml
*
* Copyright (C) 2014 Holger Kaelberer <holger.k@elberer.de>
*
* Authors:
* Holger Kaelberer <holger.k@elberer.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.1
import QtSensors 5.0
import QtGraphicalEffects 1.0
import GCompris 1.0
import Box2D 2.0
import "../../core"
import "balancebox.js" as Activity
ActivityBase {
id: activity
onStart: focus = true
onStop: {}
Keys.enabled: ApplicationInfo.isMobile ? false : true
Keys.onPressed: Activity.processKeyPress(event.key)
Keys.onReleased: Activity.processKeyRelease(event.key)
pageComponent: Rectangle {
id: background
anchors.fill: parent
color: "#ABCDEF"
signal start
signal stop
Component.onCompleted: {
activity.start.connect(start)
activity.stop.connect(stop)
}
QtObject {
id: items
property Item main: activity.main
property alias background: background
property alias bar: bar
property alias bonus: bonus
property alias tilt: tilt
property alias timer: timer
property alias ball: ball
property alias mapWrapper: mapWrapper
property int cellSize: mapWrapper.length / Math.min(mapWrapper.rows, mapWrapper.columns)
property int wallSize: cellSize / 5
property var world: physicsWorld
property alias keyboardTimer: keyboardTimer
property var ballType: Fixture.Category1
property var wallType: Fixture.Category2
property var holeType: Fixture.Category3
property var goalType: Fixture.Category4
property var buttonType: Fixture.Category5
}
onStart: { Activity.start(items) }
onStop: { Activity.stop() }
Image {
id: mapWrapper
property double margin: 20
property int columns: 0
property int rows: 0
source: Activity.baseUrl + "/woodbackground.png"
fillMode: Image.Tile
property double length: Math.min(background.height -
2*mapWrapper.margin, background.width - 2*mapWrapper.margin);
width: length
height: length
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
// right:
Wall {
id: rightWall
width: items.wallSize
height: parent.height + items.wallSize
anchors.left: mapWrapper.right
anchors.leftMargin: - items.wallSize/2
anchors.top: parent.top
anchors.topMargin: -items.wallSize/2
shadow: true
shadowHorizontalOffset: items.tilt.yRotation
shadowVerticalOffset: items.tilt.xRotation
}
// bottom:
Wall {
id: bottomWall
width: parent.width + items.wallSize
height: items.wallSize
anchors.left: mapWrapper.left
anchors.leftMargin: - items.wallSize/2
anchors.top: parent.bottom
anchors.topMargin: -items.wallSize/2
shadow: true
shadowHorizontalOffset: items.tilt.yRotation
shadowVerticalOffset: items.tilt.xRotation
}
// top:
Wall {
id: topWall
width: parent.width + items.wallSize
height: items.wallSize
anchors.left: mapWrapper.left
anchors.leftMargin: - items.wallSize/2
anchors.top: parent.top
anchors.topMargin: -items.wallSize/2
shadow: true
shadowHorizontalOffset: items.tilt.yRotation
shadowVerticalOffset: items.tilt.xRotation
}
// left:
Wall {
id: leftWall
width: items.wallSize
height: parent.height + items.wallSize
anchors.left: mapWrapper.left
anchors.leftMargin: - items.wallSize/2
anchors.top: parent.top
anchors.topMargin: -items.wallSize/2
shadow: true
shadowHorizontalOffset: items.tilt.yRotation
shadowVerticalOffset: items.tilt.xRotation
}
BalanceItem {
id: ball
world: physicsWorld
imageSource: Activity.baseUrl + "/ball.svg"
scale: 1.0
z: 1
categories: items.ballType
collidesWith: items.wallType | items.holeType | items.goalType
| items.buttonType
density: 1
friction: Activity.friction
linearDamping: Activity.friction
restitution: Activity.restitution
bodyType: Body.Dynamic
//Component.onCompleted: console.log("XXX ball: " + width + "/" + height
// + " - " + parent.width + "/" + parent.height);
Behavior on scale {
NumberAnimation {
id: fallAnimation
duration: 1000
}
}
onBeginContact: {
//console.log("ZZZ: contact with " + other.categories);
if (other.categories !== items.wallType)
Activity.addBallContact(other);
}
onEndContact: {
if (other.categories !== items.wallType)
Activity.removeBallContact(other);
}
}
World {
id: physicsWorld
gravity: Qt.point(0, 0) // we calculate acceleration ourselves
pixelsPerMeter: Activity.pixelsPerMeter // default: 32
timeStep: Activity.step/1000 // default: 1/60
}
DebugDraw {
id: debugDraw
world: physicsWorld
visible: Activity.debugDraw
z: 1
}
}
Timer {
id: timer
interval: Activity.step;
running: false;
repeat: true
onTriggered: Activity.moveBall()
}
Item {
id: tilt
property double xRotation: 0
property double yRotation: 0
onXRotationChanged: {
if (xRotation > 90)
xRotation = 90;
else if (xRotation < -90)
xRotation = -90;
//console.log("xRotation changed to " + xRotation);
}
onYRotationChanged: {
//console.log("yRotation changed to " + yRotation);
if (yRotation > 90)
yRotation = 90;
else if (yRotation < -90)
yRotation = -90;
//console.log("xRotation changed to " + xRotation);
}
TiltSensor {
id: tiltSensor
active: ApplicationInfo.isMobile ? true : false
onReadingChanged: {
tilt.xRotation = reading.xRotation;
tilt.yRotation = reading.yRotation;
tiltText.text = "X/Y Rotation: "
+ tiltSensor.reading.xRotation
+ "/" + tiltSensor.reading.yRotation
}
}
}
Item {
id: textWrapper
anchors.left: parent.left
anchors.top: parent.top
width: parent.width
height: parent.height / 3
visible: Activity.debugDraw
Text {
id: tiltText
anchors.left: parent.left
anchors.top: parent.top
text: "X/Y Rotation: " + tilt.xRotation + "/" + tilt.yRotation
font.pointSize: 12
}
Text {
id: posText
anchors.left: parent.left
anchors.top: tiltText.bottom
text: "X/Y = " + ball.x + "/" + ball.y
font.pointSize: 12
}
}
DialogHelp {
id: dialogHelp
onClose: home()
}
Bar {
id: bar
content: BarEnumContent { value: help | home | level }
onHelpClicked: {
displayDialog(dialogHelp)
}
onPreviousLevelClicked: Activity.previousLevel()
onNextLevelClicked: Activity.nextLevel()
onHomeClicked: activity.home()
}
Bonus {
id: bonus
Component.onCompleted: {
win.connect(Activity.nextLevel);
loose.connect(Activity.initLevel);
}
}
Timer {
id: keyboardTimer
interval: Activity.keyboardTimeStep;
running: false
repeat: false
onTriggered: Activity.keyboardHandler()
}
}
}
/* GCompris - Ball.qml
*
* Copyright (C) 2014 Holger Kaelberer
*
* Authors:
* Holger Kaelberer <holger.k@elberer.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import GCompris 1.0
import Box2D 2.0
import "../../core"
import "balancebox.js" as Activity
Item {
id: ball
property alias body: itemBody
property alias world: itemBody.world
z: 1
Image {
id: ballImage
width: ball.width
height: ball.height
source: Activity.baseUrl + "/ball.svg"
anchors.centerIn: parent
}
Body {
id: itemBody
target: ball
bodyType: Body.Dynamic
world: physicsWorld
sleepingAllowed: false
fixedRotation: true
bullet: true
//onLinearVelocityChanged: {
// console.log("linVChanged: " + linearVelocity.x + "/" + linearVelocity.y)
//}
fixtures: Circle {
id: circleFix
radius: ballImage.width / 2
//x: 100