cocos2d subclassing sprite to handle touch? - cocos2d-iphone

I'm new to the cocos2d(-x) world.
I'd like to detect a touch to a sprite, and tutorials/examples seem to suggest using layer to detect touch and find the approapriate sprite with bounding box.
Is subclassing sprite to allow touch detection generally a bad idea?

Note: This answer might be outdated. I answered this at 2012.
It is not a bad idea. Here is how I do it:
header file:
#include "cocos2d.h"
using namespace cocos2d;
class TouchableSprite : public cocos2d::CCSprite, public CCTargetedTouchDelegate {
public:
virtual void onEnter();
virtual void onExit();
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
};
cpp file:
#include "TouchableSprite.h"
void TouchableSprite::onEnter(){
// before 2.0:
// CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
// since 2.0:
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
CCSprite::onEnter();
}
void TouchableSprite::onExit(){
// before 2.0:
// CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
// since 2.0:
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCSprite::onExit();
}
bool TouchableSprite::ccTouchBegan(CCTouch* touch, CCEvent* event){
//do whatever you want here
return true;
}
void TouchableSprite::ccTouchMoved(CCTouch* touch, CCEvent* event){
//do what you want
}
void TouchableSprite::ccTouchEnded(CCTouch* touch, CCEvent* event){
//do your job here
}

