I am working on my Cocos2d-x cpp project. I have successfully add touch event to move the background in Layer. Now I want to add CCMenuItemLabel to the Layer, but I find that CCMenuItemLabel doesn't work when I Touch it. How can I solve it?
I have add these functions in my Layer:
virtual void ccTouchesBegan (CCSet *pTouches, CCEvent *pEvent);
virtual void ccTouchesMoved (CCSet *pTouches, CCEvent *pEvent);
virtual void ccTouchesEnded (CCSet *pTouches, CCEvent *pEvent);
In MyLayer::init() function:
this->setTouchEnabled(true);
CCLabelTTF* test = CCLabelTTF::create("tesetdd","Arial",40);
CCMenuItemLabel* menuLabel = CCMenuItemLabel::create(test,this,menu_selector(GameWall::menuCall));
menuLabel->setPosition(ccp(winSize.width/2,winSize.height/2));
this->addChild(menuLabel,1);
Update:I have put CCMenuItemLabel into the CCMenu. But it still doesn't work.
CCLabelTTF* test = CCLabelTTF::create("tesetdd","Arial",40);
CCMenuItemLabel* menuLabel = CCMenuItemLabel::create(test,this,menu_selector(GameWall::menuCall));
menuLabel->setPosition(ccp(winSize.width/2,winSize.height/2));
CCMenu* menu = CCMenu::create(menuLabel,NULL);
menu->setPosition(CCPointZero);
this->addChild(menu,1);
Don't add CCMenuItems directly to layer. Add them to a CCMenu and add that CCMenu to your layer.
First, thanks for #Kreiri.
I have change my Touch Event function to
virtual bool ccTouchBegan (CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved (CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded (CCTouch *pTouch, CCEvent *pEvent);
and I add two more functions
virtual void onEnter();
virtual void registerWithTouchDispatcher();
and I move initial code to onEnter
CCLabelTTF* test = CCLabelTTF::create("tesetdd","Arial",40);
CCMenuItemLabel* menuLabel = CCMenuItemLabel::create(test,this,menu_selector(GameWall::menuCall));
menuLabel->setPosition(ccp(winSize.width/2,winSize.height/2));
CCMenu* menu = CCMenu::create(menuLabel,NULL);
menu->setPosition(CCPointZero);
this->addChild(menu,1);
add three more code to onEnter():
this->setTouchEnabled(true);
registerWithTouchDispatcher();
menu->registerWithTouchDispatcher();
While registerWithTouchDispatcher() :
void GameWall::registerWithTouchDispatcher(){
//registe the single point touch,and take over all touch event
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,kCCMenuHandlerPriority,true);
}
Finally, don't forget to romoveDelegate() in onExit():
void GameWall::onExit(){
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
}
Let me explain, check the document registerWithTouchDispatcher() says If isTouchEnabled, this method is called onEnter. and Override it to change the way CCLayer receives touch events. And same with CCMenu.
Related
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
Now I create a totally new SDI project
the view provides a function: GetDocument(), it helps me to get the current document's data
However, When I call the GetDocument() function,VC tells me some error occurs:Debug Assertion Failed
the following is my setting
class CHorse_programView : public CView
{
protected: // create from serialization only
CHorse_programView();
DECLARE_DYNCREATE(CHorse_programView)
// Attributes
public:
CHorse_programDoc* GetDocument();
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CHorse_programView)
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CHorse_programView();
CHorse_programDoc * GetDoc()
{
CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);
return (CHorse_programDoc *) pFrame->GetActiveDocument();
}
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CHorse_programView)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
and I want to call GetDocument() in this function
CHorse_programView::CHorse_programView()
{
GetDocument();
}
what's wrong
The CDocument and CView are not connected yet at CView construction time. You can move your code to OnInitialUpdate in the view to get full capability.
In the view's constructor, it hasn't been assigned to a document yet - that comes later.
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);
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;
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