Here is a minimal version of a code revealing the problem which is:
Moving the racket when playing the game on the Desktop kit (Windows) doesn't affect the speed of ball's movement but when run on an Android device, moving the racket affects the speed of ball's movement as though their movements have been tied together.
What solution is there for that, please?
main.qml:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 720
height: 620
title: qsTr("Movement Test")
Rectangle {
id: table
anchors.fill: parent
color: "gray"
Rectangle {
id: ball
property double xincrement: Math.random() + 0.5
property double yincrement: Math.random() + 0.5
width: 15
height: width
radius: width / 2
color: "white"
x: 300; y: 300
}
Racket {
id: myRacket
x: table.width - 50
y: table.height/3
color: "blue"
}
Timer {
interval: 5; repeat: true; running: true
function collision() {
if((ball.x + ball.width >= myRacket.x &&
ball.x < myRacket.x + myRacket.width) &&
(ball.y + ball.height >= myRacket.y &&
ball.y <= myRacket.y + myRacket.height))
return true
return false
}
onTriggered: {
if(ball.x + ball.width >= table.width)
running = false
else if(ball.x <= 0)
ball.xincrement *= -1
else if (collision())
ball.xincrement *= -1
ball.x = ball.x + (ball.xincrement * 1.5);
ball.y = ball.y + (ball.yincrement * 1.5);
if(ball.y <= 0 || ball.y + ball.height >= table.height)
ball.yincrement *= -1
}
}
}
}
Racket.qml:
import QtQuick 2.9
Rectangle {
id: root
width: 15; height: 65
property int oldY: y
property bool yUwards: false
property bool yDwards: false
onYChanged: {
if(y > oldY) yDwards = true
else if (y < oldY) yUwards = true
oldY = y
}
MouseArea {
anchors.fill: parent
anchors.margins: -root.height
drag.target: root
focus: true
hoverEnabled: true
pressAndHoldInterval: 0
drag.axis: Drag.YAxis
drag.minimumY: table.y
drag.maximumY: table.height - root.height - 10
}
}
Qt tries to render every ~16-17ms. If you set your timer to 5ms, it will try to trigger it 3 - 4 times per frame.
Other things that are happening might hinder it from keeping that pace. If the device is less powerfull this effect might be more visible than on other devices.
To see, whether the Timer achievs the set rate, you can print the current ms part of the time with:
console.log(Qt.formatTime(new Date(), "zzz"))
The logged values shall be 5 appart, if the Timer achives full speed. It will be different from 5 if it doesn't.
The easiest way would be to set a target location where the ball will move to, and animate that movement (using a Animation-Type). The Animation shall take care, that the movement speed will be kept, even in cases where the frame rate might drop.
If you want to do it manually, instead of using the timer, you should use the onAfterRendering-slot (I think of Window). This will be triggered when ever something has moved on the screen and triggered rendering. This is also the ideal moment to check collisions.
You then need to calculate the new position depnding on its velocity, the current position and the elapsed time. Latter you can get either from the JS Date()-object, or you expose it somehow form C++ using a QElapsedTimer
Related
I tried using: "react-native-sensors" library
https://github.com/react-native-sensors/react-native-sensors/blob/master/docs/API.md#orientation-observableqx-number-qy-number-qz-number-qw-number-pitch-number-roll-number-yaw-number-timestamp-string
import { orientation } from "react-native-sensors";
const subscription = orientation.subscribe(({ qx, qy, qz, qw, pitch, roll, yaw, timestamp }) =>
console.log({ qx, qy, qz, qw, pitch, roll, yaw, timestamp })
);
I tried using their given pitch,roll,yaw and converted from radian_to_degress still not the same values as the emulator.
They also provide a quaternion to Angles function
function quaternionToAngles(q) {
let data = q;
let ysqr = data.y * data.y;
let t0 = -2.0 * (ysqr + data.z * data.z) + 1.0;
let t1 = +2.0 * (data.x * data.y + data.w * data.z);
let t2 = -2.0 * (data.x * data.z - data.w * data.y);
let t3 = +2.0 * (data.y * data.z + data.w * data.x);
let t4 = -2.0 * (data.x * data.x + ysqr) + 1.0;
t2 = t2 > 1.0 ? 1.0 : t2;
t2 = t2 < -1.0 ? -1.0 : t2;
const toDeg = 180 / Math.PI;
const euler = {};
euler.pitch = Math.asin(t2) * toDeg;
euler.roll = Math.atan2(t3, t4) * toDeg;
euler.yaw = Math.atan2(t1, t0) * toDeg;
return euler;
}
The function gives wrong results for roll,yaw and pitch. I dont want to dive into the Math of this. google searching gives 100x different equations.
Im searching for simple equations to get the Z,Y,X Rotations of my Device just like the Android Emulator provides. If Z-Rot Value is "90"(on emulator) my program should also receive the same value. I have access to gyrometer,barometer,accelometer etc.
PS: I wasted way to much time on google for a problem that should be solvable with a copy paste after one google search so I hope this question will help.
You can use the 'expo-sensors' package.
To answer your question, you can use DeviceMotion API or Gyroscope API.
Gyroscope can be used directly from their documentation, so I am providing an example of DeviceMotion.
import { DeviceMotion } from 'expo-sensors';
....
....
componentDidMount(){
DeviceMotion.addListener((devicemotionData) => {
console.log(devicemotionData);
// call to setState reflecting the deviceMotion changes.
....
....
});
// set update delay in ms
DeviceMotion.setUpdateInterval(100);
}
componentWillUnmount(){
DeviceMotion.removeAllListeners();
}
....
....
the object received by the listener will be in the following format:
{
"acceleration": {
"z": -0.0005327463150024414,
"y": 0.0034074783325195312,
"x": 0.0005932972417213023
},
"accelerationIncludingGravity": {
"z": -0.8134145140647888,
"y": -9.769495010375977,
"x": 0.0011865944834426045
},
"orientation": 0,
"rotation": {
"gamma": -0.000039085680327843875,
"alpha": 0.00004289219214115292,
"beta": 1.4878977537155151
},
"rotationRate": { "gamma": 0, "alpha": 0, "beta": 0 }
}
for more examples of usage, you can refer to this article
My android device shows a different kind of measure. maybe this could help you get deductions:
alpha range : 0 to 360
beta range : -180 to 180
gamma range : in anticlockwise motion,
0 to 90 for 1st quadrant,
90 to 0 for 2nd quadrant,
0 to -90 for 3rd quadrant,
-90 to 0 for 4th quadrant
So I was implementing a simple image viewer and wanted to implement pinch to zoom inside a Flickable. After looking at several example, I came across this and thought I could do something similar. However, when I tested the code (Qt 5.13 on Android) it doesn't work as expected. The problem is that when the image is zoomed, the PinchArea stops working and the user is unable to zoom out while the Flickable still allows to "flick" the image (at that zoom level). Here is the code, which I couldn't find any mistake with:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Flickable Image Zoom Example")
Page {
id: imagePage
property string imgUrl: "qrc:/messi.jpg"
property string strHpTitle: ""
property string strThumbnailUrl: ""
anchors.fill: parent
Flickable {
id: imageFlickable
anchors.fill: parent
contentWidth: imageContainer.width; contentHeight: imageContainer.height
clip: true
onHeightChanged: if (imagePreview.status === Image.Ready) imagePreview.fitToScreen();
Item {
id: imageContainer
width: Math.max(imagePreview.width * imagePreview.scale, imageFlickable.width)
height: Math.max(imagePreview.height * imagePreview.scale, imageFlickable.height)
Image {
id: imagePreview
property real prevScale
function fitToScreen() {
scale = Math.min(imageFlickable.width / width, imageFlickable.height / height, 1)
pinchArea.minScale = scale
prevScale = scale
}
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
cache: false
asynchronous: true
source: imagePage.imgUrl
sourceSize.height: 1000;
smooth: !imageFlickable.moving
onStatusChanged: {
if (status == Image.Ready) {
fitToScreen()
}
}
onScaleChanged: {
if ((width * scale) > imageFlickable.width) {
var xoff = (imageFlickable.width / 2 + imageFlickable.contentX) * scale / prevScale;
imageFlickable.contentX = xoff - imageFlickable.width / 2
}
if ((height * scale) > imageFlickable.height) {
var yoff = (imageFlickable.height / 2 + imageFlickable.contentY) * scale / prevScale;
imageFlickable.contentY = yoff - imageFlickable.height / 2
}
prevScale = scale
}
}
}
PinchArea {
id: pinchArea
property real minScale: 1.0
property real maxScale: 3.0
anchors.fill: parent
pinch.target: imagePreview
pinch.minimumScale: minScale
pinch.maximumScale: maxScale
pinch.dragAxis: Pinch.XandYAxis
onPinchFinished: {
imageFlickable.returnToBounds()
}
}
}
}
}
Ok so I found the solution to this problem. The issue (I think) is due to a bug. For some strange reason, the Flickable in my code steals touch events from the PinchArea so the pinching touch events are ignored and passed onto the Flickable. In order to solve the problem, I found a workaround which consists in creating a MouseArea inside the PinchArea, this allows the PinchArea to receive the touch events so that the Flickable doesn't steal them. So with the workaround my code has the following structure:
Flickable{
PinchArea{
MouseArea{ anchors.fill: parent} // this is the workaround
}
Item{}
}
I have reported the bug here and uploaded a small video showing the cause.
A picture speaks better than words (black rectangle in red ApplicationWindow):
Notice the red unfilled areas on top and right side. The right side red color may be hard to notice but it is there! I want the rectangle which I have colored in black to fill the entire Application Window. See the code:
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
ApplicationWindow {
id: window
visible: true
/* Developing mobile apps you don’t need to set width
and height, because the ApplicationWindow always grabs
the total available space.
*/
//width: 640
//height: 480
color: "#ff0000" // Red color
/* For some reasons i want this Rectangle here
* and it MUST fill the entire window but I notice
* a pixel or two line on top and right of the
* screen.
*/
Rectangle {
id: page
width: window.width; height: window.height
//anchors.fill: parent // same output
color: "#000000" // Black color
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
I don't know what I'm missing here :(
Please share a solution/workaround to this problem.
I also tried this but still the same output:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
Window {
id: window
visible: true
height: Screen.height
width: Screen.width
/* Developing mobile apps you don’t need to set width
and height, because the ApplicationWindow always grabs
the total available space.
*/
//width: 640
//height: 480
color: "#ff0000" // Red color
/* For some reasons i want this Rectangle here
* and it MUST fill the entire window but I notice
* a pixel or two line on top and right of the
* screen.
*/
Rectangle {
id: page
width: window.width; height: window.height
//anchors.fill: parent // same output
color: "#000000" // Black color
}
}
I noticed that commenting following line in main.cpp resolves the issue BUT now all the widgets which I want to show in the UI look really small..! They look fine in the small-screen devices while small in large-screen devices. :(
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
Any solution for this?
EDIT:
This is a confirmed bug which has been reported HERE since almost an year! . If you are facing the same issue then please login to https://bugreports.qt.io/ and Vote THIS bug.
Well, you could use your "own" dp calculation.
main.cpp
int density = 0;
float logicalDensity = 0;
float yDpi = 0; float xDpi = 0;
#if defined(ANDROID)
QAndroidJniObject qtActivity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
QAndroidJniObject resources = qtActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;");
QAndroidJniObject displayMetrics = resources.callObjectMethod("getDisplayMetrics", "()Landroid/util/DisplayMetrics;");
density = displayMetrics.getField<int>("densityDpi");
logicalDensity = displayMetrics.getField<float>("density");
yDpi = displayMetrics.getField<float>("ydpi");
xDpi = displayMetrics.getField<float>("xdpi");
qDebug() << "Native Android Call =>>> | Logical Density: " << logicalDensity << " | DensityDPI: " << density << " | " << "++++++++++++++++++++++++";
#endif
[...]
// Set Android pixel data for QML context
engine.rootContext()->setContextProperty("densityData", density);
engine.rootContext()->setContextProperty("logicalDensityData",logicalDensity);
engine.rootContext()->setContextProperty("xDpiData",xDpi);
engine.rootContext()->setContextProperty("yDpiData",yDpi);
For your ApplicationWindow component add this:
main.qml
Component.onCompleted: {
Units.pixelDensity = Qt.binding(function() {
if (Qt.platform.os === "android") {
return densityData / 25.4; // densityData is per inch but we need per mm
}
return Screen.pixelDensity
});
function calculateDiagonal() {
if (Qt.platform.os === "android") {
return Math.sqrt(Math.pow(Screen.width, 2) +
Math.pow(Screen.height, 2)) / densityData;
}
return Math.sqrt(Math.pow(Screen.width, 2) +
Math.pow(Screen.height, 2)) / (Screen.pixelDensity * 25.4);
}
Units.multiplier = Qt.binding(function() {
var diagonal = calculateDiagonal();
Device.diagonal = diagonal;
var baseMultiplier = 1;
if (diagonal >= 3.5 && diagonal < 5.1) { //iPhone 1st generation to phablet
return 0.8;
} else if (diagonal >= 5.1 && diagonal < 6.5) {
return 1;
} else if (diagonal >= 6.5 && diagonal < 15.1) {
return baseMultiplier;
} else if (diagonal >= 15.1 && diagonal < 29) {
return 1.4 * baseMultiplier;
} else if (diagonal >= 29 && diagonal < 92) {
return 1.4 * baseMultiplier;
} else {
return 1.4 * baseMultiplier;
}
});
Device.type = Qt.binding(function () {
var diagonal = calculateDiagonal();
Device.diagonal = diagonal;
if (diagonal >= 3.5 && diagonal < 5) { //iPhone 1st generation to phablet
return Device.phone;
} else if (diagonal >= 5 && diagonal < 7.2) {
return Device.phone;
} else if (diagonal >= 7.2 && diagonal < 15.1) {
return Device.tablet;
} else if (diagonal >= 15.1 && diagonal < 29) {
return Device.desktop;
} else if (diagonal >= 29 && diagonal < 92) {
return Device.tv;
} else {
return Device.unknown;
}
});
// Nasty hack because singletons cannot import the module they were declared in, so
// the grid unit cannot be defined in either Device or Units, because it requires both.
// See https://bugreports.qt.io/browse/QTBUG-39703
Units.gridUnit = Qt.binding(function() {
return Device.type === Device.phone || Device.type === Device.phablet
? Units.dp(48) : Device.type == Device.tablet ? Units.dp(56) : Units.dp(64)
});
}
Units.qml
Object {
id: units
/*!
\internal
This holds the pixel density used for converting millimeters into pixels. This is the exact
value from \l Screen:pixelDensity, but that property only works from within a \l Window type,
so this is hardcoded here and we update it from within \l ApplicationWindow
*/
property real pixelDensity: 4.46
property real multiplier: 1.4 //default multiplier, but can be changed by user
/*!
This is the standard function to use for accessing device-independent pixels. You should use
this anywhere you need to refer to distances on the screen.
*/
function dp(number) {
return Math.round(number*((pixelDensity*25.4)/160)*multiplier);
}
function gu(number) {
return number * gridUnit
}
property int gridUnit: dp(64)
}
Device.qml
import QtQuick 2.0
pragma Singleton
/*!
\qmltype Device
\inqmlmodule Material 0.1
\brief A singleton that provides information about the current device.
*/
Object {
id: device
//some kind of enum, by screen size
property int type: desktop
property int diagonal: -1
readonly property int phone: 0
readonly property int phablet: 1
readonly property int tablet: 2
readonly property int desktop: 3
readonly property int tv: 4
readonly property int unknown: 5 //it's either bigger than tv or smaller than phone
readonly property string name: {
switch (type) {
case 0:
return "phone";
case 1:
return "phablet";
case 2:
return "tablet";
case 3:
return "computer";
case 4:
return "TV";
case 5:
return "device";
}
}
readonly property string iconName: {
switch (type) {
case 0:
return "hardware/smartphone";
case 1:
return "hardware/tablet";
case 2:
return "hardware/tablet";
case 3:
return "hardware/desktop_windows";
case 4:
return "hardware/tv";
case 5:
return "hardware/computer";
}
}
readonly property bool isMobile: type == phone || type == phablet || type == tablet
}
Units and Device components are taken from here qml-material project "https://github.com/papyros/qml-material"
After adding this you should be able to wrap every pixel statement into width: Units.dp(30)
I have an obj pause controller. It used to work when using up/down arrow then enter to click something in the pause menu. BUT how exactly do I get it use left mouse clicks (touch screen) instead of the enter key. I have this code as my enter key.
if (interest == "resume")
{
instance_destroy();
}
else if (interest == "levels")
{
room_goto(worlds);
}
else if (interest == "main_menu")
{
room_goto(main);
}
And this in my draw gui
draw_sprite(background, 0, 640, 360);
draw_sprite(spr_border, 0, 640, 360);
if (interest == "resume")
{
draw_sprite(spr_resume, 0, 640, 360 - 100);
draw_sprite(spr_levels, 0, 640, 360);
draw_sprite(spr_main_menu, 0, 640, 360 + 100);
}
else if (interest == "levels")
{
draw_sprite(spr_resume, 0, 640, 360 - 100);
draw_sprite(spr_levels, 0, 640, 360);
draw_sprite(spr_main_menu, 0, 640, 360 + 100);
}
else if (interest == "main_menu")
{
draw_sprite(spr_resume, 0, 640, 360 - 100);
draw_sprite(spr_levels, 0, 640, 360);
draw_sprite(spr_main_menu, 0, 640, 360 + 100);
}
I tried using this in my step event to get the clicking (touching) to work but its not working at all (nothing activates when touching or clicking). Is this even right?
if(device_mouse_check_button_released(0, mb_left)){
if (device_mouse_x(0) > 640 && device_mouse_x(0) < 640 + sprite_get_width(spr_resume)
&& device_mouse_y(0) > 260 && device_mouse_y(0) < 260 + sprite_get_height(spr_resume)){
//RESUME IS TOUCHED
}
}
if(device_mouse_check_button_released(0, mb_left)){
if (device_mouse_x(0) > 640 && device_mouse_x(0) < 640 + sprite_get_width(spr_levels)
&& device_mouse_y(0) > 260 && device_mouse_y(0) < 260 + sprite_get_height(spr_levels)){
}
}
if(device_mouse_check_button_released(0, mb_left)){
if (device_mouse_x(0) > 640 && device_mouse_x(0) < 640 + sprite_get_width(spr_main_menu)
&& device_mouse_y(0) > 260 && device_mouse_y(0) < 260 + sprite_get_height(spr_main_menu)){
}
}
I see a number of problems with your code that you will all need to address for anything to work:
Your interest variable is checked for changes, but nowhere is it actually changed (I would expect it to be changed in your step event script, but it doesn't do anything).
Your draw gui script draws exactly the same thing for three different values of interest. There's no visible change whatsoever.
You jump to a new room and display the selected interest at the same time (when releasing the selection), which means you'll never see the selection. You should a) draw the selected interest on press and b) jump to the room on release.
Furthermore, keep in mind that more than one finger could touch the screen simultaneously -you're only checking for the first touch where there can be 5.
A last suggestion in the GameMaker context is to use objects to check for finger press and release events (the mouse events translate as finger events on touch screens) rather than scanning screen areas for presses. It's what makes GameMaker so much easier to use as it automatically checks the instance's sprite area for collisions, a built-in behavior.
I have a map with overlays which i want to cache -
on each place the user visited on the map (which is a rectangle area) - i check if i have a cache of the overlays that reside in this rectangle .
In order to improve caching (so if the user was previously on the same rectangle,except that now he is a few meters away from the previous rectangle) - i want to "round" the coordinates.
This way, each time the user is in a rectange - i check if this rectangle is similar to previously cached rectangles and if so i bring the cached result .
Also, if the user is zoomed out and his rectangle is contained within a bigger (previously cached) rectangle - then I also can use the cached rectangle.
Any suggestions ?
If you're just looking at how to group the coordinates, decide on the maximum difference between coordinates in x and y or latitude and longtitude you want. Then there are two ways you can go about grouping them. The first is easier, but it will be slow if you have many points.
Say we have a data structure called cachedPoints, a max distance between related points called maxdistance and a new point we're trying to check to see if it's close to the other called point.
for each cachedPoint in cachedPoints
{
if (point.x - cachedPoint.x < maxdistance)
{
if (point.y - cachedPoint.y < maxdistance)
{
cachedPoint.incrementvisits();
}
}
}
the other way is to use a data structure sorted by x or latitude, and then search to see if there is a cachedpoint with an x or latitude within maxdistance of point, then check the y or longtitude. It'd be a bit faster, but it would take some kind of a hash to implement and adds a bunch of complexity you may not need.
Hopefully that's what you're asking.
If you set up a data structure like:
var a = { 'scales' : [50, 100, 200, 400, 1000],
'cachedRects': [{'location': 'rect-large-1234-5678.png', x: 1234, y: 5678, scale: 3}
{'location': 'rect-small-1240-5685.png', x: 1240, y: 5685, scale: 1} ]
}
you can use the modulo function to do this:
var currentx = GetCurrentX();
var currenty = GetCurrentY();
var currentScale = GetCurrentScale();
var rectFound = false;
foreach(rect in a.cachedRects) {
if (rect.scale === currentScale
&& currentx % a.scales[currentScale] === rect.x
&& currenty % a.scales[currentScale] === rect.y) {
rectFound = true;
useOverlay(rect);
break;
}
}
if(!rectFound) {
//could loop again for a larger rectangle of a lower scale.
}
the above may or may not turn out to be valid JS - I've not tried to run it. I hope you get the gist, anyway.
Hey You can Add Marker in Google map v2 in android.
Here i give code for add marker
MarkerOptions mOpt = new MarkerOptions();
mOpt.position(new LatLng(userHstry.getMyLatlng().latitude, userHstry.getMyLatlng().longitude)); // map.clear();
mOpt.title("Address : " + userHstry.getAddress()).snippet("Date : " + userHstry.getDate() + " , Time : " + userHstry.getTime());
map.addMarker(mOpt);