In cocos2d-x 3.0 alpha you can try this:
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = [&](Touch* touch, Event* event){
if (this->getBoundingBox().containsPoint(this->convertTouchToNodeSpace(touch))) {
return true;
}
return false;
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

it is better and much more clear to handle touches in one place. but i think, no one can bar you to do this

You do not need to subclass sprites to detect touch.
Here, Follow this LINK its a nice place to get started with Cocos2d

Related

Qt How to set focus on a QRect

I have created a Qwidget with a QRect. Now I would like to move this Rect with my keyboard. Which does not work. When trying with a mousePressEvent instead of the keyPressEvent, everything is working fine though. There are no Focuses yet set, which probably is the problem here. But it's the first time I have to work with those and I just can't figure out what to do and how to use them, even after searching online for the last hours.
I tried putting setFocusPolicy(Qt::StrongFocus) in my widgets constructor amongst other things, but nothing has worked so far. Maybe I'm just blind to an obvious solution, but please help? Thanks alot already
my paint event:
void zeichenFeld::paintEvent(QPaintEvent *event)
{
QPainter painter;
painter.begin (this);
//Avatar:
QRect rectAv=QRect(xAv, yAv, 20,20);
painter.fillRect(rectAv, Qt::BrushStyle::SolidPattern);
painter.fillRect(rectAv, colorAv);
painter.drawRect(rectAv);
painter.end();
}
void zeichenFeld::keyPressEvent(QKeyEvent *event)
{
if(event->key()==Qt::Key_A){
xAv-=10;
}
if(event->key()==Qt::Key_D){
xAv+=10;
}
update();
}
void zeichenFeld::mousePressEvent(QMouseEvent *event){
if(event->button()==Qt::LeftButton)
{
xAv-=25;
}
if(event->button()==Qt::RightButton)
{
xAv+=25;
}
update();
}
header:
protected:
void paintEvent(QPaintEvent *event);
void keyPressEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
Edit: i simply added the setFocusPolicy(Qt::StrongFocus); to the wrong contructor. Working now
a QRect is not a visual class, so if you used the PaintEvent to draw a rectangle, then you need to come up with your special way to handle that in the mouse press event.
something like:
YourClass.h
protected:
void mousePressEvent(QMouseEvent *ev) override;
void mouseMoveEvent(QMouseEvent *ev) override;
void mouseReleaseEvent(QMouseEvent *ev) override;
private:
QRect _myRect;
YourClass.cpp
void YourClass::mousePressEvent(QMouseEvent *ev) {
if (_myRect.contains(ev->localPos())){
_isRectSelected = true;
}
}
void YourClass::mouseMoveEvent(QMouseEvent *ev) {
if (_isRectSelected) {
_myRect->setX(ev->localPos().x());
_myRect->setY(ev->localPos().y());
}
}
void YourClass::mouseReleaseEvent(QMouseEvent *ev) {
if (_isRectSelected) {
_isRectSelected = false;
}
}
but I wouldn't reccomend that. Try to use QGraphicsView with QGraphicsRectItem as it's a visual class. it knows how to paint itself and it also has mouse handling.
Or even better, Qml.

Is it possible torender Qt QWidgets and QML using custom graphics engine?

I have some project where I'm interested in using of QML ui.
But it is not enough for us to use standart Qt rendering sistem, I must try to repaint all ui elements every frame, not only in QCoreApplication::processEvents().
It's requered because it is some DirectX render in background of ui.
In this case I found the way to render widgets only using QWidget::render() function, but this method uses CPU instead of GPU, so it is too slow and is't correct at all.
So, what I have:
I have QApplication integrated into my application's loop.
Signals/Slots system works fine and process events is called right
way.
Own render system with DirectX11, that i use to render other
graphics.
I need redraw (update) qt (QML) ui every frame using
DirectX, or maybe angle, but every frame of render.
Already existing code example:
In this example reimplemented widget is app's main window and used like context of DirectX render.
header:
#ifndef _QTD3DCONTEXTWIDGET_
#define _QTD3DCONTEXTWIDGET_
#include <QtWidgets/QWidget>
#include <QtQuick/QQuickView>
#include <QtQuickWidgets/QQuickWidget>
#include <QtQml/qqml.h>
#include <QtWidgets/QPushButton>
#include <QtGui/QPaintEngine>
#include <QtGui/QPaintDevice>
class QtGfxPaintEngine : public QPaintEngine
{
public:
QtGfxPaintEngine(PaintEngineFeatures caps = PaintEngineFeatures());
virtual bool begin(QPaintDevice *pdev);
virtual bool end();
//virtual void drawEllipse(const QRectF &rect);
//virtual void drawEllipse(const QRect &rect);
virtual void drawImage(const QRectF &rectangle,
const QImage &image,
const QRectF &sr,
Qt::ImageConversionFlags flags = Qt::AutoColor);
virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
virtual void drawPoints(const QPointF *points, int pointCount);
virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
virtual void drawRects(const QRectF *rects, int rectCount);
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
virtual void drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p);
// default implementation used
//virtual void drawLines(const QLineF *lines, int lineCount); //not
//virtual void drawLines(const QLine *lines, int lineCount);
//virtual void drawPath(const QPainterPath &path);
//virtual void drawPoints(const QPoint *points, int pointCount);
//virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
//virtual void drawRects(const QRect *rects, int rectCount);
virtual Type type() const;
virtual void updateState(const QPaintEngineState &newState);
private:
int getClosestPowOfTwo(QSize& size);
};
class QtD3DContextWidget : public QWidget
{
Q_OBJECT
public:
QtD3DContextWidget(QWidget* parent = nullptr);
virtual ~QtD3DContextWidget() = default;
virtual QPaintEngine* paintEngine() const;
virtual void paintEvent(QPaintEvent* event);
void mousePressEvent(QMouseEvent *event) override;
QQuickWidget* containerWidget;
QPushButton* widg;
QtGfxPaintEngine* mPaintEngine;
private:
};
#endif // !_QTD3DCONTEXTWIDGET_
cpp, without draw functions (they are implemented with DirectX and works fine):
QtD3DContextWidget::QtD3DContextWidget(QWidget* parent /*= nullptr*/)
: QWidget(parent, Qt::MSWindowsOwnDC)
{
mPaintEngine = new QtGfxPaintEngine(QPaintEngine::AllFeatures);
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
setAttribute(Qt::WA_NativeWindow, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_UpdatesDisabled, true);
setAttribute(Qt::WA_TransparentForMouseEvents);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_OpaquePaintEvent, true);
containerWidget = new QQuickWidget(this);
containerWidget->setAttribute(Qt::WA_TranslucentBackground, true);
containerWidget->setAttribute(Qt::WA_OpaquePaintEvent, true);
containerWidget->setAttribute(Qt::WA_NoSystemBackground, true);
containerWidget->setClearColor(Qt::transparent);
QString sourceUi = QCoreApplication::applicationDirPath() + "/qtui/test.qml";
containerWidget->setSource(QUrl::fromLocalFile(sourceUi));
containerWidget->move(0, 0);
resize(1280, 720);
widg = new QPushButton("test", this);
//widg->setBackgroundRole(QPalette::WindowText);
widg->resize(50, 50);
widg->move(600, 300);
widg->show();
show();
setVisible(true);
//wrapper->show();
}
QPaintEngine* QtD3DContextWidget::paintEngine() const
{
return mPaintEngine;
}
void QtD3DContextWidget::paintEvent(QPaintEvent* event)
{
}
QtGfxPaintEngine::QtGfxPaintEngine(PaintEngineFeatures caps /*= PaintEngineFeatures()*/)
: QPaintEngine(caps)
{}
QPaintEngine::Type QtGfxPaintEngine::type() const
{
return QPaintEngine::Direct3D;
}
void QtGfxPaintEngine::updateState(const QPaintEngineState &newState)
{}
and in render ui method:
QPainter painter(w);
painter.setBrushOrigin(w->containerWidget->pos());
w->containerWidget->render(&painter);// , w->widg->pos());// , w->widg->rect());// , QWidget::DrawChildren);
painter.end();
that's works fine but using CPU.
What i try it's to replace it with
w->repaint();
so entire widget is repainted, but not every frame, and in this case ui is "blinking" and widget try to repain it's background not as transparent, but with white color.
Or replace with
w->containerWidget->repaint();
and nothing is happends.
trying to call QApplication::process events just after w->repaint() don't make sence, as i can see.
and ofcourse i have comented setAttribute(Qt::WA_UpdatesDisabled, true); if i try to refresh widget in this manner.
PS: sorry for my english)))
If you want to render all Qt content through a central renderer then you might be better of implementing your own QPA (Qt Platform Abstraction) plugin, based on or derived from the normal Windows QPA plugin.
Then you can (a) handout your paint device implementation from there and not need any application side code and potentially use something like ANGLE to get the QtQuick scene graph rendered via Direct3D

