441 lines
13 KiB
C++
441 lines
13 KiB
C++
// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
|
|
|
#include "pingpong.h"
|
|
#include <QDebug>
|
|
#include <QCoreApplication>
|
|
|
|
PingPong::PingPong()
|
|
{
|
|
m_timer = new QTimer(this);
|
|
connect(m_timer, &QTimer::timeout, this, &PingPong::update);
|
|
}
|
|
|
|
PingPong::~PingPong()
|
|
{
|
|
m_timer->stop();
|
|
delete socket;
|
|
delete discoveryAgent;
|
|
}
|
|
|
|
void PingPong::startGame()
|
|
{
|
|
m_resultLeft = m_resultRight = 0;
|
|
emit resultChanged();
|
|
m_showDialog = false;
|
|
emit showDialogChanged();
|
|
//! [Start the game]
|
|
m_timer->start(10);
|
|
//! [Start the game]
|
|
}
|
|
|
|
void PingPong::update()
|
|
{
|
|
QByteArray size;
|
|
// Server is only updating the coordinates
|
|
//! [Updating coordinates]
|
|
if (m_role == 1) {
|
|
checkBoundaries();
|
|
m_ballY += m_speedy;
|
|
m_ballX += m_speedx;
|
|
|
|
size.setNum(m_ballX);
|
|
size.append(' ');
|
|
QByteArray size1;
|
|
size1.setNum(m_ballY);
|
|
size.append(size1);
|
|
size.append(' ');
|
|
size1.setNum(m_leftBlockY);
|
|
size.append(size1);
|
|
size.append(" \n");
|
|
socket->write(size.constData());
|
|
emit ballChanged();
|
|
}
|
|
else if (m_role == 2) {
|
|
size.setNum(m_rightBlockY);
|
|
size.append(" \n");
|
|
socket->write(size.constData());
|
|
}
|
|
//! [Updating coordinates]
|
|
}
|
|
|
|
void PingPong::updateBall(const float &bX, const float &bY)
|
|
{
|
|
m_ballX = bX;
|
|
m_ballY = bY;
|
|
}
|
|
|
|
void PingPong::updateLeftBlock(const float &lY)
|
|
{
|
|
m_leftBlockY = lY;
|
|
}
|
|
|
|
void PingPong::updateRightBlock(const float &rY)
|
|
{
|
|
m_rightBlockY = rY;
|
|
}
|
|
|
|
void PingPong::checkBoundaries()
|
|
{
|
|
float ballWidth = 1.f / 27;
|
|
float blockSize = 1.f / 27;
|
|
float blockHeight = 1.f / 5;
|
|
|
|
float ballCenterY = m_ballY + ballWidth / 2.;
|
|
//! [Checking the boundaries]
|
|
if (((m_ballX + ballWidth) > (1. - blockSize)) && (ballCenterY < (m_rightBlockY + blockHeight))
|
|
&& (ballCenterY > m_rightBlockY)) {
|
|
// right paddle collision
|
|
// simulating paddle surface to be a quarter of a circle
|
|
float paddlecenter = m_rightBlockY + blockHeight / 2.;
|
|
float relpos = (ballCenterY - paddlecenter) / (blockHeight / 2.); // [-1 : 1]
|
|
float surfaceangle = M_PI_4 * relpos;
|
|
|
|
// calculation of surface normal
|
|
float normalx = -cos(surfaceangle);
|
|
float normaly = sin(surfaceangle);
|
|
|
|
// calculation of surface tangent
|
|
float tangentx = sin(surfaceangle);
|
|
float tangenty = cos(surfaceangle);
|
|
|
|
// calculation of tangentialspeed
|
|
float tangentialspeed = tangentx * m_speedx + tangenty * m_speedy;
|
|
// calculation of normal speed
|
|
float normalspeed = normalx * m_speedx + normaly * m_speedy;
|
|
|
|
// speed increase of 10%
|
|
normalspeed *= 1.1f;
|
|
|
|
if (normalspeed < 0) { // if we are coming from the left. To avoid double reflections
|
|
m_speedx = tangentialspeed * tangentx - normalspeed * normalx;
|
|
m_speedy = tangentialspeed * tangenty - normalspeed * normaly;
|
|
}
|
|
} else if ((m_ballX < blockSize) && (ballCenterY < (m_leftBlockY + blockHeight))
|
|
&& (ballCenterY > m_leftBlockY)) {
|
|
// left paddle collision
|
|
// simulating paddle surface to be a quarter of a circle
|
|
float paddlecenter = m_leftBlockY + blockHeight / 2.;
|
|
float relpos = (ballCenterY - paddlecenter) / (blockHeight / 2.); // [-1 : 1]
|
|
float surfaceangle = M_PI_4 * relpos;
|
|
|
|
// calculation of surface normal
|
|
float normalx = cos(surfaceangle);
|
|
float normaly = sin(surfaceangle);
|
|
|
|
// calculation of surface tangent
|
|
float tangentx = -sin(surfaceangle);
|
|
float tangenty = cos(surfaceangle);
|
|
|
|
// calculation of tangentialspeed
|
|
float tangentialspeed = tangentx * m_speedx + tangenty * m_speedy;
|
|
// calculation of normal speed
|
|
float normalspeed = normalx * m_speedx + normaly * m_speedy;
|
|
|
|
// speed increase of 10%
|
|
normalspeed *= 1.1f;
|
|
|
|
if (normalspeed < 0) { // if we are coming from the left. To avoid double reflections
|
|
m_speedx = tangentialspeed * tangentx - normalspeed * normalx;
|
|
m_speedy = tangentialspeed * tangenty - normalspeed * normaly;
|
|
}
|
|
} else if (m_ballY < 0) {
|
|
m_speedy = std::abs(m_speedy);
|
|
} else if (m_ballY + ballWidth > 1.f) {
|
|
m_speedy = -std::abs(m_speedy);
|
|
}
|
|
//! [Checking the boundaries]
|
|
else if ((m_ballX + ballWidth) > 1.f) {
|
|
m_resultLeft++;
|
|
m_speedx = -0.002f;
|
|
m_speedy = -0.002f;
|
|
m_ballX = 0.5f;
|
|
m_ballY = 0.9f;
|
|
|
|
checkResult();
|
|
QByteArray result;
|
|
result.append("result ");
|
|
QByteArray res;
|
|
res.setNum(m_resultLeft);
|
|
result.append(res);
|
|
result.append(' ');
|
|
res.setNum(m_resultRight);
|
|
result.append(res);
|
|
result.append(" \n");
|
|
socket->write(result);
|
|
qDebug() << result;
|
|
emit resultChanged();
|
|
} else if (m_ballX < 0) {
|
|
m_resultRight++;
|
|
m_speedx = 0.002f;
|
|
m_speedy = -0.002f;
|
|
m_ballX = 0.5f;
|
|
m_ballY = 0.9f;
|
|
|
|
checkResult();
|
|
QByteArray result;
|
|
result.append("result ");
|
|
QByteArray res;
|
|
res.setNum(m_resultLeft);
|
|
result.append(res);
|
|
result.append(' ');
|
|
res.setNum(m_resultRight);
|
|
result.append(res);
|
|
result.append(" \n");
|
|
socket->write(result);
|
|
emit resultChanged();
|
|
}
|
|
}
|
|
|
|
void PingPong::checkResult()
|
|
{
|
|
if (m_resultRight < 10 && m_resultLeft < 10)
|
|
return;
|
|
|
|
if (m_resultRight == 10 && m_role == 2) {
|
|
setMessage("Game over. You win! Next game starts in 10s");
|
|
}
|
|
else if (m_resultRight == 10 && m_role == 1) {
|
|
setMessage("Game over. You lose! Next game starts in 10s");
|
|
}
|
|
else if (m_resultLeft == 10 && m_role == 1) {
|
|
setMessage("Game over. You win! Next game starts in 10s");
|
|
}
|
|
else if (m_resultLeft == 10 && m_role == 2) {
|
|
setMessage("Game over. You lose! Next game starts in 10s");
|
|
}
|
|
m_timer->stop();
|
|
QTimer::singleShot(10000, this, SLOT(startGame()));
|
|
}
|
|
|
|
void PingPong::startServer()
|
|
{
|
|
setMessage(QStringLiteral("Starting the server"));
|
|
|
|
// make discoverable
|
|
QBluetoothLocalDevice().setHostMode(QBluetoothLocalDevice::HostDiscoverable);
|
|
|
|
//! [Starting the server]
|
|
m_serverInfo = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this);
|
|
connect(m_serverInfo, &QBluetoothServer::newConnection,
|
|
this, &PingPong::clientConnected);
|
|
connect(m_serverInfo, &QBluetoothServer::errorOccurred, this, &PingPong::serverError);
|
|
const QBluetoothUuid uuid(serviceUuid);
|
|
|
|
m_serviceInfo = m_serverInfo->listen(uuid, QStringLiteral("PingPong server"));
|
|
//! [Starting the server]
|
|
if (m_serviceInfo.isValid()) {
|
|
setMessage(QStringLiteral("Server started, waiting for the client."
|
|
"You are the left player."));
|
|
}
|
|
// m_role is set to 1 if it is a server
|
|
m_role = 1;
|
|
emit roleChanged();
|
|
}
|
|
|
|
void PingPong::startClient()
|
|
{
|
|
//! [Searching for the service]
|
|
discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress());
|
|
|
|
connect(discoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered,
|
|
this, &PingPong::addService);
|
|
connect(discoveryAgent, &QBluetoothServiceDiscoveryAgent::finished,
|
|
this, &PingPong::done);
|
|
connect(discoveryAgent, &QBluetoothServiceDiscoveryAgent::errorOccurred, this,
|
|
&PingPong::serviceScanError);
|
|
#ifdef Q_OS_ANDROID
|
|
// QTBUG-61392
|
|
discoveryAgent->setUuidFilter(QBluetoothUuid(androidUuid));
|
|
#else
|
|
discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
|
|
#endif
|
|
discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
|
|
|
|
//! [Searching for the service]
|
|
if (discoveryAgent->error() == QBluetoothServiceDiscoveryAgent::NoError)
|
|
setMessage(QStringLiteral("Starting server discovery. You are the right player"));
|
|
// m_role is set to 2 if it is a client
|
|
m_role = 2;
|
|
emit roleChanged();
|
|
}
|
|
|
|
void PingPong::clientConnected()
|
|
{
|
|
//! [Initiating server socket]
|
|
if (!m_serverInfo->hasPendingConnections()) {
|
|
setMessage("FAIL: expected pending server connection");
|
|
return;
|
|
}
|
|
socket = m_serverInfo->nextPendingConnection();
|
|
if (!socket)
|
|
return;
|
|
socket->setParent(this);
|
|
connect(socket, &QBluetoothSocket::readyRead,
|
|
this, &PingPong::readSocket);
|
|
connect(socket, &QBluetoothSocket::disconnected,
|
|
this, &PingPong::clientDisconnected);
|
|
connect(socket, &QBluetoothSocket::errorOccurred, this, &PingPong::socketError);
|
|
|
|
//! [Initiating server socket]
|
|
setMessage(QStringLiteral("Client connected. Get ready!"));
|
|
QTimer::singleShot(3000, this, SLOT(startGame()));
|
|
}
|
|
|
|
void PingPong::clientDisconnected()
|
|
{
|
|
setMessage(QStringLiteral("Client disconnected"));
|
|
m_timer->stop();
|
|
}
|
|
|
|
void PingPong::socketError(QBluetoothSocket::SocketError error)
|
|
{
|
|
Q_UNUSED(error);
|
|
m_timer->stop();
|
|
}
|
|
|
|
void PingPong::serverError(QBluetoothServer::Error error)
|
|
{
|
|
setMessage(QStringLiteral("Server error occurred: ") + QVariant::fromValue(error).toString());
|
|
m_timer->stop();
|
|
}
|
|
|
|
void PingPong::done()
|
|
{
|
|
qDebug() << "Service scan done";
|
|
if (!m_serviceFound && discoveryAgent->error() == QBluetoothServiceDiscoveryAgent::NoError)
|
|
setMessage("PingPong service not found");
|
|
}
|
|
|
|
void PingPong::addService(const QBluetoothServiceInfo &service)
|
|
{
|
|
if (m_serviceFound)
|
|
return;
|
|
m_serviceFound = true;
|
|
|
|
setMessage("Service found. Stopping discovery and creating connection...");
|
|
discoveryAgent->stop();
|
|
//! [Connecting the socket]
|
|
socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
|
|
socket->connectToService(service);
|
|
|
|
connect(socket, &QBluetoothSocket::readyRead, this, &PingPong::readSocket);
|
|
connect(socket, &QBluetoothSocket::connected, this, &PingPong::serverConnected);
|
|
connect(socket, &QBluetoothSocket::disconnected, this, &PingPong::serverDisconnected);
|
|
//! [Connecting the socket]
|
|
}
|
|
|
|
void PingPong::serviceScanError(QBluetoothServiceDiscoveryAgent::Error error)
|
|
{
|
|
Q_UNUSED(error);
|
|
setMessage(QStringLiteral("Scanning error: ") + discoveryAgent->errorString());
|
|
}
|
|
|
|
bool PingPong::showDialog() const
|
|
{
|
|
return m_showDialog;
|
|
}
|
|
|
|
QString PingPong::message() const
|
|
{
|
|
return m_message;
|
|
}
|
|
|
|
void PingPong::serverConnected()
|
|
{
|
|
setMessage("Server Connected. Get ready!");
|
|
QTimer::singleShot(3000, this, SLOT(startGame()));
|
|
}
|
|
|
|
void PingPong::serverDisconnected()
|
|
{
|
|
setMessage("Server Disconnected");
|
|
m_timer->stop();
|
|
}
|
|
|
|
void PingPong::readSocket()
|
|
{
|
|
if (!socket)
|
|
return;
|
|
const char sep = ' ';
|
|
QByteArray line;
|
|
while (socket->canReadLine()) {
|
|
line = socket->readLine();
|
|
//qDebug() << QString::fromUtf8(line.constData(), line.length());
|
|
if (line.contains("result")) {
|
|
QList<QByteArray> result = line.split(sep);
|
|
if (result.size() > 2) {
|
|
QByteArray leftSide = result.at(1);
|
|
QByteArray rightSide = result.at(2);
|
|
m_resultLeft = leftSide.toInt();
|
|
m_resultRight = rightSide.toInt();
|
|
emit resultChanged();
|
|
checkResult();
|
|
}
|
|
}
|
|
}
|
|
if (m_role == 1) {
|
|
QList<QByteArray> boardSize = line.split(sep);
|
|
if (boardSize.size() > 1) {
|
|
QByteArray rightBlockY = boardSize.at(0);
|
|
m_rightBlockY = rightBlockY.toFloat();
|
|
emit rightBlockChanged();
|
|
}
|
|
} else if (m_role == 2) {
|
|
QList<QByteArray> boardSize = line.split(sep);
|
|
if (boardSize.size() > 2) {
|
|
QByteArray ballX = boardSize.at(0);
|
|
QByteArray ballY = boardSize.at(1);
|
|
QByteArray leftBlockY = boardSize.at(2);
|
|
m_ballX = ballX.toFloat();
|
|
m_ballY = ballY.toFloat();
|
|
m_leftBlockY = leftBlockY.toFloat();
|
|
emit leftBlockChanged();
|
|
emit ballChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
void PingPong::setMessage(const QString &message)
|
|
{
|
|
m_showDialog = true;
|
|
m_message = message;
|
|
emit showDialogChanged();
|
|
}
|
|
|
|
int PingPong::role() const
|
|
{
|
|
return m_role;
|
|
}
|
|
|
|
int PingPong::leftResult() const
|
|
{
|
|
return m_resultLeft;
|
|
}
|
|
|
|
int PingPong::rightResult() const
|
|
{
|
|
return m_resultRight;
|
|
}
|
|
|
|
float PingPong::ballX() const
|
|
{
|
|
return m_ballX;
|
|
}
|
|
|
|
float PingPong::ballY() const
|
|
{
|
|
return m_ballY;
|
|
}
|
|
|
|
|
|
float PingPong::leftBlockY() const
|
|
{
|
|
return m_leftBlockY;
|
|
}
|
|
|
|
float PingPong::rightBlockY() const
|
|
{
|
|
return m_rightBlockY;
|
|
}
|