QT5: How do you Drag and Drop with QGraphicsView?

Ok, I've been able to get get Drag and Drop working on QGraphicsScene, but not QGraphicsView. My code looks something like this:
class GraphicsView : public QGraphicsView
{
public:
GraphicsView(QGraphicsScene *scene) : QGraphicsView(scene) {}
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
event->setAccepted(true);
update();
}
void dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
event->setAccepted(true);
update();
}
void dropEvent(QGraphicsSceneDragDropEvent *event)
{
//stuff that never runs...
update();
}
};
Although, this code works perfectly fine with QGraphicsScene inherited instead of QGraphicsView. I also ran the setAcceptDrops(true) function. What am I doing wrong?

Unable to get touches in COCOS 2dx?

Below is my HelloWorld.h class:
class HelloWorld : public cocos2d::CCLayer
{
public:
HelloWorld();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
b2World* world;
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
// implement the "static node()" method manually
CREATE_FUNC(HelloWorld);
virtual void draw();
virtual void ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
virtual void ccTouchesMoved(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
virtual void ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
void update(float dt);
};
And in my HelloWorld.cpp class i have initialized My init method
bool HelloWorld::init(){
setTouchEnabled( true );
setAccelerometerEnabled( true );
scheduleUpdate();
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
return true;
}
This code is working for me now! :)
The Targeted Delegate is for single touch events. Change your events to something like this:
virtual bool ccTouchBegan (CCTouch *pTouch, CCEvent *pEvent)
You can read up more about targeted and standard touch delegates on the iPhone side of the Cocos2D documentation at this Link
Writing the delegate in my initialisation method as per below solved the problem
CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this, 1);
If you want to disable multiTouch capability you can use :
virtual bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
virtual void ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){}
virtual void ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){}
And so you need to:
bool init()
{
...
setTouchEnabled(true);
this->setTouchMode(ccTouchesMode::kCCTouchesOneByOne); // Important
}
If you omit last line you will need to override these (multiTouch mode):
void ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
void ccTouchesMoved(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
void ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event);

Create a sprite from a class that inherits CCSprite

I have a Balloon class (see this) that inherits from CCSprite. I have given it properties like balloonSpeed and balloonStrength. I seem to be having problems in it, though.
What I want to do is that when I make an instance of the Balloon class, I want it to do the following:
Give it a texture (a PNG file of a balloon).
Set properties like balloonSpeed and balloonStrength.
Add actions to make it move and accept touch input.
When the object is touched, I want to:
Count if # of taps = balloonStrength. if so, destroy Balloon.
I have done a simpler version of this where a Balloon object is destroyed when it is touched. I want to apply OOP and custom classes here but I can't seem to get the right way of doing it.
Thanks in advance.
then the h file should looks like below:
#include "cocos2d.h"
using namespace cocos2d;
class Balloon : public cocos2d::CCSprite, public CCTargetedTouchDelegate {
public:
float balloonSpeed;
int balloonStrength;
int numberOfTaps;
virtual void onEnter();
virtual void onExit();
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
};
and in your touch method:
bool Balloon::ccTouchBegan(CCTouch* touch, CCEvent* event){
CCPoint touchLocation = this->getParent()->convertTouchToNodeSpace(touch);
if (CCRect::CCRectContainsPoint(this->boundingBox(), touchLocation)) {
this->numberOfTaps++;
if(this->balloonStrength == this->numberOfTaps){
this->removeFromParentAndCleanup(true);
}
}
return true;
}
you can use it after you add the blueBalloon as a child of a layer or node as below:
blueBalloon->balloonSpeed = 2.0f;
blueBalloon->numberOfTaps = 0;
blueBalloon->balloonStrength = 5;